#if defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) #include #include #endif #include "build_cpuid.cpp" // #if defined(GB_SYSTEM_WINDOWS) // #define DEFAULT_TO_THREADED_CHECKER // #endif #define DEFAULT_MAX_ERROR_COLLECTOR_COUNT (36) enum TargetOsKind : u16 { TargetOs_Invalid, TargetOs_windows, TargetOs_darwin, TargetOs_linux, TargetOs_essence, TargetOs_freebsd, TargetOs_openbsd, TargetOs_netbsd, TargetOs_haiku, TargetOs_wasi, TargetOs_js, TargetOs_orca, TargetOs_freestanding, TargetOs_COUNT, }; gb_global String target_os_names[TargetOs_COUNT] = { str_lit(""), str_lit("windows"), str_lit("darwin"), str_lit("linux"), str_lit("essence"), str_lit("freebsd"), str_lit("openbsd"), str_lit("netbsd"), str_lit("haiku"), str_lit("wasi"), str_lit("js"), str_lit("orca"), str_lit("freestanding"), }; enum TargetArchKind : u16 { TargetArch_Invalid, TargetArch_amd64, TargetArch_i386, TargetArch_arm32, TargetArch_arm64, TargetArch_wasm32, TargetArch_wasm64p32, TargetArch_riscv64, TargetArch_COUNT, }; gb_global String target_arch_names[TargetArch_COUNT] = { str_lit(""), str_lit("amd64"), str_lit("i386"), str_lit("arm32"), str_lit("arm64"), str_lit("wasm32"), str_lit("wasm64p32"), str_lit("riscv64"), }; enum TargetEndianKind : u8 { TargetEndian_Little, TargetEndian_Big, TargetEndian_COUNT, }; gb_global String target_endian_names[TargetEndian_COUNT] = { str_lit("little"), str_lit("big"), }; enum TargetABIKind : u16 { TargetABI_Default, TargetABI_Win64, TargetABI_SysV, TargetABI_COUNT, }; gb_global String target_abi_names[TargetABI_COUNT] = { str_lit(""), str_lit("win64"), str_lit("sysv"), }; enum Windows_Subsystem : u8 { Windows_Subsystem_UNKNOWN, Windows_Subsystem_BOOT_APPLICATION, Windows_Subsystem_CONSOLE, // Default, Windows_Subsystem_EFI_APPLICATION, Windows_Subsystem_EFI_BOOT_SERVICE_DRIVER, Windows_Subsystem_EFI_ROM, Windows_Subsystem_EFI_RUNTIME_DRIVER, Windows_Subsystem_NATIVE, Windows_Subsystem_POSIX, Windows_Subsystem_WINDOWS, Windows_Subsystem_WINDOWSCE, Windows_Subsystem_COUNT, }; gb_global String windows_subsystem_names[Windows_Subsystem_COUNT] = { str_lit(""), str_lit("BOOT_APPLICATION"), str_lit("CONSOLE"), // Default str_lit("EFI_APPLICATION"), str_lit("EFI_BOOT_SERVICE_DRIVER"), str_lit("EFI_ROM"), str_lit("EFI_RUNTIME_DRIVER"), str_lit("NATIVE"), str_lit("POSIX"), str_lit("WINDOWS"), str_lit("WINDOWSCE"), }; struct MicroarchFeatureList { String microarch; String features; }; #if defined(GB_SYSTEM_WINDOWS) #include #else #include #endif #include "build_settings_microarch.cpp" gb_global TargetEndianKind target_endians[TargetArch_COUNT] = { TargetEndian_Little, TargetEndian_Little, TargetEndian_Little, TargetEndian_Little, TargetEndian_Little, TargetEndian_Little, TargetEndian_Little, }; #ifndef ODIN_VERSION_RAW #define ODIN_VERSION_RAW "dev-unknown-unknown" #endif gb_global String const ODIN_VERSION = str_lit(ODIN_VERSION_RAW); struct TargetMetrics { TargetOsKind os; TargetArchKind arch; isize ptr_size; isize int_size; isize max_align; isize max_simd_align; String target_triplet; TargetABIKind abi; }; enum Subtarget : u32 { Subtarget_Default, Subtarget_iPhone, Subtarget_iPhoneSimulator, Subtarget_Android, Subtarget_COUNT, Subtarget_Invalid, // NOTE(harold): Must appear after _COUNT as this is not a real subtarget }; gb_global String subtarget_strings[Subtarget_COUNT] = { str_lit(""), str_lit("iphone"), str_lit("iphonesimulator"), str_lit("android"), }; enum QueryDataSetKind { QueryDataSet_Invalid, QueryDataSet_GlobalDefinitions, QueryDataSet_GoToDefinitions, }; struct QueryDataSetSettings { QueryDataSetKind kind; bool ok; bool compact; }; enum BuildModeKind { BuildMode_Executable, BuildMode_DynamicLibrary, BuildMode_StaticLibrary, BuildMode_Object, BuildMode_Assembly, BuildMode_LLVM_IR, BuildMode_COUNT, }; enum CommandKind : u64 { Command_run = 1<<0, Command_build = 1<<1, Command_check = 1<<2, Command_doc = 1<<3, Command_version = 1<<4, Command_test = 1<<5, Command_strip_semicolon = 1<<6, Command_bug_report = 1<<7, Command_bundle_android = 1<<8, Command_bundle_macos = 1<<9, Command_bundle_ios = 1<<10, Command_bundle_orca = 1<<11, Command__does_check = Command_run|Command_build|Command_check|Command_doc|Command_test|Command_strip_semicolon, Command__does_build = Command_run|Command_build|Command_test, Command_all = ~(CommandKind)0, }; gb_global char const *odin_command_strings[32] = { "run", "build", "check", "doc", "version", "test", "strip-semicolon", "", "bundle android", "bundle macos", "bundle ios", "bundle orca", }; enum CmdDocFlag : u32 { CmdDocFlag_Short = 1<<0, CmdDocFlag_AllPackages = 1<<1, CmdDocFlag_DocFormat = 1<<2, }; enum TimingsExportFormat : i32 { TimingsExportUnspecified = 0, TimingsExportJson = 1, TimingsExportCSV = 2, }; enum DependenciesExportFormat : i32 { DependenciesExportUnspecified = 0, DependenciesExportMake = 1, DependenciesExportJson = 2, }; enum ErrorPosStyle { ErrorPosStyle_Default, // path(line:column) msg ErrorPosStyle_Unix, // path:line:column: msg ErrorPosStyle_COUNT }; enum RelocMode : u8 { RelocMode_Default, RelocMode_Static, RelocMode_PIC, RelocMode_DynamicNoPIC, }; enum BuildPath : u8 { BuildPath_Main_Package, // Input Path to the package directory (or file) we're building. BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`. BuildPath_RES, // Output Path for .res file, generated from previous. BuildPath_Win_SDK_Bin_Path, // windows_sdk_bin_path BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path BuildPath_VS_EXE, // vs_exe_path BuildPath_VS_LIB, // vs_library_path BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`. BuildPath_Symbols, // Output Path for .pdb or .dSym file, can be overridden with `-pdb-name:`. BuildPathCOUNT, }; enum VetFlags : u64 { VetFlag_NONE = 0, VetFlag_Shadowing = 1u<<0, VetFlag_UsingStmt = 1u<<1, VetFlag_UsingParam = 1u<<2, VetFlag_Style = 1u<<3, VetFlag_Semicolon = 1u<<4, VetFlag_UnusedVariables = 1u<<5, VetFlag_UnusedImports = 1u<<6, VetFlag_Deprecated = 1u<<7, VetFlag_Cast = 1u<<8, VetFlag_Tabs = 1u<<9, VetFlag_UnusedProcedures = 1u<<10, VetFlag_ExplicitAllocators = 1u<<11, VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports, VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt|VetFlag_Deprecated|VetFlag_Cast, VetFlag_Using = VetFlag_UsingStmt|VetFlag_UsingParam, }; u64 get_vet_flag_from_name(String const &name) { if (name == "unused") { return VetFlag_Unused; } else if (name == "unused-variables") { return VetFlag_UnusedVariables; } else if (name == "unused-imports") { return VetFlag_UnusedImports; } else if (name == "shadowing") { return VetFlag_Shadowing; } else if (name == "using-stmt") { return VetFlag_UsingStmt; } else if (name == "using-param") { return VetFlag_UsingParam; } else if (name == "style") { return VetFlag_Style; } else if (name == "semicolon") { return VetFlag_Semicolon; } else if (name == "deprecated") { return VetFlag_Deprecated; } else if (name == "cast") { return VetFlag_Cast; } else if (name == "tabs") { return VetFlag_Tabs; } else if (name == "unused-procedures") { return VetFlag_UnusedProcedures; } else if (name == "explicit-allocators") { return VetFlag_ExplicitAllocators; } return VetFlag_NONE; } enum OptInFeatureFlags : u64 { OptInFeatureFlag_NONE = 0, OptInFeatureFlag_DynamicLiterals = 1u<<0, }; u64 get_feature_flag_from_name(String const &name) { if (name == "dynamic-literals") { return OptInFeatureFlag_DynamicLiterals; } return OptInFeatureFlag_NONE; } enum SanitizerFlags : u32 { SanitizerFlag_NONE = 0, SanitizerFlag_Address = 1u<<0, SanitizerFlag_Memory = 1u<<1, SanitizerFlag_Thread = 1u<<2, }; struct BuildCacheData { u64 crc; String cache_dir; // manifests String files_path; String args_path; String env_path; bool copy_already_done; }; enum LinkerChoice : i32 { Linker_Invalid = -1, Linker_Default = 0, Linker_lld, Linker_radlink, Linker_COUNT, }; enum SourceCodeLocationInfo : u8 { SourceCodeLocationInfo_Normal = 0, SourceCodeLocationInfo_Obfuscated = 1, SourceCodeLocationInfo_Filename = 2, SourceCodeLocationInfo_None = 3, }; String linker_choices[Linker_COUNT] = { str_lit("default"), str_lit("lld"), str_lit("radlink"), }; // This stores the information for the specify architecture of this build struct BuildContext { // Constants String ODIN_OS; // Target operating system String ODIN_ARCH; // Target architecture String ODIN_VENDOR; // Compiler vendor String ODIN_VERSION; // Compiler version String ODIN_ROOT; // Odin ROOT String ODIN_BUILD_PROJECT_NAME; // Odin main/initial package's directory name Windows_Subsystem ODIN_WINDOWS_SUBSYSTEM; // .Console, .Windows bool ODIN_DEBUG; // Odin in debug mode bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing) bool ODIN_DEFAULT_TO_PANIC_ALLOCATOR; // Whether the default allocator is a "panic" allocator or not (i.e. panics on any call to it) bool ODIN_FOREIGN_ERROR_PROCEDURES; bool ODIN_VALGRIND_SUPPORT; ErrorPosStyle ODIN_ERROR_POS_STYLE; TargetEndianKind endian_kind; // In bytes i64 ptr_size; // Size of a pointer, must be >= 4 i64 int_size; // Size of a int/uint, must be >= 4 i64 max_align; // max alignment, must be >= 1 (and typically >= ptr_size) i64 max_simd_align; // max alignment, must be >= 1 (and typically >= ptr_size) CommandKind command_kind; String command; TargetMetrics metrics; bool show_help; Array build_paths; // Contains `Path` objects to output filename, pdb, resource and intermediate files. // BuildPath enum contains the indices of paths we know *before* the work starts. String out_filepath; String resource_filepath; String pdb_filepath; u64 vet_flags; u32 sanitizer_flags; StringSet vet_packages; bool has_resource; String link_flags; String extra_linker_flags; String extra_assembler_flags; String microarch; BuildModeKind build_mode; bool keep_executable; bool generate_docs; bool custom_optimization_level; i32 optimization_level; bool show_timings; TimingsExportFormat export_timings_format; String export_timings_file; DependenciesExportFormat export_dependencies_format; String export_dependencies_file; bool show_unused; bool show_unused_with_location; bool show_more_timings; bool show_defineables; String export_defineables_file; bool show_system_calls; bool keep_temp_files; bool ignore_unknown_attributes; bool no_bounds_check; bool no_type_assert; bool dynamic_literals; // Opt-in to `#+feature dynamic-literals` project-wide. bool no_output_files; bool no_crt; bool no_rpath; bool no_entry_point; bool no_thread_local; bool cross_compiling; bool different_os; bool keep_object_files; bool disallow_do; LinkerChoice linker_choice; StringSet custom_attributes; bool strict_style; bool ignore_warnings; bool warnings_as_errors; bool hide_error_line; bool terse_errors; bool json_errors; bool has_ansi_terminal_colours; bool fast_isel; bool ignore_lazy; bool ignore_llvm_build; bool ignore_panic; bool ignore_microsoft_magic; bool linker_map_file; bool use_single_module; bool use_separate_modules; bool module_per_file; bool cached; BuildCacheData build_cache_data; bool internal_no_inline; bool internal_by_value; bool no_threaded_checker; bool show_debug_messages; bool copy_file_contents; bool no_rtti; bool dynamic_map_calls; SourceCodeLocationInfo source_code_location_info; bool min_link_libs; bool print_linker_flags; RelocMode reloc_mode; bool disable_red_zone; isize max_error_count; bool tilde_backend; u32 cmd_doc_flags; Array extra_packages; bool test_all_packages; gbAffinity affinity; isize thread_count; PtrMap defined_values; StringSet target_features_set; String target_features_string; bool strict_target_features; String minimum_os_version_string; bool minimum_os_version_string_given; int ODIN_ANDROID_API_LEVEL; String ODIN_ANDROID_SDK; String ODIN_ANDROID_NDK; String ODIN_ANDROID_NDK_TOOLCHAIN; String ODIN_ANDROID_NDK_TOOLCHAIN_LIB; String ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL; String ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT; String android_keystore; String android_keystore_alias; String android_keystore_password; }; gb_global BuildContext build_context = {0}; gb_internal bool IS_ODIN_DEBUG(void) { return build_context.ODIN_DEBUG; } gb_internal bool global_warnings_as_errors(void) { return build_context.warnings_as_errors; } gb_internal bool global_ignore_warnings(void) { return build_context.ignore_warnings; } gb_internal isize MAX_ERROR_COLLECTOR_COUNT(void) { if (build_context.max_error_count <= 0) { return DEFAULT_MAX_ERROR_COLLECTOR_COUNT; } return build_context.max_error_count; } // NOTE: AMD64 targets had their alignment on 128 bit ints bumped from 8 to 16 (undocumented of course). #if LLVM_VERSION_MAJOR >= 18 #define AMD64_MAX_ALIGNMENT 16 #else #define AMD64_MAX_ALIGNMENT 8 #endif #if LLVM_VERSION_MAJOR >= 18 #define I386_MAX_ALIGNMENT 16 #else #define I386_MAX_ALIGNMENT 4 #endif gb_global TargetMetrics target_windows_i386 = { TargetOs_windows, TargetArch_i386, 4, 4, I386_MAX_ALIGNMENT, 16, str_lit("i386-pc-windows-msvc"), }; gb_global TargetMetrics target_windows_amd64 = { TargetOs_windows, TargetArch_amd64, 8, 8, AMD64_MAX_ALIGNMENT, 32, str_lit("x86_64-pc-windows-msvc"), }; gb_global TargetMetrics target_linux_i386 = { TargetOs_linux, TargetArch_i386, 4, 4, I386_MAX_ALIGNMENT, 16, str_lit("i386-pc-linux-gnu"), }; gb_global TargetMetrics target_linux_amd64 = { TargetOs_linux, TargetArch_amd64, 8, 8, AMD64_MAX_ALIGNMENT, 32, str_lit("x86_64-pc-linux-gnu"), }; gb_global TargetMetrics target_linux_arm64 = { TargetOs_linux, TargetArch_arm64, 8, 8, 16, 32, str_lit("aarch64-linux-elf"), }; gb_global TargetMetrics target_linux_arm32 = { TargetOs_linux, TargetArch_arm32, 4, 4, 8, 16, str_lit("arm-unknown-linux-gnueabihf"), }; gb_global TargetMetrics target_linux_riscv64 = { TargetOs_linux, TargetArch_riscv64, 8, 8, 16, 32, str_lit("riscv64-linux-gnu"), }; gb_global TargetMetrics target_darwin_amd64 = { TargetOs_darwin, TargetArch_amd64, 8, 8, AMD64_MAX_ALIGNMENT, 32, str_lit("x86_64-apple-macosx"), // NOTE: Changes during initialization based on build flags. }; gb_global TargetMetrics target_darwin_arm64 = { TargetOs_darwin, TargetArch_arm64, 8, 8, 16, 32, str_lit("arm64-apple-macosx"), // NOTE: Changes during initialization based on build flags. }; gb_global TargetMetrics target_freebsd_i386 = { TargetOs_freebsd, TargetArch_i386, 4, 4, I386_MAX_ALIGNMENT, 16, str_lit("i386-unknown-freebsd-elf"), }; gb_global TargetMetrics target_freebsd_amd64 = { TargetOs_freebsd, TargetArch_amd64, 8, 8, AMD64_MAX_ALIGNMENT, 32, str_lit("x86_64-unknown-freebsd-elf"), }; gb_global TargetMetrics target_freebsd_arm64 = { TargetOs_freebsd, TargetArch_arm64, 8, 8, 16, 32, str_lit("aarch64-unknown-freebsd-elf"), }; gb_global TargetMetrics target_openbsd_amd64 = { TargetOs_openbsd, TargetArch_amd64, 8, 8, AMD64_MAX_ALIGNMENT, 32, str_lit("x86_64-unknown-openbsd-elf"), }; gb_global TargetMetrics target_netbsd_amd64 = { TargetOs_netbsd, TargetArch_amd64, 8, 8, AMD64_MAX_ALIGNMENT, 32, str_lit("x86_64-unknown-netbsd-elf"), }; gb_global TargetMetrics target_netbsd_arm64 = { TargetOs_netbsd, TargetArch_arm64, 8, 8, 16, 32, str_lit("aarch64-unknown-netbsd-elf"), }; gb_global TargetMetrics target_haiku_amd64 = { TargetOs_haiku, TargetArch_amd64, 8, 8, AMD64_MAX_ALIGNMENT, 32, str_lit("x86_64-unknown-haiku"), }; gb_global TargetMetrics target_essence_amd64 = { TargetOs_essence, TargetArch_amd64, 8, 8, AMD64_MAX_ALIGNMENT, 32, str_lit("x86_64-pc-none-elf"), }; gb_global TargetMetrics target_freestanding_wasm32 = { TargetOs_freestanding, TargetArch_wasm32, 4, 4, 8, 16, str_lit("wasm32-freestanding-js"), }; gb_global TargetMetrics target_js_wasm32 = { TargetOs_js, TargetArch_wasm32, 4, 4, 8, 16, str_lit("wasm32-js-js"), }; gb_global TargetMetrics target_wasi_wasm32 = { TargetOs_wasi, TargetArch_wasm32, 4, 4, 8, 16, str_lit("wasm32-wasi-js"), }; gb_global TargetMetrics target_orca_wasm32 = { TargetOs_orca, TargetArch_wasm32, 4, 4, 8, 16, str_lit("wasm32-wasi-js"), }; gb_global TargetMetrics target_freestanding_wasm64p32 = { TargetOs_freestanding, TargetArch_wasm64p32, 4, 8, 8, 16, str_lit("wasm32-freestanding-js"), }; gb_global TargetMetrics target_js_wasm64p32 = { TargetOs_js, TargetArch_wasm64p32, 4, 8, 8, 16, str_lit("wasm32-js-js"), }; gb_global TargetMetrics target_wasi_wasm64p32 = { TargetOs_wasi, TargetArch_wasm32, 4, 8, 8, 16, str_lit("wasm32-wasi-js"), }; gb_global TargetMetrics target_freestanding_amd64_sysv = { TargetOs_freestanding, TargetArch_amd64, 8, 8, AMD64_MAX_ALIGNMENT, 32, str_lit("x86_64-pc-none-gnu"), TargetABI_SysV, }; gb_global TargetMetrics target_freestanding_amd64_win64 = { TargetOs_freestanding, TargetArch_amd64, 8, 8, AMD64_MAX_ALIGNMENT, 32, str_lit("x86_64-pc-none-msvc"), TargetABI_Win64, }; gb_global TargetMetrics target_freestanding_arm64 = { TargetOs_freestanding, TargetArch_arm64, 8, 8, 16, 32, str_lit("aarch64-none-elf"), }; gb_global TargetMetrics target_freestanding_arm32 = { TargetOs_freestanding, TargetArch_arm32, 4, 4, 8, 16, str_lit("arm-unknown-unknown-gnueabihf"), }; gb_global TargetMetrics target_freestanding_riscv64 = { TargetOs_freestanding, TargetArch_riscv64, 8, 8, 16, 32, str_lit("riscv64-unknown-gnu"), }; struct NamedTargetMetrics { String name; TargetMetrics *metrics; }; gb_global NamedTargetMetrics named_targets[] = { { str_lit("darwin_amd64"), &target_darwin_amd64 }, { str_lit("darwin_arm64"), &target_darwin_arm64 }, { str_lit("essence_amd64"), &target_essence_amd64 }, { str_lit("linux_i386"), &target_linux_i386 }, { str_lit("linux_amd64"), &target_linux_amd64 }, { str_lit("linux_arm64"), &target_linux_arm64 }, { str_lit("linux_arm32"), &target_linux_arm32 }, { str_lit("linux_riscv64"), &target_linux_riscv64 }, { str_lit("windows_i386"), &target_windows_i386 }, { str_lit("windows_amd64"), &target_windows_amd64 }, { str_lit("freebsd_i386"), &target_freebsd_i386 }, { str_lit("freebsd_amd64"), &target_freebsd_amd64 }, { str_lit("freebsd_arm64"), &target_freebsd_arm64 }, { str_lit("netbsd_amd64"), &target_netbsd_amd64 }, { str_lit("netbsd_arm64"), &target_netbsd_arm64 }, { str_lit("openbsd_amd64"), &target_openbsd_amd64 }, { str_lit("haiku_amd64"), &target_haiku_amd64 }, { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, { str_lit("wasi_wasm32"), &target_wasi_wasm32 }, { str_lit("js_wasm32"), &target_js_wasm32 }, { str_lit("orca_wasm32"), &target_orca_wasm32 }, { str_lit("freestanding_wasm64p32"), &target_freestanding_wasm64p32 }, { str_lit("js_wasm64p32"), &target_js_wasm64p32 }, { str_lit("wasi_wasm64p32"), &target_wasi_wasm64p32 }, { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv }, { str_lit("freestanding_amd64_win64"), &target_freestanding_amd64_win64 }, { str_lit("freestanding_arm64"), &target_freestanding_arm64 }, { str_lit("freestanding_arm32"), &target_freestanding_arm32 }, { str_lit("freestanding_riscv64"), &target_freestanding_riscv64 }, }; gb_global NamedTargetMetrics *selected_target_metrics; gb_global Subtarget selected_subtarget; gb_internal TargetOsKind get_target_os_from_string(String str, Subtarget *subtarget_ = nullptr, String *subtarget_str = nullptr) { String os_name = str; String subtarget = {}; auto part = string_partition(str, str_lit(":")); if (part.match.len == 1) { os_name = part.head; subtarget = part.tail; } TargetOsKind kind = TargetOs_Invalid; for (isize i = 0; i < TargetOs_COUNT; i++) { if (str_eq_ignore_case(target_os_names[i], os_name)) { kind = cast(TargetOsKind)i; break; } } if (subtarget_str) *subtarget_str = subtarget; if (subtarget_) { if (subtarget.len != 0) { *subtarget_ = Subtarget_Invalid; if (str_eq_ignore_case(subtarget, "generic") || str_eq_ignore_case(subtarget, "default")) { *subtarget_ = Subtarget_Default; } else { for (isize i = 1; i < Subtarget_COUNT; i++) { if (str_eq_ignore_case(subtarget_strings[i], subtarget)) { *subtarget_ = cast(Subtarget)i; break; } } } } else { *subtarget_ = Subtarget_Default; } } return kind; } gb_internal TargetArchKind get_target_arch_from_string(String str) { for (isize i = 0; i < TargetArch_COUNT; i++) { if (str_eq_ignore_case(target_arch_names[i], str)) { return cast(TargetArchKind)i; } } return TargetArch_Invalid; } gb_internal bool is_excluded_target_filename(String name) { String original_name = name; name = remove_extension_from_path(name); if (string_starts_with(name, str_lit("."))) { // Ignore .*.odin files return true; } if (build_context.command_kind != Command_test) { String test_suffix = str_lit("_test"); if (string_ends_with(name, test_suffix) && name != test_suffix) { // Ignore *_test.odin files return true; } } String str1 = {}; String str2 = {}; isize n = 0; str1 = name; n = str1.len; for (isize i = str1.len-1; i >= 0 && str1[i] != '_'; i--) { n -= 1; } str1 = substring(str1, n, str1.len); str2 = substring(name, 0, gb_max(n-1, 0)); n = str2.len; for (isize i = str2.len-1; i >= 0 && str2[i] != '_'; i--) { n -= 1; } str2 = substring(str2, n, str2.len); if (str1 == name) { return false; } TargetOsKind os1 = get_target_os_from_string(str1); TargetArchKind arch1 = get_target_arch_from_string(str1); TargetOsKind os2 = get_target_os_from_string(str2); TargetArchKind arch2 = get_target_arch_from_string(str2); if (os1 != TargetOs_Invalid && arch2 != TargetArch_Invalid) { return os1 != build_context.metrics.os || arch2 != build_context.metrics.arch; } else if (arch1 != TargetArch_Invalid && os2 != TargetOs_Invalid) { return arch1 != build_context.metrics.arch || os2 != build_context.metrics.os; } else if (os1 != TargetOs_Invalid) { return os1 != build_context.metrics.os; } else if (arch1 != TargetArch_Invalid) { return arch1 != build_context.metrics.arch; } return false; } struct LibraryCollections { String name; String path; }; gb_global Array library_collections = {0}; gb_internal void add_library_collection(String name, String path) { LibraryCollections lc = {name, string_trim_whitespace(path)}; array_add(&library_collections, lc); } gb_internal bool find_library_collection_path(String name, String *path) { for (auto const &lc : library_collections) { if (lc.name == name) { if (path) *path = lc.path; return true; } } return false; } gb_internal bool is_arch_wasm(void) { switch (build_context.metrics.arch) { case TargetArch_wasm32: case TargetArch_wasm64p32: return true; } return false; } gb_internal bool is_arch_x86(void) { switch (build_context.metrics.arch) { case TargetArch_i386: case TargetArch_amd64: return true; } return false; } // TODO(bill): OS dependent versions for the BuildContext // join_path // is_dir // is_file // is_abs_path // has_subdir gb_global String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1}; gb_global String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1}; gb_global String const SEPARATOR_STRING = #if defined(GB_SYSTEM_WINDOWS) WIN32_SEPARATOR_STRING; #else NIX_SEPARATOR_STRING; #endif gb_global String const WASM_MODULE_NAME_SEPARATOR = str_lit(".."); gb_internal String internal_odin_root_dir(void); gb_internal String odin_root_dir(void) { if (global_module_path_set) { return global_module_path; } gbAllocator a = permanent_allocator(); char const *found = gb_get_env("ODIN_ROOT", a); if (found) { String path = path_to_full_path(a, make_string_c(found)); #if defined(GB_SYSTEM_WINDOWS) path = normalize_path(a, path, WIN32_SEPARATOR_STRING); #else path = normalize_path(a, path, NIX_SEPARATOR_STRING); #endif global_module_path = path; global_module_path_set = true; return global_module_path; } return internal_odin_root_dir(); } #if defined(GB_SYSTEM_WINDOWS) gb_internal String internal_odin_root_dir(void) { String path = global_module_path; isize len, i; wchar_t *text; if (global_module_path_set) { return global_module_path; } auto path_buf = array_make(heap_allocator(), 300); len = 0; for (;;) { len = GetModuleFileNameW(nullptr, &path_buf[0], cast(int)path_buf.count); if (len == 0) { return make_string(nullptr, 0); } if (len < path_buf.count) { break; } array_resize(&path_buf, 2*path_buf.count + 300); } len += 1; // NOTE(bill): It needs an extra 1 for some reason mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); GetModuleFileNameW(nullptr, text, cast(int)len); path = string16_to_string(heap_allocator(), make_string16(text, len)); for (i = path.len-1; i >= 0; i--) { u8 c = path[i]; if (c == '/' || c == '\\') { break; } path.len--; } global_module_path = path; global_module_path_set = true; array_free(&path_buf); return path; } #elif defined(GB_SYSTEM_HAIKU) #include gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_); gb_internal String internal_odin_root_dir(void) { String path = global_module_path; isize len, i; u8 *text; if (global_module_path_set) { return global_module_path; } auto path_buf = array_make(heap_allocator(), 300); defer (array_free(&path_buf)); len = 0; for (;;) { u32 sz = path_buf.count; int res = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, nullptr, &path_buf[0], sz); if(res == B_OK) { len = sz; break; } else { array_resize(&path_buf, sz + 1); } } mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); text = gb_alloc_array(permanent_allocator(), u8, len + 1); gb_memmove(text, &path_buf[0], len); path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr); for (i = path.len-1; i >= 0; i--) { u8 c = path[i]; if (c == '/' || c == '\\') { break; } path.len--; } global_module_path = path; global_module_path_set = true; return path; } #elif defined(GB_SYSTEM_OSX) #include gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_); gb_internal String internal_odin_root_dir(void) { String path = global_module_path; isize len, i; u8 *text; if (global_module_path_set) { return global_module_path; } auto path_buf = array_make(heap_allocator(), 300); defer (array_free(&path_buf)); len = 0; for (;;) { u32 sz = path_buf.count; int res = _NSGetExecutablePath(&path_buf[0], &sz); if(res == 0) { len = sz; break; } else { array_resize(&path_buf, sz + 1); } } mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); text = gb_alloc_array(permanent_allocator(), u8, len + 1); gb_memmove(text, &path_buf[0], len); path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr); for (i = path.len-1; i >= 0; i--) { u8 c = path[i]; if (c == '/' || c == '\\') { break; } path.len--; } global_module_path = path; global_module_path_set = true; return path; } #else // NOTE: Linux / Unix is unfinished and not tested very well. #include gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_); gb_internal String internal_odin_root_dir(void) { String path = global_module_path; isize len, i; u8 *text; if (global_module_path_set) { return global_module_path; } auto path_buf = array_make(heap_allocator(), 300); defer (array_free(&path_buf)); len = 0; for (;;) { // This is not a 100% reliable system, but for the purposes // of this compiler, it should be _good enough_. // That said, there's no solid 100% method on Linux to get the program's // path without checking this link. Sorry. #if defined(GB_SYSTEM_FREEBSD) int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = -1; len = path_buf.count; sysctl(mib, 4, &path_buf[0], (size_t *) &len, NULL, 0); #elif defined(GB_SYSTEM_NETBSD) len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count); #elif defined(GB_SYSTEM_DRAGONFLYBSD) len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count); #elif defined(GB_SYSTEM_LINUX) len = readlink("/proc/self/exe", &path_buf[0], path_buf.count); #elif defined(GB_SYSTEM_OPENBSD) int error; int mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV, }; // get argv size error = sysctl(mib, 4, NULL, (size_t *) &len, NULL, 0); if (error == -1) { // sysctl error return make_string(nullptr, 0); } // get argv char **argv = (char **)gb_malloc(len); error = sysctl(mib, 4, argv, (size_t *) &len, NULL, 0); if (error == -1) { // sysctl error gb_mfree(argv); return make_string(nullptr, 0); } // NOTE(Jeroen): // On OpenBSD, if `odin` is on the path, `argv[0]` will contain just `odin`, // even though that isn't then the relative path. // When run from Odin's directory, it returns `./odin`. // Check argv[0] for lack of / to see if it's a reference to PATH. // If so, walk PATH to find the executable. len = gb_strlen(argv[0]); bool slash_found = false; bool odin_found = false; for (int i = 0; i < len; i += 1) { if (argv[0][i] == '/') { slash_found = true; break; } } if (slash_found) { // copy argv[0] to path_buf if(len < path_buf.count) { gb_memmove(&path_buf[0], argv[0], len); odin_found = true; } } else { gbAllocator a = heap_allocator(); char const *path_env = gb_get_env("PATH", a); defer (gb_free(a, cast(void *)path_env)); if (path_env) { int path_len = gb_strlen(path_env); int path_start = 0; int path_end = 0; for (; path_start < path_len; ) { for (; path_end <= path_len; path_end++) { if (path_env[path_end] == ':' || path_end == path_len) { break; } } String path_needle = (const String)make_string((const u8 *)(path_env + path_start), path_end - path_start); String argv0 = (const String)make_string((const u8 *)argv[0], len); String odin_candidate = concatenate3_strings(a, path_needle, STR_LIT("/"), argv0); defer (gb_free(a, odin_candidate.text)); if (gb_file_exists((const char *)odin_candidate.text)) { len = odin_candidate.len; if(len < path_buf.count) { gb_memmove(&path_buf[0], odin_candidate.text, len); } odin_found = true; break; } path_start = path_end + 1; path_end = path_start; if (path_start > path_len) { break; } } } if (!odin_found) { gb_printf_err("Odin could not locate itself in PATH, and ODIN_ROOT wasn't set either.\n"); } } gb_mfree(argv); #endif if(len == 0 || len == -1) { return make_string(nullptr, 0); } if (len < path_buf.count) { break; } array_resize(&path_buf, 2*path_buf.count + 300); } mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); text = gb_alloc_array(permanent_allocator(), u8, len + 1); gb_memmove(text, &path_buf[0], len); path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr); for (i = path.len-1; i >= 0; i--) { u8 c = path[i]; if (c == '/' || c == '\\') { break; } path.len--; } global_module_path = path; global_module_path_set = true; return path; } #endif gb_global BlockingMutex fullpath_mutex; #if defined(GB_SYSTEM_WINDOWS) gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) { String result = {}; String16 string16 = string_to_string16(heap_allocator(), s); defer (gb_free(heap_allocator(), string16.text)); DWORD len; mutex_lock(&fullpath_mutex); len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr); if (len != 0) { wchar_t *text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); GetFullPathNameW(&string16[0], len, text, nullptr); mutex_unlock(&fullpath_mutex); text[len] = 0; result = string16_to_string(a, make_string16(text, len)); result = string_trim_whitespace(result); // Replace Windows style separators for (isize i = 0; i < result.len; i++) { if (result.text[i] == '\\') { result.text[i] = '/'; } } if (ok_) *ok_ = true; } else { if (ok_) *ok_ = false; mutex_unlock(&fullpath_mutex); } return result; } #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) struct PathToFullpathResult { String result; bool ok; }; gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) { static gb_thread_local StringMap cache; PathToFullpathResult *cached = string_map_get(&cache, s); if (cached != nullptr) { if (ok_) *ok_ = cached->ok; return copy_string(a, cached->result); } char *p; p = realpath(cast(char *)s.text, 0); defer (free(p)); if(p == nullptr) { if (ok_) *ok_ = false; // Path doesn't exist or is malformed, Windows's `GetFullPathNameW` does not check for // existence of the file where `realpath` does, which causes different behaviour between platforms. // Two things could be done here: // 1. clean the path and resolve it manually, just like the Windows function does, // probably requires porting `filepath.clean` from Odin and doing some more processing. // 2. just return a copy of the original path. // // I have opted for 2 because it is much simpler + we already return `ok = false` + further // checks and processes will use the path and cause errors (which we want). String result = copy_string(a, s); PathToFullpathResult cached_result = {}; cached_result.result = copy_string(permanent_allocator(), result); cached_result.ok = false; string_map_set(&cache, copy_string(permanent_allocator(), s), cached_result); return result; } if (ok_) *ok_ = true; String result = copy_string(a, make_string_c(p)); PathToFullpathResult cached_result = {}; cached_result.result = copy_string(permanent_allocator(), result); cached_result.ok = true; string_map_set(&cache, copy_string(permanent_allocator(), s), cached_result); return result; } #else #error Implement system #endif gb_internal String get_fullpath_relative(gbAllocator a, String base_dir, String path, bool *ok_) { u8 *str = gb_alloc_array(heap_allocator(), u8, base_dir.len+1+path.len+1); defer (gb_free(heap_allocator(), str)); isize i = 0; gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len; gb_memmove(str+i, "/", 1); i += 1; gb_memmove(str+i, path.text, path.len); i += path.len; str[i] = 0; // IMPORTANT NOTE(bill): Remove trailing path separators // this is required to make sure there is a conventional // notation for the path for (/**/; i > 0; i--) { u8 c = str[i-1]; if (c != '/' && c != '\\') { break; } } String res = make_string(str, i); res = string_trim_whitespace(res); return path_to_fullpath(a, res, ok_); } gb_internal String get_fullpath_base_collection(gbAllocator a, String path, bool *ok_) { String module_dir = odin_root_dir(); String base = str_lit("base/"); isize str_len = module_dir.len + base.len + path.len; u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1); defer (gb_free(heap_allocator(), str)); isize i = 0; gb_memmove(str+i, module_dir.text, module_dir.len); i += module_dir.len; gb_memmove(str+i, base.text, base.len); i += base.len; gb_memmove(str+i, path.text, path.len); i += path.len; str[i] = 0; String res = make_string(str, i); res = string_trim_whitespace(res); return path_to_fullpath(a, res, ok_); } gb_internal String get_fullpath_core_collection(gbAllocator a, String path, bool *ok_) { String module_dir = odin_root_dir(); String core = str_lit("core/"); isize str_len = module_dir.len + core.len + path.len; u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1); defer (gb_free(heap_allocator(), str)); isize i = 0; gb_memmove(str+i, module_dir.text, module_dir.len); i += module_dir.len; gb_memmove(str+i, core.text, core.len); i += core.len; gb_memmove(str+i, path.text, path.len); i += path.len; str[i] = 0; String res = make_string(str, i); res = string_trim_whitespace(res); return path_to_fullpath(a, res, ok_); } gb_internal bool show_error_line(void) { return !build_context.hide_error_line && !build_context.json_errors; } gb_internal bool terse_errors(void) { return build_context.terse_errors; } gb_internal bool json_errors(void) { return build_context.json_errors; } gb_internal bool has_ansi_terminal_colours(void) { return build_context.has_ansi_terminal_colours && !json_errors(); } gb_internal void init_android_values(bool with_sdk) { auto *bc = &build_context; { // Android SDK/API Level String default_level = str_lit("34"); if (!bc->minimum_os_version_string_given) { bc->minimum_os_version_string = default_level; } BigInt level = {}; bool success = false; big_int_from_string(&level, bc->minimum_os_version_string, &success); if (!success) { gb_printf_err("Warning: Invalid -minimum-os-version:%.*s for -subtarget:Android, defaulting to %.*s\n", LIT(bc->minimum_os_version_string), LIT(default_level)); bc->minimum_os_version_string = default_level; big_int_from_string(&level, bc->minimum_os_version_string, &success); GB_ASSERT(success); } i64 new_level = big_int_to_i64(&level); if (new_level >= 21) { bc->ODIN_ANDROID_API_LEVEL = cast(int)new_level; } else { gb_printf_err("Warning: Invalid -minimum-os-version:%.*s for -subtarget:Android, defaulting to %.*s\n", LIT(bc->minimum_os_version_string), LIT(default_level)); bc->ODIN_ANDROID_API_LEVEL = atoi(cast(char const *)default_level.text); } } bc->ODIN_ANDROID_NDK = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_NDK", permanent_allocator())), NIX_SEPARATOR_STRING); bc->ODIN_ANDROID_NDK_TOOLCHAIN = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_NDK_TOOLCHAIN", permanent_allocator())), NIX_SEPARATOR_STRING); bc->ODIN_ANDROID_SDK = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_SDK", permanent_allocator())), NIX_SEPARATOR_STRING); #if defined(GB_SYSTEM_WINDOWS) if (bc->ODIN_ANDROID_SDK.len == 0) { bc->ODIN_ANDROID_SDK = normalize_path(permanent_allocator(), path_to_fullpath(permanent_allocator(), str_lit("%LocalAppData%/Android/Sdk"), nullptr), NIX_SEPARATOR_STRING); } #endif if (bc->ODIN_ANDROID_NDK.len != 0 && bc->ODIN_ANDROID_NDK_TOOLCHAIN.len == 0) { String arch = str_lit("x86_64"); #if defined (GB_CPU_ARM) // TODO(bill): this is a complete guess arch = str_lit("aarch64"); #endif #if defined(GB_SYSTEM_WINDOWS) bc->ODIN_ANDROID_NDK_TOOLCHAIN = concatenate4_strings(temporary_allocator(), bc->ODIN_ANDROID_NDK, str_lit("toolchains/llvm/prebuilt/"), str_lit("windows-"), arch); #elif defined(GB_SYSTEM_OSX) // TODO(bill): is this name even correct? bc->ODIN_ANDROID_NDK_TOOLCHAIN = concatenate4_strings(temporary_allocator(), bc->ODIN_ANDROID_NDK, str_lit("toolchains/llvm/prebuilt/"), str_lit("darwin-"), arch); #elif defined(GB_SYSTEM_LINUX) bc->ODIN_ANDROID_NDK_TOOLCHAIN = concatenate4_strings(temporary_allocator(), bc->ODIN_ANDROID_NDK, str_lit("toolchains/llvm/prebuilt/"), str_lit("linux-"), arch); #endif bc->ODIN_ANDROID_NDK_TOOLCHAIN = normalize_path(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, NIX_SEPARATOR_STRING); } if (bc->ODIN_ANDROID_NDK.len == 0 && !with_sdk) { gb_printf_err("Error: ODIN_ANDROID_NDK not set"); gb_exit(1); } if (bc->ODIN_ANDROID_NDK_TOOLCHAIN.len == 0 && !with_sdk) { gb_printf_err("Error: ODIN_ANDROID_NDK not set"); gb_exit(1); } bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, str_lit("sysroot/usr/lib/aarch64-linux-android/")); char buf[32] = {}; gb_snprintf(buf, gb_size_of(buf), "%d/", bc->ODIN_ANDROID_API_LEVEL); bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB, make_string_c(buf)); bc->ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, str_lit("sysroot/")); if (with_sdk) { if (bc->ODIN_ANDROID_SDK.len == 0) { gb_printf_err("Error: ODIN_ANDROID_SDK not set, which is required for -build-mode:executable for -subtarget:android"); gb_exit(1); } if (bc->android_keystore.len == 0) { gb_printf_err("Error: -android-keystore: has not been set\n"); gb_exit(1); } } } gb_internal bool has_asm_extension(String const &path) { String ext = path_extension(path); if (ext == ".asm") { return true; } else if (ext == ".s") { return true; } else if (ext == ".S") { return true; } return false; } // temporary gb_internal char *token_pos_to_string(TokenPos const &pos) { gbString s = gb_string_make_reserve(temporary_allocator(), 128); String file = get_file_path_string(pos.file_id); switch (build_context.ODIN_ERROR_POS_STYLE) { default: /*fallthrough*/ case ErrorPosStyle_Default: s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column); break; case ErrorPosStyle_Unix: s = gb_string_append_fmt(s, "%.*s:%d:%d:", LIT(file), pos.line, pos.column); break; } return s; } gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subtarget) { BuildContext *bc = &build_context; gb_affinity_init(&bc->affinity); if (bc->thread_count == 0) { bc->thread_count = gb_max(bc->affinity.thread_count, 1); } bc->ODIN_VENDOR = str_lit("odin"); bc->ODIN_VERSION = ODIN_VERSION; bc->ODIN_ROOT = odin_root_dir(); if (bc->max_error_count <= 0) { bc->max_error_count = DEFAULT_MAX_ERROR_COLLECTOR_COUNT; } { char const *found = gb_get_env("ODIN_ERROR_POS_STYLE", permanent_allocator()); if (found) { ErrorPosStyle kind = ErrorPosStyle_Default; String style = make_string_c(found); style = string_trim_whitespace(style); if (style == "" || style == "default" || style == "odin") { kind = ErrorPosStyle_Default; } else if (style == "unix" || style == "gcc" || style == "clang" || style == "llvm") { kind = ErrorPosStyle_Unix; } else { gb_printf_err("Invalid ODIN_ERROR_POS_STYLE: got %.*s\n", LIT(style)); gb_printf_err("Valid formats:\n"); gb_printf_err("\t\"default\" or \"odin\"\n"); gb_printf_err("\t\tpath(line:column) message\n"); gb_printf_err("\t\"unix\"\n"); gb_printf_err("\t\tpath:line:column: message\n"); gb_exit(1); } build_context.ODIN_ERROR_POS_STYLE = kind; } } bc->copy_file_contents = true; TargetMetrics *metrics = nullptr; #if defined(GB_ARCH_64_BIT) #if defined(GB_SYSTEM_WINDOWS) metrics = &target_windows_amd64; #elif defined(GB_SYSTEM_OSX) #if defined(GB_CPU_ARM) metrics = &target_darwin_arm64; #else metrics = &target_darwin_amd64; #endif #elif defined(GB_SYSTEM_FREEBSD) #if defined(GB_CPU_ARM) metrics = &target_freebsd_arm64; #else metrics = &target_freebsd_amd64; #endif #elif defined(GB_SYSTEM_OPENBSD) metrics = &target_openbsd_amd64; #elif defined(GB_SYSTEM_NETBSD) #if defined(GB_CPU_ARM) metrics = &target_netbsd_arm64; #else metrics = &target_netbsd_amd64; #endif #elif defined(GB_SYSTEM_HAIKU) metrics = &target_haiku_amd64; #elif defined(GB_CPU_ARM) metrics = &target_linux_arm64; #elif defined(GB_CPU_RISCV) metrics = &target_linux_riscv64; #else metrics = &target_linux_amd64; #endif #elif defined(GB_CPU_ARM) #if defined(GB_SYSTEM_WINDOWS) #error "Build Error: Unsupported architecture" #elif defined(GB_SYSTEM_OSX) #error "Build Error: Unsupported architecture" #elif defined(GB_SYSTEM_FREEBSD) #error "Build Error: Unsupported architecture" #else metrics = &target_linux_arm32; #endif #else #if defined(GB_SYSTEM_WINDOWS) metrics = &target_windows_i386; #elif defined(GB_SYSTEM_OSX) #error "Build Error: Unsupported architecture" #elif defined(GB_SYSTEM_FREEBSD) metrics = &target_freebsd_i386; #else metrics = &target_linux_i386; #endif #endif if (cross_target != nullptr && metrics != cross_target) { bc->different_os = cross_target->os != metrics->os; bc->cross_compiling = true; metrics = cross_target; } GB_ASSERT(metrics->os != TargetOs_Invalid); GB_ASSERT(metrics->arch != TargetArch_Invalid); GB_ASSERT(metrics->ptr_size > 1); GB_ASSERT(metrics->int_size > 1); GB_ASSERT(metrics->max_align > 1); GB_ASSERT(metrics->max_simd_align > 1); GB_ASSERT(metrics->int_size >= metrics->ptr_size); if (metrics->int_size > metrics->ptr_size) { GB_ASSERT(metrics->int_size == 2*metrics->ptr_size); } bc->metrics = *metrics; bc->ODIN_OS = target_os_names[metrics->os]; bc->ODIN_ARCH = target_arch_names[metrics->arch]; bc->endian_kind = target_endians[metrics->arch]; bc->ptr_size = metrics->ptr_size; bc->int_size = metrics->int_size; bc->max_align = metrics->max_align; bc->max_simd_align = metrics->max_simd_align; bc->link_flags = str_lit(" "); #if defined(DEFAULT_TO_THREADED_CHECKER) bc->threaded_checker = true; #endif if (bc->disable_red_zone) { if (is_arch_wasm() && bc->metrics.os == TargetOs_freestanding) { gb_printf_err("-disable-red-zone is not support for this target"); gb_exit(1); } } if (bc->metrics.os == TargetOs_freestanding) { bc->no_entry_point = true; } else { if (bc->no_rtti) { gb_printf_err("-no-rtti is only allowed on freestanding targets\n"); gb_exit(1); } } // Default to subsystem:CONSOLE on Windows targets if (bc->ODIN_WINDOWS_SUBSYSTEM == Windows_Subsystem_UNKNOWN && bc->metrics.os == TargetOs_windows) { bc->ODIN_WINDOWS_SUBSYSTEM = Windows_Subsystem_CONSOLE; } if (subtarget == Subtarget_Android) { switch (build_context.build_mode) { case BuildMode_DynamicLibrary: case BuildMode_Object: case BuildMode_Assembly: case BuildMode_LLVM_IR: break; default: case BuildMode_Executable: case BuildMode_StaticLibrary: if ((build_context.command_kind & Command__does_build) != 0) { gb_printf_err("Unsupported -build-mode for -subtarget:android\n"); gb_printf_err("\tCurrently only supporting: \n"); // gb_printf_err("\t\texe\n"); gb_printf_err("\t\tshared\n"); gb_printf_err("\t\tobject\n"); gb_printf_err("\t\tassembly\n"); gb_printf_err("\t\tllvm-ir\n"); gb_exit(1); } break; } } if (metrics->os == TargetOs_darwin) { switch (subtarget) { case Subtarget_iPhone: switch (metrics->arch) { case TargetArch_arm64: bc->metrics.target_triplet = str_lit("arm64-apple-ios"); break; default: GB_PANIC("Unknown architecture for -subtarget:iphone"); } break; case Subtarget_iPhoneSimulator: switch (metrics->arch) { case TargetArch_arm64: bc->metrics.target_triplet = str_lit("arm64-apple-ios-simulator"); break; case TargetArch_amd64: bc->metrics.target_triplet = str_lit("x86_64-apple-ios-simulator"); break; default: GB_PANIC("Unknown architecture for -subtarget:iphonesimulator"); } break; } } else if (metrics->os == TargetOs_linux && subtarget == Subtarget_Android) { switch (metrics->arch) { case TargetArch_arm64: bc->metrics.target_triplet = str_lit("aarch64-none-linux-android"); bc->reloc_mode = RelocMode_PIC; break; default: GB_PANIC("Unknown architecture for -subtarget:android"); } } if (bc->metrics.os == TargetOs_windows) { switch (bc->metrics.arch) { case TargetArch_amd64: bc->link_flags = str_lit("/machine:x64 "); break; case TargetArch_i386: bc->link_flags = str_lit("/machine:x86 "); break; } } else if (bc->metrics.os == TargetOs_darwin) { bc->link_flags = concatenate3_strings(permanent_allocator(), str_lit("-target "), bc->metrics.target_triplet, str_lit(" ")); } else if (is_arch_wasm()) { gbString link_flags = gb_string_make(heap_allocator(), " "); // NOTE(laytan): Put the stack first in the memory, // causing a stack overflow to error immediately instead of corrupting globals. link_flags = gb_string_appendc(link_flags, "--stack-first "); // NOTE(laytan): default stack size is 64KiB, up to a more reasonable 1MiB. link_flags = gb_string_appendc(link_flags, "-z stack-size=1048576 "); // link_flags = gb_string_appendc(link_flags, "--export-all "); // link_flags = gb_string_appendc(link_flags, "--export-table "); // if (bc->metrics.arch == TargetArch_wasm64) { // link_flags = gb_string_appendc(link_flags, "-mwasm64 "); // } if (bc->metrics.os != TargetOs_orca) { link_flags = gb_string_appendc(link_flags, "--allow-undefined "); } if (bc->no_entry_point || bc->metrics.os == TargetOs_orca) { link_flags = gb_string_appendc(link_flags, "--no-entry "); } bc->link_flags = make_string_c(link_flags); // Disallow on wasm bc->use_separate_modules = false; } if(bc->metrics.arch == TargetArch_riscv64 && bc->cross_compiling) { bc->link_flags = str_lit("-target riscv64 "); } else { // NOTE: for targets other than darwin, we don't specify a `-target` link flag. // This is because we don't support cross-linking and clang is better at figuring // out what the actual target for linking is, // for example, on x86/alpine/musl it HAS to be `x86_64-alpine-linux-musl` to link correctly. // // Note that codegen will still target the triplet we specify, but the intricate details of // a target shouldn't matter as much to codegen (if it does at all) as it does to linking. } // NOTE: needs to be done after adding the -target flag to the linker flags so the linker // does not annoy the user with version warnings. if (metrics->os == TargetOs_darwin) { if (!bc->minimum_os_version_string_given) { if (subtarget == Subtarget_Default) { bc->minimum_os_version_string = str_lit("11.0.0"); } else if (subtarget == Subtarget_iPhone || subtarget == Subtarget_iPhoneSimulator) { // NOTE(harold): We default to 17.4 on iOS because that's when os_sync_wait_on_address was added and // we'd like to avoid any potential App Store issues by using the private ulock_* there. bc->minimum_os_version_string = str_lit("17.4"); } } if (subtarget == Subtarget_iPhoneSimulator) { // For the iPhoneSimulator subtarget, the version must be between 'ios' and '-simulator'. String suffix = str_lit("-simulator"); GB_ASSERT(string_ends_with(bc->metrics.target_triplet, suffix)); String prefix = substring(bc->metrics.target_triplet, 0, bc->metrics.target_triplet.len - suffix.len); bc->metrics.target_triplet = concatenate3_strings(permanent_allocator(), prefix, bc->minimum_os_version_string, suffix); } else { bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string); } } else if (selected_subtarget == Subtarget_Android) { init_android_values(bc->build_mode == BuildMode_Executable && (bc->command_kind & Command__does_build) != 0); } if (!bc->custom_optimization_level) { // NOTE(bill): when building with `-debug` but not specifying an optimization level // default to `-o:none` to improve the debug symbol generation by default if (bc->ODIN_DEBUG) { bc->optimization_level = -1; // -o:none } else { bc->optimization_level = 0; // -o:minimal } } bc->optimization_level = gb_clamp(bc->optimization_level, -1, 3); if (bc->optimization_level <= 0) { if (!is_arch_wasm()) { bc->use_separate_modules = true; } } if (build_context.use_single_module) { bc->use_separate_modules = false; } bc->ODIN_VALGRIND_SUPPORT = false; if (build_context.metrics.os != TargetOs_windows) { switch (bc->metrics.arch) { case TargetArch_amd64: bc->ODIN_VALGRIND_SUPPORT = true; break; } } if (bc->metrics.os == TargetOs_freestanding) { bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR = !bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR; } } #if defined(GB_SYSTEM_WINDOWS) // NOTE(IC): In order to find Visual C++ paths without relying on environment variables. // NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`. #include "microsoft_craziness.h" #endif // NOTE: the target feature and microarch lists are all sorted, so if it turns out to be slow (I don't think it will) // a binary search is possible. gb_internal bool check_single_target_feature_is_valid(String const &feature_list, String const &feature) { String_Iterator it = {feature_list, 0}; for (;;) { String str = string_split_iterator(&it, ','); if (str == "") break; if (str == feature) { return true; } } return false; } gb_internal bool check_target_feature_is_valid(String const &feature, TargetArchKind arch, String *invalid) { String feature_list = target_features_list[arch]; String_Iterator it = {feature, 0}; for (;;) { String str = string_split_iterator(&it, ','); if (str == "") break; if (!check_single_target_feature_is_valid(feature_list, str)) { if (invalid) *invalid = str; return false; } } return true; } gb_internal bool check_target_feature_is_valid_globally(String const &feature, String *invalid) { String_Iterator it = {feature, 0}; for (;;) { String str = string_split_iterator(&it, ','); if (str == "") break; bool valid = false; for (int arch = TargetArch_Invalid; arch < TargetArch_COUNT; arch += 1) { if (check_target_feature_is_valid(str, cast(TargetArchKind)arch, invalid)) { valid = true; break; } } if (!valid) { if (invalid) *invalid = str; return false; } } return true; } gb_internal bool check_target_feature_is_valid_for_target_arch(String const &feature, String *invalid) { return check_target_feature_is_valid(feature, build_context.metrics.arch, invalid); } gb_internal bool check_target_feature_is_enabled(String const &feature, String *not_enabled) { String_Iterator it = {feature, 0}; for (;;) { String str = string_split_iterator(&it, ','); if (str == "") break; if (!string_set_exists(&build_context.target_features_set, str)) { if (not_enabled) *not_enabled = str; return false; } } return true; } gb_internal bool check_target_feature_is_superset_of(String const &superset, String const &of, String *missing) { String_Iterator it = {of, 0}; for (;;) { String str = string_split_iterator(&it, ','); if (str == "") break; if (!check_single_target_feature_is_valid(superset, str)) { if (missing) *missing = str; return false; } } return true; } gb_internal String infer_object_extension_from_build_context() { String output_extension = {}; if (is_arch_wasm()) { output_extension = STR_LIT("wasm.o"); } else { switch (build_context.metrics.os) { case TargetOs_windows: output_extension = STR_LIT("obj"); break; default: case TargetOs_darwin: case TargetOs_linux: case TargetOs_essence: output_extension = STR_LIT("o"); break; case TargetOs_freestanding: switch (build_context.metrics.abi) { default: case TargetABI_Default: case TargetABI_SysV: output_extension = STR_LIT("o"); break; case TargetABI_Win64: output_extension = STR_LIT("obj"); break; } break; } } return output_extension; } // NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate. // We've previously called `parse_build_flags`, so `out_filepath` should be set. gb_internal bool init_build_paths(String init_filename) { gbAllocator ha = heap_allocator(); BuildContext *bc = &build_context; // NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index. array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT); string_set_init(&bc->target_features_set, 1024); // [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path. bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename); { String build_project_name = last_path_element(bc->build_paths[BuildPath_Main_Package].basename); GB_ASSERT(build_project_name.len > 0); bc->ODIN_BUILD_PROJECT_NAME = build_project_name; } bool produces_output_file = false; if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) { produces_output_file = true; } else if (bc->command_kind & Command__does_build) { produces_output_file = true; } if (!produces_output_file) { // Command doesn't produce output files. We're done. return true; } #if defined(GB_SYSTEM_WINDOWS) if (bc->metrics.os == TargetOs_windows) { if (bc->resource_filepath.len > 0) { bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath); if (!string_ends_with(bc->resource_filepath, str_lit(".res"))) { bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res")); bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath); bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc")); } } if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) { // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest. Find_Result find_result = find_visual_studio_and_windows_sdk(); if (find_result.windows_sdk_version == 0) { gb_printf_err("Windows SDK not found.\n"); return false; } if (build_context.linker_choice == Linker_Default && find_result.vs_exe_path.len == 0) { gb_printf_err("link.exe not found.\n"); return false; } if (find_result.vs_library_path.len == 0) { gb_printf_err("VS library path not found.\n"); return false; } if (find_result.windows_sdk_um_library_path.len > 0) { GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0); if (find_result.windows_sdk_bin_path.len > 0) { bc->build_paths[BuildPath_Win_SDK_Bin_Path] = path_from_string(ha, find_result.windows_sdk_bin_path); } if (find_result.windows_sdk_um_library_path.len > 0) { bc->build_paths[BuildPath_Win_SDK_UM_Lib] = path_from_string(ha, find_result.windows_sdk_um_library_path); } if (find_result.windows_sdk_ucrt_library_path.len > 0) { bc->build_paths[BuildPath_Win_SDK_UCRT_Lib] = path_from_string(ha, find_result.windows_sdk_ucrt_library_path); } if (find_result.vs_exe_path.len > 0) { bc->build_paths[BuildPath_VS_EXE] = path_from_string(ha, find_result.vs_exe_path); } if (find_result.vs_library_path.len > 0) { bc->build_paths[BuildPath_VS_LIB] = path_from_string(ha, find_result.vs_library_path); } } } } #endif // All the build targets and OSes. String output_extension; if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) { output_extension = STR_LIT("odin-doc"); } else if (is_arch_wasm()) { output_extension = STR_LIT("wasm"); } else if (build_context.build_mode == BuildMode_Executable) { // By default use no executable extension. output_extension = make_string(nullptr, 0); String const single_file_extension = str_lit(".odin"); if (selected_subtarget == Subtarget_Android) { // NOTE(bill): It's always shared! output_extension = STR_LIT("so"); } else if (build_context.metrics.os == TargetOs_windows) { output_extension = STR_LIT("exe"); } else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { // Do nothing: we don't want the .bin extension // when cross compiling } else if (path_is_directory(last_path_element(bc->build_paths[BuildPath_Main_Package].basename))) { // Add .bin extension to avoid collision // with package directory name output_extension = STR_LIT("bin"); } else if (string_ends_with(init_filename, single_file_extension) && path_is_directory(remove_extension_from_path(init_filename))) { // Add bin extension if compiling single-file package // with same output name as a directory output_extension = STR_LIT("bin"); } } else if (build_context.build_mode == BuildMode_DynamicLibrary) { // By default use a .so shared library extension. output_extension = STR_LIT("so"); if (build_context.metrics.os == TargetOs_windows) { output_extension = STR_LIT("dll"); } else if (build_context.metrics.os == TargetOs_darwin) { output_extension = STR_LIT("dylib"); } } else if (build_context.build_mode == BuildMode_StaticLibrary) { output_extension = STR_LIT("a"); if (build_context.metrics.os == TargetOs_windows) { output_extension = STR_LIT("lib"); } } else if (build_context.build_mode == BuildMode_Object) { output_extension = infer_object_extension_from_build_context(); } else if (build_context.build_mode == BuildMode_Assembly) { // By default use a .S asm extension. output_extension = STR_LIT("S"); } else if (build_context.build_mode == BuildMode_LLVM_IR) { output_extension = STR_LIT("ll"); } else { GB_PANIC("Unhandled build mode/target combination.\n"); } if (bc->out_filepath.len > 0) { bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath); if (build_context.metrics.os == TargetOs_windows) { String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]); defer (gb_free(ha, output_file.text)); if (path_is_directory(bc->build_paths[BuildPath_Output])) { gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file)); return false; } else if (bc->build_paths[BuildPath_Output].ext.len == 0) { gb_printf_err("Output path %.*s must have an appropriate extension.\n", LIT(output_file)); return false; } } } else { Path output_path; if (str_eq(init_filename, str_lit("."))) { // We must name the output file after the current directory. debugf("Output name will be created from current base name %.*s.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename)); String last_element = last_path_element(bc->build_paths[BuildPath_Main_Package].basename); if (last_element.len == 0) { gb_printf_err("The output name is created from the last path element. `%.*s` has none. Use `-out:output_name.ext` to set it.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename)); return false; } output_path.basename = copy_string(ha, bc->build_paths[BuildPath_Main_Package].basename); output_path.name = copy_string(ha, last_element); } else { // Init filename was not 'current path'. // Contruct the output name from the path elements as usual. String output_name = init_filename; // If it ends with a trailing (back)slash, strip it before continuing. while (output_name.len > 0 && (output_name[output_name.len-1] == '/' || output_name[output_name.len-1] == '\\')) { output_name.len -= 1; } // Only trim the extension if it's an Odin source file. // This lets people build folders with extensions or files beginning with dots. if (path_extension(output_name) == ".odin" && !path_is_directory(output_name)) { output_name = remove_extension_from_path(output_name); } output_name = remove_directory_from_path(output_name); output_name = copy_string(ha, string_trim_whitespace(output_name)); // This is `path_from_string` without the extension trimming. Path res = {}; if (output_name.len > 0) { String fullpath = path_to_full_path(ha, output_name); defer (gb_free(ha, fullpath.text)); res.basename = directory_from_path(fullpath); res.basename = copy_string(ha, res.basename); if (path_is_directory(fullpath)) { if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') { res.basename.len--; } } else { isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len; res.name = substring(fullpath, name_start, fullpath.len); res.name = copy_string(ha, res.name); } } output_path = res; // Note(Dragos): This is a fix for empty filenames // Turn the trailing folder into the file name if (output_path.name.len == 0) { isize len = output_path.basename.len; while (len > 1 && output_path.basename[len - 1] != '/') { len -= 1; } // We reached the slash String old_basename = output_path.basename; output_path.basename.len = len - 1; // Remove the slash output_path.name = substring(old_basename, len, old_basename.len); output_path.basename = copy_string(ha, output_path.basename); output_path.name = copy_string(ha, output_path.name); // The old basename is wrong. Delete it gb_free(ha, old_basename.text); } // Replace extension. if (output_path.ext.len > 0) { gb_free(ha, output_path.ext.text); } } output_path.ext = copy_string(ha, output_extension); bc->build_paths[BuildPath_Output] = output_path; } if (build_context.ODIN_DEBUG) { if (build_context.metrics.os == TargetOs_windows) { if (bc->pdb_filepath.len > 0) { bc->build_paths[BuildPath_Symbols] = path_from_string(ha, bc->pdb_filepath); } else { Path symbol_path; symbol_path.basename = copy_string(ha, bc->build_paths[BuildPath_Output].basename); symbol_path.name = copy_string(ha, bc->build_paths[BuildPath_Output].name); symbol_path.ext = copy_string(ha, STR_LIT("pdb")); bc->build_paths[BuildPath_Symbols] = symbol_path; } } else if (build_context.metrics.os == TargetOs_darwin) { Path symbol_path; symbol_path.basename = copy_string(ha, bc->build_paths[BuildPath_Output].basename); symbol_path.name = copy_string(ha, bc->build_paths[BuildPath_Output].name); symbol_path.ext = copy_string(ha, STR_LIT("dSYM")); bc->build_paths[BuildPath_Symbols] = symbol_path; } } // Do we have an extension? We might not if the output filename was supplied. if (bc->build_paths[BuildPath_Output].ext.len == 0) { if (build_context.metrics.os == TargetOs_windows || is_arch_wasm() || build_context.build_mode != BuildMode_Executable) { bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension); } } String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]); defer (gb_free(ha, output_file.text)); // Check if output path is a directory. if (path_is_directory(bc->build_paths[BuildPath_Output])) { gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file)); return false; } // gbFile output_file_test; // const char* output_file_name = (const char*)output_file.text; // gbFileError output_test_err = gb_file_open_mode(&output_file_test, gbFileMode_Append | gbFileMode_Rw, output_file_name); // if (output_test_err == 0) { // gb_file_close(&output_file_test); // gb_file_remove(output_file_name); // } else { // String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]); // defer (gb_free(ha, output_file.text)); // gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file)); // return false; // } if (build_context.sanitizer_flags & SanitizerFlag_Address) { switch (build_context.metrics.os) { case TargetOs_windows: case TargetOs_linux: case TargetOs_darwin: case TargetOs_freebsd: break; default: gb_printf_err("-sanitize:address is only supported on Windows, Linux, Darwin, and FreeBSD\n"); return false; } } if (build_context.sanitizer_flags & SanitizerFlag_Memory) { switch (build_context.metrics.os) { case TargetOs_linux: case TargetOs_freebsd: break; default: gb_printf_err("-sanitize:memory is only supported on Linux and FreeBSD\n"); return false; } } if (build_context.sanitizer_flags & SanitizerFlag_Thread) { switch (build_context.metrics.os) { case TargetOs_linux: case TargetOs_darwin: case TargetOs_freebsd: break; default: gb_printf_err("-sanitize:thread is only supported on Linux, Darwin, and FreeBSD\n"); return false; } } bool no_crt_checks_failed = false; if (build_context.no_crt && !build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR && !build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) { switch (build_context.metrics.os) { case TargetOs_linux: case TargetOs_darwin: case TargetOs_essence: case TargetOs_freebsd: case TargetOs_openbsd: case TargetOs_netbsd: case TargetOs_haiku: gb_printf_err("-no-crt on Unix systems requires either -default-to-nil-allocator or -default-to-panic-allocator to also be present, because the default allocator requires CRT\n"); no_crt_checks_failed = true; } } if (build_context.no_crt && !build_context.no_thread_local) { switch (build_context.metrics.os) { case TargetOs_linux: case TargetOs_darwin: case TargetOs_essence: case TargetOs_freebsd: case TargetOs_openbsd: case TargetOs_netbsd: case TargetOs_haiku: gb_printf_err("-no-crt on Unix systems requires the -no-thread-local flag to also be present, because the TLS is inaccessible without CRT\n"); no_crt_checks_failed = true; } } if (no_crt_checks_failed) { return false; } return true; }