mirror of
https://github.com/nicbarker/clay.git
synced 2025-04-18 20:28:01 +00:00
465 lines
16 KiB
Plaintext
465 lines
16 KiB
Plaintext
AT_COMPILE_TIME :: true;
|
|
|
|
SOURCE_PATH :: "source";
|
|
|
|
#if AT_COMPILE_TIME {
|
|
#run,stallable {
|
|
Compiler.set_build_options_dc(.{do_output=false});
|
|
options := Compiler.get_build_options();
|
|
args := options.compile_time_command_line;
|
|
if !generate_bindings(args, options.minimum_os_version) {
|
|
Compiler.compiler_set_workspace_status(.FAILED);
|
|
}
|
|
}
|
|
} else {
|
|
#import "System";
|
|
|
|
main :: () {
|
|
set_working_directory(path_strip_filename(get_path_of_running_executable()));
|
|
if !generate_bindings(get_command_line_arguments(), #run get_build_options().minimum_os_version) {
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Build_Type :: enum {
|
|
STATIC_LIBRARY;
|
|
DYNAMIC_LIBRARY;
|
|
EXECUTABLE;
|
|
OBJ_FILE;
|
|
}
|
|
|
|
build_cpp_static_lib :: #bake_arguments build_cpp(type = .STATIC_LIBRARY);
|
|
build_cpp_dynamic_lib :: #bake_arguments build_cpp(type = .DYNAMIC_LIBRARY);
|
|
build_cpp_executable :: #bake_arguments build_cpp(type = .EXECUTABLE);
|
|
|
|
// This is a modified version of the procedure from BuildCpp. It will assume a clang-like compiler if you add something to compiler_executable_path.
|
|
build_cpp :: (
|
|
output_basename: string,
|
|
files: ..string,
|
|
type: Build_Type,
|
|
debug := false,
|
|
extra: [] string = .[],
|
|
library_files: [] string = .[],
|
|
target := OS,
|
|
compiler_executable_path := "",
|
|
ar_executable_path := "",
|
|
working_directory := "",
|
|
loc := #caller_location
|
|
) -> bool {
|
|
Basic.auto_release_temp();
|
|
Basic.push_allocator(Basic.temp);
|
|
|
|
arguments: [..] string;
|
|
|
|
output_filename: string;
|
|
if target == .WINDOWS && compiler_executable_path == "" {
|
|
if #complete type == {
|
|
case .STATIC_LIBRARY;
|
|
output_filename = Basic.tprint("%.lib", output_basename);
|
|
case .DYNAMIC_LIBRARY;
|
|
output_filename = Basic.tprint("%.dll", output_basename);
|
|
case .EXECUTABLE;
|
|
output_filename = Basic.tprint("%.exe", output_basename);
|
|
case .OBJ_FILE;
|
|
output_filename = Basic.tprint("%.obj", output_basename);
|
|
}
|
|
|
|
#if OS == .WINDOWS {
|
|
String.path_overwrite_separators(output_filename, #char "\\");
|
|
|
|
vc_path, linker_path := WindowsResources.find_visual_studio_in_a_ridiculous_garbage_way();
|
|
|
|
kit_root := WindowsResources.find_windows_kit_root();
|
|
if !kit_root {
|
|
Compiler.compiler_report("Unable to find Windows Kit root; can't compile.\n", loc);
|
|
return false;
|
|
}
|
|
} else {
|
|
Compiler.compiler_report("Unable to find Visual Studio; can't compile.\n", loc);
|
|
vc_path, linker_path: string;
|
|
kit_root: string;
|
|
return false; // Visual studio is not available in non-windows OS.
|
|
}
|
|
|
|
linker := String.join(linker_path, "\\", "cl.exe");
|
|
Basic.array_add(*arguments, linker);
|
|
|
|
Basic.array_add(*arguments, "/nologo");
|
|
|
|
// Include directories:
|
|
vc_include_path := String.join(vc_path, "\\..\\..\\include");
|
|
kit_root_include := String.replace(kit_root, "Lib", "Include");
|
|
Basic.array_add(*arguments,
|
|
Basic.tprint("/I%", vc_include_path),
|
|
Basic.tprint("/I%\\um", kit_root_include),
|
|
Basic.tprint("/I%\\ucrt", kit_root_include),
|
|
Basic.tprint("/I%\\shared", kit_root_include),
|
|
);
|
|
|
|
// Definitions:
|
|
Basic.array_add(*arguments, "/DWIN32");
|
|
if debug {
|
|
Basic.array_add(*arguments, "/DDEBUG");
|
|
}
|
|
|
|
// Compiler options:
|
|
if debug {
|
|
Basic.array_add(*arguments, "/Od"); // Disable optimizations.
|
|
} else {
|
|
Basic.array_add(*arguments,
|
|
"/O2", // Maximize speed.
|
|
"/Oi", // Enable intrinsics.
|
|
);
|
|
}
|
|
Basic.array_add(*arguments, "/W3");
|
|
|
|
if debug {
|
|
Basic.array_add(*arguments,
|
|
"/DEBUG", // Generate debug info.
|
|
"/Zi", // Generate pdb file.
|
|
Basic.tprint("/Fd%.pdb", output_basename), // Sets name of pdb file.
|
|
);
|
|
}
|
|
|
|
Basic.array_add(*arguments,
|
|
"-diagnostics:caret",
|
|
"-diagnostics:column"
|
|
);
|
|
|
|
Basic.array_add(*arguments, .. extra);
|
|
|
|
// Add files:
|
|
objs: [..] string;
|
|
Basic.array_reserve(*objs, files.count);
|
|
for files {
|
|
src := String.copy_temporary_string(it);
|
|
String.path_overwrite_separators(src);
|
|
Basic.array_add(*arguments, src);
|
|
|
|
Basic.array_add(*objs, Basic.tprint("%.obj", String.path_strip_extension(String.path_filename(it))));
|
|
}
|
|
|
|
// Make sure to cleanup the resulting obj files.
|
|
defer {
|
|
if type != .OBJ_FILE {
|
|
for objs File.file_delete(it);
|
|
}
|
|
|
|
if type == .DYNAMIC_LIBRARY then File.file_delete(Basic.tprint("%.exp", output_basename));
|
|
}
|
|
|
|
if type == .STATIC_LIBRARY || type == .OBJ_FILE {
|
|
Basic.array_add(*arguments, "/c"); // Compile without linking.
|
|
} else {
|
|
Basic.array_add(*arguments, "/link");
|
|
|
|
// Linker options:
|
|
if type == .DYNAMIC_LIBRARY {
|
|
Basic.array_add(*arguments, "/DLL");
|
|
}
|
|
Basic.array_add(*arguments,
|
|
"/MACHINE:AMD64",
|
|
Basic.tprint("/OUT:%", output_filename),
|
|
Basic.tprint("/libpath:%", vc_path),
|
|
Basic.tprint("/libpath:%\\um\\x64", kit_root),
|
|
Basic.tprint("/libpath:%\\ucrt\\x64", kit_root),
|
|
);
|
|
}
|
|
|
|
Basic.array_add(*arguments, .. library_files);
|
|
|
|
Basic.log("%", Process.get_quoted_command_string(arguments));
|
|
result, output_string, error_string := Process.run_command(..arguments, capture_and_return_output = true, print_captured_output = true, working_directory = working_directory);
|
|
|
|
if result.exit_code {
|
|
Compiler.compiler_report(Basic.tprint("Compiler failed with exit code '%'.\n", result.exit_code), loc);
|
|
return false;
|
|
}
|
|
|
|
if type == .STATIC_LIBRARY {
|
|
// Create library:
|
|
Basic.array_reset_keeping_memory(*arguments);
|
|
|
|
librarian := String.join(linker_path, "\\lib.exe");
|
|
|
|
Basic.array_add(*arguments, librarian, "/nologo");
|
|
Basic.array_add(*arguments, ..objs);
|
|
Basic.array_add(*arguments, Basic.tprint("/OUT:%", output_filename));
|
|
|
|
Basic.log("%", Process.get_quoted_command_string(arguments));
|
|
result, output_string, error_string := Process.run_command(..arguments, capture_and_return_output = true, print_captured_output = true, working_directory = working_directory);
|
|
|
|
if result.exit_code {
|
|
Compiler.compiler_report(Basic.tprint("Librarian failed with exit code '%'.\n", result.exit_code), loc);
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
if #complete type == {
|
|
case .STATIC_LIBRARY;
|
|
#if OS == .WINDOWS {
|
|
output_filename = Basic.tprint("%.lib", output_basename);
|
|
} else {
|
|
output_filename = Basic.tprint("%.a", output_basename);
|
|
}
|
|
|
|
case .OBJ_FILE;
|
|
output_filename = Basic.tprint("%.o", output_basename);
|
|
case .DYNAMIC_LIBRARY;
|
|
if target == {
|
|
case .WINDOWS; output_filename = Basic.tprint("%.dll", output_basename);
|
|
case .MACOS; output_filename = Basic.tprint("%.dylib", output_basename);
|
|
case .LINUX; #through;
|
|
case .ANDROID; output_filename = Basic.tprint("%.so", output_basename);
|
|
case .NONE; #if OS == .WINDOWS then output_filename = Basic.tprint("%.dll", output_basename); else assert(false);
|
|
case; assert(false);
|
|
}
|
|
case .EXECUTABLE;
|
|
output_filename = Basic.copy_temporary_string(output_basename);
|
|
}
|
|
|
|
#if OS == .WINDOWS {
|
|
String.path_overwrite_separators(output_filename, #char "\\");
|
|
}
|
|
|
|
compiler := compiler_executable_path;
|
|
ar := ar_executable_path;
|
|
if !compiler || (type == .STATIC_LIBRARY && !ar) {
|
|
is_cpp_project := false;
|
|
for files {
|
|
if String.ends_with(it, ".cpp") {
|
|
is_cpp_project = true; // Use c++ compiler, so we link the required c++ runtime libraries by default.
|
|
break;
|
|
}
|
|
}
|
|
|
|
if !compiler {
|
|
if is_cpp_project {
|
|
// @Cleanup: Could be simplified to the following, but that currently triggers a compiler bug. -rluba, 2024-01-23
|
|
// compiler = ifx to_string(getenv("CXX")) else "clang++";
|
|
compiler = to_string(getenv("CXX"));
|
|
if !compiler compiler ="clang++";
|
|
} else {
|
|
compiler = to_string(getenv("CC"));
|
|
if !compiler compiler ="clang";
|
|
}
|
|
}
|
|
|
|
if !ar ar = "ar";
|
|
|
|
}
|
|
|
|
Basic.array_add(*arguments, compiler);
|
|
|
|
if debug {
|
|
Basic.array_add(*arguments, "-g", "-Og");
|
|
} else {
|
|
Basic.array_add(*arguments, "-O3");
|
|
}
|
|
|
|
Basic.array_add(*arguments, ..extra);
|
|
|
|
if type == .STATIC_LIBRARY || type == .OBJ_FILE {
|
|
array_add(*arguments, "-c");
|
|
} else {
|
|
if type == .DYNAMIC_LIBRARY {
|
|
array_add(*arguments, "-shared", "-fpic");
|
|
}
|
|
|
|
if target == .MACOS {
|
|
if type == {
|
|
case .DYNAMIC_LIBRARY;
|
|
array_add(*arguments, "-install_name", tprint("@rpath/%", output_filename));
|
|
case .EXECUTABLE;
|
|
array_add(*arguments, "-rpath", "@loader_path");
|
|
}
|
|
}
|
|
|
|
array_add(*arguments, "-o", output_filename);
|
|
}
|
|
|
|
// Add files:
|
|
objs: [..] string;
|
|
array_reserve(*objs, files.count);
|
|
for files {
|
|
src := copy_temporary_string(it);
|
|
String.path_overwrite_separators(src);
|
|
array_add(*arguments, src);
|
|
|
|
array_add(*objs, tprint("%.o", String.path_basename(it)));
|
|
}
|
|
|
|
// Make sure to cleanup the resulting obj files.
|
|
defer {
|
|
if type != .OBJ_FILE {
|
|
for objs File.file_delete(it);
|
|
}
|
|
}
|
|
|
|
array_add(*arguments, .. library_files);
|
|
|
|
log("%", Process.get_quoted_command_string(arguments));
|
|
result, output_string, error_string := Process.run_command(..arguments, capture_and_return_output = true, print_captured_output = true, working_directory = working_directory);
|
|
|
|
if result.exit_code {
|
|
Compiler.compiler_report(tprint("Compiler failed with exit code '%'.\n", result.exit_code), loc);
|
|
return false;
|
|
}
|
|
|
|
if type == .STATIC_LIBRARY {
|
|
File.file_delete(output_filename); // ar only adds/updates the archive, but does not delete files from it.
|
|
|
|
// Create library:
|
|
array_reset_keeping_memory(*arguments);
|
|
array_add(*arguments,
|
|
ar,
|
|
"-rc", // replace or insert files into the archive, do not warn if archive needs to be created.
|
|
output_filename,
|
|
);
|
|
|
|
array_add(*arguments, ..objs);
|
|
|
|
// Run archiver command:
|
|
log("%", Process.get_quoted_command_string(arguments));
|
|
result, output_string, error_string := Process.run_command(..arguments, capture_and_return_output = true, print_captured_output = true, working_directory = working_directory);
|
|
|
|
if result.exit_code {
|
|
Compiler.compiler_report(tprint("Archive command failed with exit code '%'.\n", result.exit_code), loc);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
enum_cpp_files :: (path: string, recursive:=false) -> [..] string {
|
|
files: [..] string;
|
|
|
|
visitor :: (info: *FileUtils.File_Visit_Info, files: *[..] string) {
|
|
extension := String.path_extension(info.full_name);
|
|
if extension == "cpp" || extension == "c" {
|
|
Basic.array_add(files, String.copy_string(info.full_name));
|
|
}
|
|
}
|
|
|
|
FileUtils.visit_files(path, recursive=recursive, *files, visitor);
|
|
|
|
return files;
|
|
}
|
|
|
|
free_cpp_files :: (files: [] string) {
|
|
for files Basic.free(it);
|
|
Basic.array_free(files);
|
|
}
|
|
|
|
|
|
generate_bindings :: (args: [] string, minimum_os_version: type_of(Compiler.Build_Options.minimum_os_version)) -> bool {
|
|
compile := Basic.array_find(args, "-compile");
|
|
compile_debug := Basic.array_find(args, "-debug");
|
|
|
|
if compile {
|
|
could_copy := FileUtils.copy_file("../../clay.h", "source/clay.h");
|
|
if !could_copy then return false;
|
|
|
|
source_file := Basic.tprint("%/clay.c", SOURCE_PATH);
|
|
|
|
success := true;
|
|
#if OS == .WINDOWS {
|
|
File.make_directory_if_it_does_not_exist("clay-jai/windows", true);
|
|
|
|
// Can't use this because clay doesn't support MSVC
|
|
success &&= build_cpp_static_lib(
|
|
"clay-jai/windows/clay",
|
|
source_file,
|
|
debug = compile_debug,
|
|
target=.NONE,
|
|
compiler_executable_path="clang",
|
|
ar_executable_path="llvm-ar",
|
|
);
|
|
|
|
// {
|
|
// command := Process.break_command_into_strings("clang -c -o clay-jai/windows/clay.lib -static source/clay.c");
|
|
// result, out, error := Process.run_command(..command, capture_and_return_output = true);
|
|
|
|
// write_string(out);
|
|
// if result.exit_code != 0
|
|
// {
|
|
// write_string("Failed to build clay. Do you have clang installed ?\n");
|
|
// write_string(error);
|
|
// success = false;
|
|
// }
|
|
// }
|
|
|
|
// {
|
|
// command := Process.break_command_into_strings("clang -c -o clay-jai/windows/clay.dll -dynamic source/clay.c");
|
|
// result, out, error := Process.run_command(..command, capture_and_return_output = true);
|
|
|
|
// write_string(out);
|
|
// if result.exit_code != 0
|
|
// {
|
|
// write_string("Failed to build clay. Do you have clang installed ?\n");
|
|
// write_string(error);
|
|
// success = false;
|
|
// }
|
|
// }
|
|
} else {
|
|
// TODO MacOS
|
|
// TODO Linux
|
|
assert(false);
|
|
}
|
|
|
|
if !success then return false;
|
|
}
|
|
|
|
output_filename: string;
|
|
options: Generator.Generate_Bindings_Options;
|
|
{
|
|
using options;
|
|
|
|
#if OS == .WINDOWS {
|
|
Basic.array_add(*libpaths, "clay-jai/windows");
|
|
output_filename = "windows.jai";
|
|
} else {
|
|
assert(false);
|
|
}
|
|
|
|
Basic.array_add(*libnames, "clay");
|
|
Basic.array_add(*include_paths, SOURCE_PATH);
|
|
Basic.array_add(*source_files, Basic.tprint("%/clay.h", SOURCE_PATH));
|
|
Basic.array_add(*strip_prefixes, "Clay_");
|
|
|
|
auto_detect_enum_prefixes = true;
|
|
log_stripped_declarations = true;
|
|
generate_compile_time_struct_checks = false;
|
|
}
|
|
|
|
could_generate := Generator.generate_bindings(options, output_filename);
|
|
|
|
File.file_delete("source/clay.h");
|
|
|
|
return could_generate;
|
|
}
|
|
|
|
#scope_file
|
|
|
|
using Basic :: #import "Basic";
|
|
Generator :: #import "Bindings_Generator";
|
|
Compiler :: #import "Compiler";
|
|
File :: #import "File";
|
|
FileUtils :: #import "File_Utilities";
|
|
BuildCpp :: #import "BuildCpp";
|
|
Process :: #import "Process";
|
|
String :: #import "String";
|
|
WindowsResources :: #import "Windows_Resources";
|
|
|
|
#if OS == .WINDOWS {
|
|
Windows :: #import "Windows";
|
|
getenv :: Windows.getenv;
|
|
} else {
|
|
Posix :: #import "POSIX";
|
|
getenv :: Posix.getenv;
|
|
}
|