// #define NO_ARRAY_BOUNDS_CHECK #include "common.cpp" #include "timings.cpp" #include "tokenizer.cpp" #if defined(GB_SYSTEM_WINDOWS) #pragma warning(push) #pragma warning(disable: 4505) #endif #include "big_int.cpp" #if defined(GB_SYSTEM_WINDOWS) #pragma warning(pop) #endif #include "exact_value.cpp" #include "build_settings.cpp" gb_global ThreadPool global_thread_pool; gb_internal void init_global_thread_pool(void) { isize thread_count = gb_max(build_context.thread_count, 1); isize worker_count = thread_count; // +1 thread_pool_init(&global_thread_pool, worker_count, "ThreadPoolWorker"); } gb_internal bool thread_pool_add_task(WorkerTaskProc *proc, void *data) { return thread_pool_add_task(&global_thread_pool, proc, data); } gb_internal void thread_pool_wait(void) { thread_pool_wait(&global_thread_pool); } gb_internal i64 PRINT_PEAK_USAGE(void) { if (build_context.show_more_timings) { #if defined(GB_SYSTEM_WINDOWS) PROCESS_MEMORY_COUNTERS p = {sizeof(p)}; if (GetProcessMemoryInfo(GetCurrentProcess(), &p, sizeof(p))) { gb_printf("\n"); gb_printf("Peak Memory Size: %.3f MiB\n", (cast(f64)p.PeakWorkingSetSize) / cast(f64)(1024ull * 1024ull)); return cast(i64)p.PeakWorkingSetSize; } #endif } return 0; } gb_global BlockingMutex debugf_mutex; gb_internal void debugf(char const *fmt, ...) { if (build_context.show_debug_messages) { mutex_lock(&debugf_mutex); gb_printf_err("[DEBUG] "); va_list va; va_start(va, fmt); (void)gb_printf_err_va(fmt, va); va_end(va); mutex_unlock(&debugf_mutex); } } gb_global Timings global_timings = {0}; #if defined(GB_SYSTEM_WINDOWS) #include "llvm-c/Types.h" #else #include #include #endif #include "parser.hpp" #include "checker.hpp" #include "parser.cpp" #include "checker.cpp" #include "docs.cpp" #include "cached.cpp" #include "linker.cpp" #include "bundle_command.cpp" #if defined(GB_SYSTEM_WINDOWS) && defined(ODIN_TILDE_BACKEND) #define ALLOW_TILDE 1 #else #define ALLOW_TILDE 0 #endif #if ALLOW_TILDE #include "tilde.cpp" #endif #include "llvm_backend.cpp" #include "bug_report.cpp" // NOTE(bill): 'name' is used in debugging and profiling modes gb_internal i32 system_exec_command_line_app_internal(bool exit_on_err, char const *name, char const *fmt, va_list va) { isize const cmd_cap = 64<<20; // 64 MiB should be more than enough char *cmd_line = gb_alloc_array(gb_heap_allocator(), char, cmd_cap); isize cmd_len = 0; i32 exit_code = 0; cmd_len = gb_snprintf_va(cmd_line, cmd_cap-1, fmt, va); if (build_context.print_linker_flags) { // NOTE(bill): remove the first argument (the executable) from the executable list // and then print it for the "linker flags" while (*cmd_line && gb_char_is_space(*cmd_line)) { cmd_line++; } if (*cmd_line == '\"') for (cmd_line++; *cmd_line; cmd_line++) { if (*cmd_line == '\\') { cmd_line++; if (*cmd_line == '\"') { cmd_line++; } } else if (*cmd_line == '\"') { cmd_line++; break; } } while (*cmd_line && gb_char_is_space(*cmd_line)) { cmd_line++; } fprintf(stdout, "%s\n", cmd_line); return exit_code; } #if defined(GB_SYSTEM_WINDOWS) STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)}; PROCESS_INFORMATION pi = {0}; String16 wcmd = {}; start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; start_info.wShowWindow = SW_SHOW; start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); if (build_context.show_system_calls) { gb_printf_err("[SYSTEM CALL] %s\n", name); gb_printf_err("%.*s\n\n", cast(int)(cmd_len-1), cmd_line); } wcmd = string_to_string16(permanent_allocator(), make_string(cast(u8 *)cmd_line, cmd_len-1)); if (CreateProcessW(nullptr, cast(wchar_t *)wcmd.text, nullptr, nullptr, true, 0, nullptr, nullptr, &start_info, &pi)) { WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else { // NOTE(bill): failed to create process gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line); exit_code = -1; } #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) if (build_context.show_system_calls) { gb_printf_err("[SYSTEM CALL] %s\n", name); gb_printf_err("%s\n\n", cmd_line); } exit_code = system(cmd_line); if (exit_on_err && WIFSIGNALED(exit_code)) { raise(WTERMSIG(exit_code)); } if (WIFEXITED(exit_code)) { exit_code = WEXITSTATUS(exit_code); } #endif if (exit_on_err && exit_code) { exit(exit_code); } return exit_code; } gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { va_list va; va_start(va, fmt); i32 exit_code = system_exec_command_line_app_internal(/* exit_on_err= */ false, name, fmt, va); va_end(va); return exit_code; } gb_internal void system_must_exec_command_line_app(char const *name, char const *fmt, ...) { va_list va; va_start(va, fmt); system_exec_command_line_app_internal(/* exit_on_err= */ true, name, fmt, va); va_end(va); } #if defined(GB_SYSTEM_WINDOWS) #define popen _popen #define pclose _pclose #endif gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output) { GB_ASSERT(output); u8 buffer[256]; FILE *stream; stream = popen(command, "r"); if (!stream) { return false; } defer (pclose(stream)); while (!feof(stream)) { size_t n = fread(buffer, 1, 255, stream); *output = gb_string_append_length(*output, buffer, n); if (ferror(stream)) { return false; } } if (build_context.show_system_calls) { gb_printf_err("[SYSTEM CALL OUTPUT] %s -> %s\n", command, *output); } return true; } gb_internal Array setup_args(int argc, char const **argv) { gbAllocator a = heap_allocator(); #if defined(GB_SYSTEM_WINDOWS) int wargc = 0; wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc); auto args = array_make(a, 0, wargc); for (isize i = 0; i < wargc; i++) { u16 *warg = cast(u16 *)wargv[i]; isize wlen = string16_len(warg); String16 wstr = make_string16(warg, wlen); String arg = string16_to_string(a, wstr); if (arg.len > 0) { array_add(&args, arg); } } return args; #else auto args = array_make(a, 0, argc); for (isize i = 0; i < argc; i++) { String arg = make_string_c(argv[i]); if (arg.len > 0) { array_add(&args, arg); } } return args; #endif } gb_internal void print_usage_line(i32 indent, char const *fmt, ...) { while (indent --> 0) { gb_printf("\t"); } va_list va; va_start(va, fmt); gb_printf_va(fmt, va); va_end(va); gb_printf("\n"); } gb_internal void usage(String argv0, String argv1 = {}) { if (argv1 == "run.") { print_usage_line(0, "Did you mean 'odin run .'?"); } else if (argv1 == "build.") { print_usage_line(0, "Did you mean 'odin build .'?"); } print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(argv0)); print_usage_line(0, "Usage:"); print_usage_line(1, "%.*s command [arguments]", LIT(argv0)); print_usage_line(0, "Commands:"); print_usage_line(1, "build Compiles directory of .odin files, as an executable."); print_usage_line(1, " One must contain the program's entry point, all must be in the same package."); print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable."); print_usage_line(1, "bundle Bundles a directory in a specific layout for that platform."); print_usage_line(1, "check Parses and type checks a directory of .odin files."); print_usage_line(1, "strip-semicolon Parses, type checks, and removes unneeded semicolons from the entire program."); print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package."); print_usage_line(1, "doc Generates documentation from a directory of .odin files."); print_usage_line(1, "version Prints version."); print_usage_line(1, "report Prints information useful to reporting a bug."); print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections."); print_usage_line(0, ""); print_usage_line(0, "For further details on a command, invoke command help:"); print_usage_line(1, "e.g. `odin build -help` or `odin help build`"); } enum BuildFlagKind { BuildFlag_Invalid, BuildFlag_Help, BuildFlag_SingleFile, BuildFlag_OutFile, BuildFlag_OptimizationMode, BuildFlag_ShowTimings, BuildFlag_ShowUnused, BuildFlag_ShowUnusedWithLocation, BuildFlag_ShowMoreTimings, BuildFlag_ExportTimings, BuildFlag_ExportTimingsFile, BuildFlag_ExportDependencies, BuildFlag_ExportDependenciesFile, BuildFlag_ShowSystemCalls, BuildFlag_ThreadCount, BuildFlag_KeepTempFiles, BuildFlag_Collection, BuildFlag_Define, BuildFlag_BuildMode, BuildFlag_KeepExecutable, BuildFlag_Target, BuildFlag_Subtarget, BuildFlag_Debug, BuildFlag_DisableAssert, BuildFlag_NoBoundsCheck, BuildFlag_NoTypeAssert, BuildFlag_NoDynamicLiterals, BuildFlag_DynamicLiterals, BuildFlag_NoCRT, BuildFlag_NoRPath, BuildFlag_NoEntryPoint, BuildFlag_UseLLD, BuildFlag_UseRADLink, BuildFlag_Linker, BuildFlag_UseSeparateModules, BuildFlag_UseSingleModule, BuildFlag_NoThreadedChecker, BuildFlag_ShowDebugMessages, BuildFlag_ShowDefineables, BuildFlag_ExportDefineables, BuildFlag_Vet, BuildFlag_VetShadowing, BuildFlag_VetUnused, BuildFlag_VetUnusedImports, BuildFlag_VetUnusedVariables, BuildFlag_VetUnusedProcedures, BuildFlag_VetUsingStmt, BuildFlag_VetUsingParam, BuildFlag_VetStyle, BuildFlag_VetSemicolon, BuildFlag_VetCast, BuildFlag_VetTabs, BuildFlag_VetPackages, BuildFlag_CustomAttribute, BuildFlag_IgnoreUnknownAttributes, BuildFlag_ExtraLinkerFlags, BuildFlag_ExtraAssemblerFlags, BuildFlag_Microarch, BuildFlag_TargetFeatures, BuildFlag_StrictTargetFeatures, BuildFlag_MinimumOSVersion, BuildFlag_NoThreadLocal, BuildFlag_RelocMode, BuildFlag_DisableRedZone, BuildFlag_DisallowDo, BuildFlag_DefaultToNilAllocator, BuildFlag_DefaultToPanicAllocator, BuildFlag_StrictStyle, BuildFlag_ForeignErrorProcedures, BuildFlag_NoRTTI, BuildFlag_DynamicMapCalls, BuildFlag_ObfuscateSourceCodeLocations, BuildFlag_SourceCodeLocations, BuildFlag_Compact, BuildFlag_GlobalDefinitions, BuildFlag_GoToDefinitions, BuildFlag_Short, BuildFlag_AllPackages, BuildFlag_DocFormat, BuildFlag_IgnoreWarnings, BuildFlag_WarningsAsErrors, BuildFlag_TerseErrors, BuildFlag_VerboseErrors, BuildFlag_JsonErrors, BuildFlag_ErrorPosStyle, BuildFlag_MaxErrorCount, BuildFlag_MinLinkLibs, BuildFlag_PrintLinkerFlags, BuildFlag_IntegerDivisionByZero, // internal use only BuildFlag_InternalFastISel, BuildFlag_InternalIgnoreLazy, BuildFlag_InternalIgnoreLLVMBuild, BuildFlag_InternalIgnorePanic, BuildFlag_InternalModulePerFile, BuildFlag_InternalCached, BuildFlag_InternalNoInline, BuildFlag_InternalByValue, BuildFlag_InternalWeakMonomorphization, BuildFlag_InternalLLVMVerification, BuildFlag_Tilde, BuildFlag_Sanitize, #if defined(GB_SYSTEM_WINDOWS) BuildFlag_IgnoreVsSearch, BuildFlag_ResourceFile, BuildFlag_WindowsPdbName, BuildFlag_Subsystem, #endif BuildFlag_AndroidKeystore, BuildFlag_AndroidKeystoreAlias, BuildFlag_AndroidKeystorePassword, BuildFlag_COUNT, }; enum BuildFlagParamKind { BuildFlagParam_None, BuildFlagParam_Boolean, BuildFlagParam_Integer, BuildFlagParam_Float, BuildFlagParam_String, BuildFlagParam_COUNT, }; struct BuildFlag { BuildFlagKind kind; String name; BuildFlagParamKind param_kind; u64 command_support; bool allow_multiple; }; gb_internal void add_flag(Array *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u64 command_support, bool allow_multiple=false) { BuildFlag flag = {kind, name, param_kind, command_support, allow_multiple}; array_add(build_flags, flag); } gb_internal ExactValue build_param_to_exact_value(String name, String param) { ExactValue value = {}; /* Bail out on an empty param string */ if (param.len == 0) { gb_printf_err("Invalid flag parameter for '%.*s' = '%.*s'\n", LIT(name), LIT(param)); return value; } /* Attempt to parse as bool first. */ if (str_eq_ignore_case(param, str_lit("t")) || str_eq_ignore_case(param, str_lit("true"))) { return exact_value_bool(true); } if (str_eq_ignore_case(param, str_lit("f")) || str_eq_ignore_case(param, str_lit("false"))) { return exact_value_bool(false); } /* Try to parse as an integer or float */ if (param[0] == '-' || param[0] == '+' || gb_is_between(param[0], '0', '9')) { if (string_contains_char(param, '.')) { value = exact_value_float_from_string(param); } else { value = exact_value_integer_from_string(param); } if (value.kind != ExactValue_Invalid) { return value; } } /* Treat the param as a string literal, optionally be quoted in '' to avoid being parsed as a bool, integer or float. */ value = exact_value_string(param); if (param[0] == '\'' && value.kind == ExactValue_String) { String s = value.value_string; if (s.len > 1 && s[0] == '\'' && s[s.len-1] == '\'') { value.value_string = substring(s, 1, s.len-1); } } if (value.kind != ExactValue_String) { gb_printf_err("Invalid flag parameter for '%.*s' = '%.*s'\n", LIT(name), LIT(param)); } return value; } // Writes a did-you-mean message for formerly deprecated flags. gb_internal void did_you_mean_flag(String flag) { gbAllocator a = heap_allocator(); String name = copy_string(a, flag); defer (gb_free(a, name.text)); string_to_lower(&name); if (name == "opt") { gb_printf_err("`-opt` is an unrecognized option. Did you mean `-o`?\n"); return; } gb_printf_err("Unknown flag: '%.*s'\n", LIT(flag)); } gb_internal bool parse_build_flags(Array args) { auto build_flags = array_make(heap_allocator(), 0, BuildFlag_COUNT); add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_SingleFile, str_lit("file"), BuildFlagParam_None, Command__does_build | Command__does_check); add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build | Command_test | Command_doc); add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_ExportDependencies, str_lit("export-dependencies"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ExportDependenciesFile, str_lit("export-dependencies-file"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check); add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check); add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all); add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build | Command_strip_semicolon); add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true); add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message add_flag(&build_flags, BuildFlag_KeepExecutable, str_lit("keep-executable"), BuildFlagParam_None, Command__does_build | Command_test); add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_Subtarget, str_lit("subtarget"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoTypeAssert, str_lit("no-type-assert"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoThreadLocal, str_lit("no-thread-local"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DynamicLiterals, str_lit("dynamic-literals"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_NoRPath, str_lit("no-rpath"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test); add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_UseRADLink, str_lit("radlink"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_Linker, str_lit("linker"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_UseSingleModule, str_lit("use-single-module"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_ShowDefineables, str_lit("show-defineables"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ExportDefineables, str_lit("export-defineables"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUnusedVariables, str_lit("vet-unused-variables"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUnusedProcedures, str_lit("vet-unused-procedures"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUnusedImports, str_lit("vet-unused-imports"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetShadowing, str_lit("vet-shadowing"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUsingStmt, str_lit("vet-using-stmt"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUsingParam, str_lit("vet-using-param"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetStyle, str_lit("vet-style"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetSemicolon, str_lit("vet-semicolon"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetCast, str_lit("vet-cast"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetTabs, str_lit("vet-tabs"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetPackages, str_lit("vet-packages"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_CustomAttribute, str_lit("custom-attribute"), BuildFlagParam_String, Command__does_check, true); add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_TargetFeatures, str_lit("target-features"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_StrictTargetFeatures, str_lit("strict-target-features"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_MinimumOSVersion, str_lit("minimum-os-version"), BuildFlagParam_String, Command__does_build | Command_bundle_android); add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DefaultToPanicAllocator, str_lit("default-to-panic-allocator"),BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoRTTI, str_lit("no-rtti"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DynamicMapCalls, str_lit("dynamic-map-calls"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ObfuscateSourceCodeLocations, str_lit("obfuscate-source-code-locations"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_SourceCodeLocations, str_lit("source-code-locations"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc); add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc | Command_test | Command_build); add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc); add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_TerseErrors, str_lit("terse-errors"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_JsonErrors, str_lit("json-errors"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_ErrorPosStyle, str_lit("error-pos-style"), BuildFlagParam_String, Command_all); add_flag(&build_flags, BuildFlag_MaxErrorCount, str_lit("max-error-count"), BuildFlagParam_Integer, Command_all); add_flag(&build_flags, BuildFlag_MinLinkLibs, str_lit("min-link-libs"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_PrintLinkerFlags, str_lit("print-linker-flags"), BuildFlagParam_None, Command_build); add_flag(&build_flags, BuildFlag_IntegerDivisionByZero, str_lit("integer-division-by-zero"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnorePanic, str_lit("internal-ignore-panic"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalModulePerFile, str_lit("internal-module-per-file"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalCached, str_lit("internal-cached"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalNoInline, str_lit("internal-no-inline"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalWeakMonomorphization, str_lit("internal-weak-monomorphization"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalLLVMVerification, str_lit("internal-ignore-llvm-verification"), BuildFlagParam_None, Command_all); #if ALLOW_TILDE add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build); #endif add_flag(&build_flags, BuildFlag_Sanitize, str_lit("sanitize"), BuildFlagParam_String, Command__does_build, true); #if defined(GB_SYSTEM_WINDOWS) add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build); #endif add_flag(&build_flags, BuildFlag_AndroidKeystore, str_lit("android-keystore"), BuildFlagParam_String, Command_bundle_android); add_flag(&build_flags, BuildFlag_AndroidKeystoreAlias, str_lit("android-keystore-alias"), BuildFlagParam_String, Command_bundle_android); add_flag(&build_flags, BuildFlag_AndroidKeystorePassword, str_lit("android-keystore-password"), BuildFlagParam_String, Command_bundle_android); Array flag_args = {}; if (build_context.command_kind == Command_bundle_android) { GB_ASSERT(args.count >= 4); flag_args = array_slice(args, 4, args.count); } else { GB_ASSERT(args.count >= 3); flag_args = array_slice(args, 3, args.count); } bool set_flags[BuildFlag_COUNT] = {}; bool bad_flags = false; for (String flag : flag_args) { if (flag[0] != '-') { gb_printf_err("Invalid flag: %.*s\n", LIT(flag)); continue; } if (string_starts_with(flag, str_lit("--"))) { flag = substring(flag, 1, flag.len); } String name = substring(flag, 1, flag.len); isize end = 0; bool have_equals = false; for (; end < name.len; end++) { if (name[end] == ':') break; if (name[end] == '=') { have_equals = true; break; } } name = substring(name, 0, end); String param = {}; if (end < flag.len-1) param = substring(flag, 2+end, flag.len); bool is_supported = true; bool found = false; BuildFlag found_bf = {}; for (BuildFlag const &bf : build_flags) { if (bf.name == name) { found = true; found_bf = bf; if ((bf.command_support & build_context.command_kind) == 0) { is_supported = false; break; } if (set_flags[bf.kind]) { gb_printf_err("Previous flag set: '%.*s'\n", LIT(name)); bad_flags = true; } else { ExactValue value = {}; bool ok = false; if (bf.param_kind == BuildFlagParam_None) { if (param.len == 0) { ok = true; } else { gb_printf_err("Flag '%.*s' was not expecting a parameter '%.*s'\n", LIT(name), LIT(param)); bad_flags = true; } } else if (param.len == 0) { gb_printf_err("Flag missing for '%.*s'\n", LIT(name)); bad_flags = true; } else { ok = true; switch (bf.param_kind) { default: { ok = false; } break; case BuildFlagParam_Boolean: { if (str_eq_ignore_case(param, str_lit("t")) || str_eq_ignore_case(param, str_lit("true")) || param == "1") { value = exact_value_bool(true); } else if (str_eq_ignore_case(param, str_lit("f")) || str_eq_ignore_case(param, str_lit("false")) || param == "0") { value = exact_value_bool(false); } else { gb_printf_err("Invalid flag parameter for '%.*s' : '%.*s'\n", LIT(name), LIT(param)); } } break; case BuildFlagParam_Integer: { value = exact_value_integer_from_string(param); } break; case BuildFlagParam_Float: { value = exact_value_float_from_string(param); } break; case BuildFlagParam_String: { value = exact_value_string(param); if (value.kind == ExactValue_String) { String s = value.value_string; if (s.len > 1 && s[0] == '"' && s[s.len-1] == '"') { value.value_string = substring(s, 1, s.len-1); } } break; } } } if (ok) { switch (bf.param_kind) { case BuildFlagParam_None: if (value.kind != ExactValue_Invalid) { gb_printf_err("%.*s expected no value, got %.*s\n", LIT(name), LIT(param)); bad_flags = true; ok = false; } break; case BuildFlagParam_Boolean: if (value.kind != ExactValue_Bool) { gb_printf_err("%.*s expected a boolean, got %.*s\n", LIT(name), LIT(param)); bad_flags = true; ok = false; } break; case BuildFlagParam_Integer: if (value.kind != ExactValue_Integer) { gb_printf_err("%.*s expected an integer, got %.*s\n", LIT(name), LIT(param)); bad_flags = true; ok = false; } break; case BuildFlagParam_Float: if (value.kind != ExactValue_Float) { gb_printf_err("%.*s expected a floating pointer number, got %.*s\n", LIT(name), LIT(param)); bad_flags = true; ok = false; } break; case BuildFlagParam_String: if (value.kind != ExactValue_String) { gb_printf_err("%.*s expected a string, got %.*s\n", LIT(name), LIT(param)); bad_flags = true; ok = false; } break; } if (ok) switch (bf.kind) { case BuildFlag_Help: build_context.show_help = true; break; case BuildFlag_OutFile: { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; path = string_trim_whitespace(path); if (is_build_flag_path_valid(path)) { build_context.out_filepath = path_to_full_path(heap_allocator(), path); } else { gb_printf_err("Invalid -out path, got %.*s\n", LIT(path)); bad_flags = true; } break; } case BuildFlag_OptimizationMode: { GB_ASSERT(value.kind == ExactValue_String); if (value.value_string == "none") { build_context.custom_optimization_level = true; build_context.optimization_level = -1; } else if (value.value_string == "minimal") { build_context.custom_optimization_level = true; build_context.optimization_level = 0; } else if (value.value_string == "size") { build_context.custom_optimization_level = true; build_context.optimization_level = 1; } else if (value.value_string == "speed") { build_context.custom_optimization_level = true; build_context.optimization_level = 2; } else if (value.value_string == "aggressive" && LB_USE_NEW_PASS_SYSTEM) { build_context.custom_optimization_level = true; build_context.optimization_level = 3; } else { gb_printf_err("Invalid optimization mode for -o:, got %.*s\n", LIT(value.value_string)); gb_printf_err("Valid optimization modes:\n"); gb_printf_err("\tminimal\n"); gb_printf_err("\tsize\n"); gb_printf_err("\tspeed\n"); if (LB_USE_NEW_PASS_SYSTEM) { gb_printf_err("\taggressive\n"); } gb_printf_err("\tnone (useful for -debug builds)\n"); bad_flags = true; } break; } case BuildFlag_ShowTimings: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_timings = true; break; } case BuildFlag_ShowUnused: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_unused = true; break; } case BuildFlag_ShowUnusedWithLocation: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_unused = true; build_context.show_unused_with_location = true; break; } case BuildFlag_ShowMoreTimings: GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_timings = true; build_context.show_more_timings = true; break; case BuildFlag_ExportTimings: { GB_ASSERT(value.kind == ExactValue_String); /* NOTE(Jeroen): `build_context.export_timings_format == 0` means the option wasn't used. */ if (value.value_string == "json") { build_context.export_timings_format = TimingsExportJson; } else if (value.value_string == "csv") { build_context.export_timings_format = TimingsExportCSV; } else { gb_printf_err("Invalid export format for -export-timings:, got %.*s\n", LIT(value.value_string)); gb_printf_err("Valid export formats:\n"); gb_printf_err("\tjson\n"); gb_printf_err("\tcsv\n"); bad_flags = true; } break; } case BuildFlag_ExportTimingsFile: { GB_ASSERT(value.kind == ExactValue_String); String export_path = string_trim_whitespace(value.value_string); if (is_build_flag_path_valid(export_path)) { build_context.export_timings_file = path_to_full_path(heap_allocator(), export_path); } else { gb_printf_err("Invalid -export-timings-file path, got %.*s\n", LIT(export_path)); bad_flags = true; } break; } case BuildFlag_ExportDependencies: { GB_ASSERT(value.kind == ExactValue_String); if (value.value_string == "make") { build_context.export_dependencies_format = DependenciesExportMake; } else if (value.value_string == "json") { build_context.export_dependencies_format = DependenciesExportJson; } else { gb_printf_err("Invalid export format for -export-dependencies:, got %.*s\n", LIT(value.value_string)); gb_printf_err("Valid export formats:\n"); gb_printf_err("\tmake\n"); gb_printf_err("\tjson\n"); bad_flags = true; } break; } case BuildFlag_ExportDependenciesFile: { GB_ASSERT(value.kind == ExactValue_String); String export_path = string_trim_whitespace(value.value_string); if (is_build_flag_path_valid(export_path)) { build_context.export_dependencies_file = path_to_full_path(heap_allocator(), export_path); } else { gb_printf_err("Invalid -export-dependencies path, got %.*s\n", LIT(export_path)); bad_flags = true; } break; } case BuildFlag_ShowDefineables: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_defineables = true; break; } case BuildFlag_ExportDefineables: { GB_ASSERT(value.kind == ExactValue_String); String export_path = string_trim_whitespace(value.value_string); if (is_build_flag_path_valid(export_path)) { build_context.export_defineables_file = path_to_full_path(heap_allocator(), export_path); } else { gb_printf_err("Invalid -export-defineables path, got %.*s\n", LIT(export_path)); bad_flags = true; } break; } case BuildFlag_ShowSystemCalls: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_system_calls = true; break; } case BuildFlag_ThreadCount: { GB_ASSERT(value.kind == ExactValue_Integer); isize count = cast(isize)big_int_to_i64(&value.value_integer); if (count <= 0) { gb_printf_err("%.*s expected a positive non-zero number, got %.*s\n", LIT(name), LIT(param)); build_context.thread_count = 1; } else { build_context.thread_count = count; } break; } case BuildFlag_KeepTempFiles: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.keep_temp_files = true; break; } case BuildFlag_Collection: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; isize eq_pos = -1; for (isize i = 0; i < str.len; i++) { if (str[i] == '=') { eq_pos = i; break; } } if (eq_pos < 0) { gb_printf_err("Expected 'name=path', got '%.*s'\n", LIT(param)); bad_flags = true; break; } String name = substring(str, 0, eq_pos); String path = substring(str, eq_pos+1, str.len); if (name.len == 0 || path.len == 0) { gb_printf_err("Expected 'name=path', got '%.*s'\n", LIT(param)); bad_flags = true; break; } if (!string_is_valid_identifier(name)) { gb_printf_err("Library collection name '%.*s' must be a valid identifier\n", LIT(name)); bad_flags = true; break; } if (name == "_") { gb_printf_err("Library collection name cannot be an underscore\n"); bad_flags = true; break; } if (name == "system") { gb_printf_err("Library collection name 'system' is reserved\n"); bad_flags = true; break; } String prev_path = {}; bool found = find_library_collection_path(name, &prev_path); if (found) { gb_printf_err("Library collection '%.*s' already exists with path '%.*s'\n", LIT(name), LIT(prev_path)); bad_flags = true; break; } gbAllocator a = heap_allocator(); bool path_ok = false; String fullpath = path_to_fullpath(a, path, &path_ok); if (!path_ok || !path_is_directory(fullpath)) { gb_printf_err("Library collection '%.*s' path must be a directory, got '%.*s'\n", LIT(name), LIT(path_ok ? fullpath : path)); gb_free(a, fullpath.text); bad_flags = true; break; } add_library_collection(name, path); // NOTE(bill): Allow for multiple library collections continue; } case BuildFlag_Define: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; isize eq_pos = -1; for (isize i = 0; i < str.len; i++) { if (str[i] == '=') { eq_pos = i; break; } } if (eq_pos < 0) { gb_printf_err("Expected 'name=value', got '%.*s'\n", LIT(param)); bad_flags = true; break; } String name = substring(str, 0, eq_pos); String value = substring(str, eq_pos+1, str.len); if (name.len == 0 || value.len == 0) { gb_printf_err("Expected 'name=value', got '%.*s'\n", LIT(param)); bad_flags = true; break; } if (!string_is_valid_identifier(name)) { gb_printf_err("Defined constant name '%.*s' must be a valid identifier\n", LIT(name)); bad_flags = true; break; } if (name == "_") { gb_printf_err("Defined constant name cannot be an underscore\n"); bad_flags = true; break; } char const *key = string_intern(name); if (map_get(&build_context.defined_values, key) != nullptr) { gb_printf_err("Defined constant '%.*s' already exists\n", LIT(name)); bad_flags = true; break; } ExactValue v = build_param_to_exact_value(name, value); if (v.kind != ExactValue_Invalid) { map_set(&build_context.defined_values, key, v); } else { gb_printf_err("Invalid define constant value: '%.*s'. Define constants must be a valid Odin literal.\n", LIT(value)); bad_flags = true; } break; } case BuildFlag_Target: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; bool found = false; for (isize i = 0; i < gb_count_of(named_targets); i++) { if (str_eq_ignore_case(str, named_targets[i].name)) { found = true; selected_target_metrics = named_targets + i; break; } } if (!found) { if (str != "?") { struct DistanceAndTargetIndex { isize distance; isize target_index; }; DistanceAndTargetIndex distances[gb_count_of(named_targets)] = {}; for (isize i = 0; i < gb_count_of(named_targets); i++) { distances[i].target_index = i; distances[i].distance = levenstein_distance_case_insensitive(str, named_targets[i].name); } gb_sort_array(distances, gb_count_of(distances), gb_isize_cmp(gb_offset_of(DistanceAndTargetIndex, distance))); gb_printf_err("Unknown target '%.*s'\n", LIT(str)); if (distances[0].distance <= MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) { gb_printf_err("Did you mean:\n"); for (isize i = 0; i < gb_count_of(named_targets); i++) { if (distances[i].distance > MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) { break; } gb_printf_err("\t%.*s\n", LIT(named_targets[distances[i].target_index].name)); } } } gb_printf_err("All supported targets:\n"); for (isize i = 0; i < gb_count_of(named_targets); i++) { gb_printf_err("\t%.*s\n", LIT(named_targets[i].name)); } bad_flags = true; } break; } case BuildFlag_Subtarget: if (selected_target_metrics == nullptr) { gb_printf_err("-target must be set before -subtarget is used\n"); bad_flags = true; } else { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; bool found = false; if (selected_target_metrics->metrics->os != TargetOs_darwin && selected_target_metrics->metrics->os != TargetOs_linux ) { gb_printf_err("-subtarget can only be used with darwin and linux based targets at the moment\n"); bad_flags = true; break; } for (u32 i = 1; i < Subtarget_COUNT; i++) { String name = subtarget_strings[i]; if (str_eq_ignore_case(str, name)) { selected_subtarget = cast(Subtarget)i; found = true; break; } } if (!found) { gb_printf_err("Unknown subtarget '%.*s'\n", LIT(str)); gb_printf_err("All supported subtargets:\n"); for (u32 i = 1; i < Subtarget_COUNT; i++) { String name = subtarget_strings[i]; gb_printf_err("\t%.*s\n", LIT(name)); } bad_flags = true; } } break; case BuildFlag_BuildMode: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; if (build_context.command != "build") { gb_printf_err("'build-mode' can only be used with the 'build' command\n"); bad_flags = true; break; } if (str == "dll" || str == "shared" || str == "dynamic") { build_context.build_mode = BuildMode_DynamicLibrary; } else if (str == "obj" || str == "object") { build_context.build_mode = BuildMode_Object; } else if (str == "static" || str == "lib") { build_context.build_mode = BuildMode_StaticLibrary; } else if (str == "exe") { build_context.build_mode = BuildMode_Executable; } else if (str == "asm" || str == "assembly" || str == "assembler") { build_context.build_mode = BuildMode_Assembly; } else if (str == "llvm" || str == "llvm-ir") { build_context.build_mode = BuildMode_LLVM_IR; } else if (str == "test") { build_context.build_mode = BuildMode_Executable; build_context.command_kind = Command_test; } else { gb_printf_err("Unknown build mode '%.*s'\n", LIT(str)); gb_printf_err("Valid build modes:\n"); gb_printf_err("\tdll, shared, dynamic\n"); gb_printf_err("\tlib, static\n"); gb_printf_err("\tobj, object\n"); gb_printf_err("\texe\n"); gb_printf_err("\tasm, assembly, assembler\n"); gb_printf_err("\tllvm, llvm-ir\n"); gb_printf_err("\ttest\n"); bad_flags = true; break; } break; } case BuildFlag_KeepExecutable: build_context.keep_executable = true; break; case BuildFlag_Debug: build_context.ODIN_DEBUG = true; break; case BuildFlag_DisableAssert: build_context.ODIN_DISABLE_ASSERT = true; break; case BuildFlag_NoBoundsCheck: build_context.no_bounds_check = true; break; case BuildFlag_NoTypeAssert: build_context.no_type_assert = true; break; case BuildFlag_NoDynamicLiterals: gb_printf_err("Warning: Use of -no-dynamic-literals is now redundant\n"); break; case BuildFlag_DynamicLiterals: build_context.dynamic_literals = true; break; case BuildFlag_NoCRT: build_context.no_crt = true; break; case BuildFlag_NoRPath: build_context.no_rpath = true; break; case BuildFlag_NoEntryPoint: build_context.no_entry_point = true; break; case BuildFlag_NoThreadLocal: build_context.no_thread_local = true; break; case BuildFlag_UseLLD: gb_printf_err("Warning: Use of -lld has been deprecated in favour of -linker:lld\n"); build_context.linker_choice = Linker_lld; break; case BuildFlag_UseRADLink: gb_printf_err("Warning: Use of -lld has been deprecated in favour of -linker:radlink\n"); build_context.linker_choice = Linker_radlink; break; case BuildFlag_Linker: { GB_ASSERT(value.kind == ExactValue_String); LinkerChoice linker_choice = Linker_Invalid; for (i32 i = 0; i < Linker_COUNT; i++) { if (linker_choices[i] == value.value_string) { linker_choice = cast(LinkerChoice)i; break; } } if (linker_choice == Linker_Invalid) { gb_printf_err("Invalid option for -linker:. Expected one of the following\n"); for (i32 i = 0; i < Linker_COUNT; i++) { gb_printf_err("\t%.*s\n", LIT(linker_choices[i])); } bad_flags = true; } else { build_context.linker_choice = linker_choice; } } break; case BuildFlag_UseSeparateModules: if (build_context.use_single_module) { gb_printf_err("-use-separate-modules cannot be used with -use-single-module\n"); bad_flags = true; } build_context.use_separate_modules = true; break; case BuildFlag_UseSingleModule: if (build_context.use_separate_modules) { gb_printf_err("-use-single-module cannot be used with -use-separate-modules\n"); bad_flags = true; } build_context.use_single_module = true; break; case BuildFlag_NoThreadedChecker: build_context.no_threaded_checker = true; break; case BuildFlag_ShowDebugMessages: build_context.show_debug_messages = true; break; case BuildFlag_Vet: build_context.vet_flags |= VetFlag_All; break; case BuildFlag_VetUnusedVariables: build_context.vet_flags |= VetFlag_UnusedVariables; break; case BuildFlag_VetUnusedImports: build_context.vet_flags |= VetFlag_UnusedImports; break; case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break; case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break; case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; break; case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break; case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break; case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break; case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break; case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break; case BuildFlag_VetUnusedProcedures: build_context.vet_flags |= VetFlag_UnusedProcedures; if (!set_flags[BuildFlag_VetPackages]) { gb_printf_err("-%.*s must be used with -vet-packages\n", LIT(name)); bad_flags = true; } break; case BuildFlag_VetPackages: { GB_ASSERT(value.kind == ExactValue_String); String val = value.value_string; String_Iterator it = {val, 0}; for (;;) { String pkg = string_split_iterator(&it, ','); if (pkg.len == 0) { break; } pkg = string_trim_whitespace(pkg); if (!string_is_valid_identifier(pkg)) { gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(pkg)); bad_flags = true; continue; } string_set_add(&build_context.vet_packages, pkg); } } break; case BuildFlag_CustomAttribute: { GB_ASSERT(value.kind == ExactValue_String); String val = value.value_string; String_Iterator it = {val, 0}; for (;;) { String attr = string_split_iterator(&it, ','); if (attr.len == 0) { break; } attr = string_trim_whitespace(attr); if (!string_is_valid_identifier(attr)) { gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(attr)); bad_flags = true; continue; } string_set_add(&build_context.custom_attributes, attr); } } break; case BuildFlag_IgnoreUnknownAttributes: build_context.ignore_unknown_attributes = true; break; case BuildFlag_ExtraLinkerFlags: GB_ASSERT(value.kind == ExactValue_String); build_context.extra_linker_flags = value.value_string; break; case BuildFlag_ExtraAssemblerFlags: GB_ASSERT(value.kind == ExactValue_String); build_context.extra_assembler_flags = value.value_string; break; case BuildFlag_Microarch: { GB_ASSERT(value.kind == ExactValue_String); build_context.microarch = value.value_string; string_to_lower(&build_context.microarch); break; } case BuildFlag_TargetFeatures: { GB_ASSERT(value.kind == ExactValue_String); build_context.target_features_string = value.value_string; string_to_lower(&build_context.target_features_string); break; } case BuildFlag_StrictTargetFeatures: build_context.strict_target_features = true; break; case BuildFlag_MinimumOSVersion: { GB_ASSERT(value.kind == ExactValue_String); build_context.minimum_os_version_string = value.value_string; build_context.minimum_os_version_string_given = true; break; } case BuildFlag_RelocMode: { GB_ASSERT(value.kind == ExactValue_String); String v = value.value_string; if (v == "default") { build_context.reloc_mode = RelocMode_Default; } else if (v == "static") { build_context.reloc_mode = RelocMode_Static; } else if (v == "pic") { build_context.reloc_mode = RelocMode_PIC; } else if (v == "dynamic-no-pic") { build_context.reloc_mode = RelocMode_DynamicNoPIC; } else { gb_printf_err("-reloc-mode flag expected one of the following\n"); gb_printf_err("\tdefault\n"); gb_printf_err("\tstatic\n"); gb_printf_err("\tpic\n"); gb_printf_err("\tdynamic-no-pic\n"); bad_flags = true; } break; } case BuildFlag_DisableRedZone: build_context.disable_red_zone = true; break; case BuildFlag_DisallowDo: build_context.disallow_do = true; break; case BuildFlag_NoRTTI: if (name == "disallow-rtti") { gb_printf_err("'-disallow-rtti' has been replaced with '-no-rtti'\n"); bad_flags = true; } build_context.no_rtti = true; break; case BuildFlag_DynamicMapCalls: build_context.dynamic_map_calls = true; break; case BuildFlag_ObfuscateSourceCodeLocations: gb_printf_err("'-obfuscate-source-code-locations' is now deprecated in favor of '-source-code-locations:obfuscated'\n"); build_context.source_code_location_info = SourceCodeLocationInfo_Obfuscated; break; case BuildFlag_SourceCodeLocations: if (str_eq_ignore_case(value.value_string, str_lit("normal"))) { build_context.source_code_location_info = SourceCodeLocationInfo_Normal; } else if (str_eq_ignore_case(value.value_string, str_lit("obfuscated"))) { build_context.source_code_location_info = SourceCodeLocationInfo_Obfuscated; } else if (str_eq_ignore_case(value.value_string, str_lit("filename"))) { build_context.source_code_location_info = SourceCodeLocationInfo_Filename; } else if (str_eq_ignore_case(value.value_string, str_lit("none"))) { build_context.source_code_location_info = SourceCodeLocationInfo_None; } else { gb_printf_err("-source-code-locations: options are 'normal', 'obfuscated', 'filename', and 'none'\n"); bad_flags = true; } break; case BuildFlag_DefaultToNilAllocator: if (build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) { gb_printf_err("'-default-to-panic-allocator' cannot be used with '-default-to-nil-allocator'\n"); bad_flags = true; } build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true; break; case BuildFlag_DefaultToPanicAllocator: if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) { gb_printf_err("'-default-to-nil-allocator' cannot be used with '-default-to-panic-allocator'\n"); bad_flags = true; } build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR = true; break; case BuildFlag_ForeignErrorProcedures: build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true; break; case BuildFlag_StrictStyle: build_context.strict_style = true; break; case BuildFlag_Short: build_context.cmd_doc_flags |= CmdDocFlag_Short; break; case BuildFlag_AllPackages: build_context.cmd_doc_flags |= CmdDocFlag_AllPackages; build_context.test_all_packages = true; break; case BuildFlag_DocFormat: build_context.cmd_doc_flags |= CmdDocFlag_DocFormat; break; case BuildFlag_IgnoreWarnings: { if (build_context.warnings_as_errors) { gb_printf_err("-ignore-warnings cannot be used with -warnings-as-errors\n"); bad_flags = true; } else { build_context.ignore_warnings = true; } break; } case BuildFlag_WarningsAsErrors: { if (build_context.ignore_warnings) { gb_printf_err("-warnings-as-errors cannot be used with -ignore-warnings\n"); bad_flags = true; } else { build_context.warnings_as_errors = true; } break; } case BuildFlag_TerseErrors: build_context.hide_error_line = true; build_context.terse_errors = true; break; case BuildFlag_VerboseErrors: gb_printf_err("-verbose-errors is now the default, -terse-errors can disable it\n"); build_context.hide_error_line = false; build_context.terse_errors = false; break; case BuildFlag_JsonErrors: build_context.json_errors = true; break; case BuildFlag_ErrorPosStyle: GB_ASSERT(value.kind == ExactValue_String); if (str_eq_ignore_case(value.value_string, str_lit("odin")) || str_eq_ignore_case(value.value_string, str_lit("default"))) { build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Default; } else if (str_eq_ignore_case(value.value_string, str_lit("unix"))) { build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Unix; } else { gb_printf_err("-error-pos-style options are 'unix', 'odin', and 'default' (odin)\n"); bad_flags = true; } break; case BuildFlag_MaxErrorCount: { i64 count = big_int_to_i64(&value.value_integer); if (count <= 0) { gb_printf_err("-%.*s must be greater than 0", LIT(bf.name)); bad_flags = true; } else { build_context.max_error_count = cast(isize)count; } break; } case BuildFlag_MinLinkLibs: build_context.min_link_libs = true; break; case BuildFlag_PrintLinkerFlags: build_context.print_linker_flags = true; break; case BuildFlag_IntegerDivisionByZero: GB_ASSERT(value.kind == ExactValue_String); if (str_eq_ignore_case(value.value_string, "trap")) { build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Trap; } else if (str_eq_ignore_case(value.value_string, "zero")) { build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Zero; } else if (str_eq_ignore_case(value.value_string, "self")) { build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Self; }else { gb_printf_err("-integer-division-by-zero options are 'trap', 'zero', and 'self'.\n"); bad_flags = true; } break; case BuildFlag_InternalFastISel: build_context.fast_isel = true; break; case BuildFlag_InternalIgnoreLazy: build_context.ignore_lazy = true; break; case BuildFlag_InternalIgnoreLLVMBuild: build_context.ignore_llvm_build = true; break; case BuildFlag_InternalIgnorePanic: build_context.ignore_panic = true; break; case BuildFlag_InternalModulePerFile: build_context.module_per_file = true; build_context.use_separate_modules = true; break; case BuildFlag_InternalCached: build_context.cached = true; build_context.use_separate_modules = true; break; case BuildFlag_InternalNoInline: build_context.internal_no_inline = true; break; case BuildFlag_InternalByValue: build_context.internal_by_value = true; break; case BuildFlag_InternalWeakMonomorphization: build_context.internal_weak_monomorphization = true; break; case BuildFlag_InternalLLVMVerification: build_context.internal_ignore_llvm_verification = true; break; case BuildFlag_Tilde: build_context.tilde_backend = true; break; case BuildFlag_Sanitize: GB_ASSERT(value.kind == ExactValue_String); if (build_context.sanitizer_flags != 0) { gb_printf_err("-sanitize: may only be used once\n"); bad_flags = true; } if (str_eq_ignore_case(value.value_string, str_lit("address"))) { build_context.sanitizer_flags |= SanitizerFlag_Address; } else if (str_eq_ignore_case(value.value_string, str_lit("memory"))) { build_context.sanitizer_flags |= SanitizerFlag_Memory; } else if (str_eq_ignore_case(value.value_string, str_lit("thread"))) { build_context.sanitizer_flags |= SanitizerFlag_Thread; } else { gb_printf_err("-sanitize: options are 'address', 'memory', and 'thread'\n"); bad_flags = true; } break; #if defined(GB_SYSTEM_WINDOWS) case BuildFlag_IgnoreVsSearch: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.ignore_microsoft_magic = true; break; } case BuildFlag_ResourceFile: { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; path = string_trim_whitespace(path); if (is_build_flag_path_valid(path)) { bool is_resource = string_ends_with(path, str_lit(".rc")) || string_ends_with(path, str_lit(".res")); if(!is_resource) { gb_printf_err("Invalid -resource path %.*s, missing .rc or .res file\n", LIT(path)); bad_flags = true; break; } else if (!gb_file_exists((const char *)path.text)) { gb_printf_err("Invalid -resource path %.*s, file does not exist.\n", LIT(path)); bad_flags = true; break; } build_context.resource_filepath = path; build_context.has_resource = true; } else { gb_printf_err("Invalid -resource path, got %.*s\n", LIT(path)); bad_flags = true; } break; } case BuildFlag_WindowsPdbName: { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; path = string_trim_whitespace(path); if (is_build_flag_path_valid(path)) { if (path_is_directory(path)) { gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path)); bad_flags = true; break; } // #if defined(GB_SYSTEM_WINDOWS) // String ext = path_extension(path); // if (ext != ".pdb") { // path = substring(path, 0, string_extension_position(path)); // } // #endif build_context.pdb_filepath = path; } else { gb_printf_err("Invalid -pdb-name path, got %.*s\n", LIT(path)); bad_flags = true; } break; } case BuildFlag_Subsystem: { // TODO(Jeroen): Parse optional "[,major[.minor]]" GB_ASSERT(value.kind == ExactValue_String); String subsystem = value.value_string; bool subsystem_found = false; for (int i = 1; i < Windows_Subsystem_COUNT; i++) { if (str_eq_ignore_case(subsystem, windows_subsystem_names[i])) { build_context.ODIN_WINDOWS_SUBSYSTEM = Windows_Subsystem(i); subsystem_found = true; break; } } // WINDOW is a hidden alias for WINDOWS. Check it. String subsystem_windows_alias = str_lit("WINDOW"); if (!subsystem_found && str_eq_ignore_case(subsystem, subsystem_windows_alias)) { build_context.ODIN_WINDOWS_SUBSYSTEM = Windows_Subsystem_WINDOWS; subsystem_found = true; break; } if (!subsystem_found) { gb_printf_err("Invalid -subsystem string, got %.*s. Expected one of:\n", LIT(subsystem)); gb_printf_err("\t"); for (int i = 1; i < Windows_Subsystem_COUNT; i++) { if (i > 1) { gb_printf_err(", "); } gb_printf_err("%.*s", LIT(windows_subsystem_names[i])); if (i == Windows_Subsystem_CONSOLE) { gb_printf_err(" (default)"); } if (i == Windows_Subsystem_WINDOWS) { gb_printf_err(" (or WINDOW)"); } } gb_printf_err("\n"); bad_flags = true; } break; } #endif case BuildFlag_AndroidKeystore: GB_ASSERT(value.kind == ExactValue_String); build_context.android_keystore = value.value_string; break; case BuildFlag_AndroidKeystoreAlias: GB_ASSERT(value.kind == ExactValue_String); build_context.android_keystore_alias = value.value_string; break; case BuildFlag_AndroidKeystorePassword: GB_ASSERT(value.kind == ExactValue_String); build_context.android_keystore_password = value.value_string; break; } } if (!bf.allow_multiple) { set_flags[bf.kind] = ok; } } break; } } if (found && !is_supported) { gb_printf_err("Unknown flag for 'odin %.*s': '%.*s'\n", LIT(build_context.command), LIT(name)); gb_printf_err("'%.*s' is supported with the following commands:\n", LIT(name)); gb_printf_err("\t"); i32 count = 0; for (u64 i = 0; i < 64; i++) { if (found_bf.command_support & (1ull< 0) { gb_printf_err(", "); } gb_printf_err("%s", odin_command_strings[i]); count += 1; } } gb_printf_err("\n"); bad_flags = true; } else if (!found) { did_you_mean_flag(name); bad_flags = true; } } if ((!(build_context.export_timings_format == TimingsExportUnspecified)) && (build_context.export_timings_file.len == 0)) { gb_printf_err("`-export-timings:` requires `-export-timings-file:` to be specified as well\n"); bad_flags = true; } else if ((build_context.export_timings_format == TimingsExportUnspecified) && (build_context.export_timings_file.len > 0)) { gb_printf_err("`-export-timings-file:` requires `-export-timings:` to be specified as well\n"); bad_flags = true; } if (build_context.export_timings_format && !(build_context.show_timings || build_context.show_more_timings)) { gb_printf_err("`-export-timings:` requires `-show-timings` or `-show-more-timings` to be present\n"); bad_flags = true; } if (build_context.export_dependencies_format != DependenciesExportUnspecified && build_context.print_linker_flags) { gb_printf_err("-export-dependencies cannot be used with -print-linker-flags\n"); bad_flags = true; } else if (build_context.show_timings && build_context.print_linker_flags) { gb_printf_err("-show-timings/-show-more-timings cannot be used with -print-linker-flags\n"); bad_flags = true; } if ((build_context.command_kind & (Command_doc | Command_test)) == 0 && build_context.test_all_packages) { gb_printf_err("`-test-all-packages` can only be used with `odin build -build-mode:test`, `odin test`, or `odin doc`.\n"); bad_flags = true; } return !bad_flags; } gb_internal void timings_export_all(Timings *t, Checker *c, bool timings_are_finalized = false) { GB_ASSERT((!(build_context.export_timings_format == TimingsExportUnspecified) && build_context.export_timings_file.len > 0)); /* NOTE(Jeroen): Whether we call `timings_print_all()`, then `timings_export_all()`, the other way around, or just one of them, we only need to stop the clock once. */ if (!timings_are_finalized) { timings__stop_current_section(t); t->total.finish = time_stamp_time_now(); } TimingUnit unit = TimingUnit_Millisecond; /* Prepare file for export. */ gbFile f = {}; char * fileName = (char *)build_context.export_timings_file.text; gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, fileName); if (err != gbFileError_None) { gb_printf_err("Failed to export timings to: %s\n", fileName); exit_with_errors(); return; } else { gb_printf("\nExporting timings to '%s'... ", fileName); } defer (gb_file_close(&f)); if (build_context.export_timings_format == TimingsExportJson) { /* JSON export */ Parser *p = c->parser; isize lines = p->total_line_count; isize tokens = p->total_token_count; isize files = 0; isize packages = p->packages.count; isize total_file_size = 0; for (AstPackage *pkg : p->packages) { files += pkg->files.count; for (AstFile *file : pkg->files) { total_file_size += file->tokenizer.end - file->tokenizer.start; } } gb_fprintf(&f, "{\n"); gb_fprintf(&f, "\t\"totals\": [\n"); gb_fprintf(&f, "\t\t{\"name\": \"total_packages\", \"count\": %td},\n", packages); gb_fprintf(&f, "\t\t{\"name\": \"total_files\", \"count\": %td},\n", files); gb_fprintf(&f, "\t\t{\"name\": \"total_lines\", \"count\": %td},\n", lines); gb_fprintf(&f, "\t\t{\"name\": \"total_tokens\", \"count\": %td},\n", tokens); gb_fprintf(&f, "\t\t{\"name\": \"total_file_size\", \"count\": %td},\n", total_file_size); gb_fprintf(&f, "\t],\n"); gb_fprintf(&f, "\t\"timings\": [\n"); t->total_time_seconds = time_stamp_as_s(t->total, t->freq); f64 total_time = time_stamp(t->total, t->freq, unit); gb_fprintf(&f, "\t\t{\"name\": \"%.*s\", \"millis\": %.3f},\n", LIT(t->total.label), total_time); for (TimeStamp const &ts : t->sections) { f64 section_time = time_stamp(ts, t->freq, unit); gb_fprintf(&f, "\t\t{\"name\": \"%.*s\", \"millis\": %.3f},\n", LIT(ts.label), section_time); } gb_fprintf(&f, "\t],\n"); gb_fprintf(&f, "}\n"); } else if (build_context.export_timings_format == TimingsExportCSV) { /* CSV export */ t->total_time_seconds = time_stamp_as_s(t->total, t->freq); f64 total_time = time_stamp(t->total, t->freq, unit); /* CSV doesn't really like floating point values. Cast to `int`. */ gb_fprintf(&f, "\"%.*s\", %d\n", LIT(t->total.label), int(total_time)); for (TimeStamp const &ts : t->sections) { f64 section_time = time_stamp(ts, t->freq, unit); gb_fprintf(&f, "\"%.*s\", %d\n", LIT(ts.label), int(section_time)); } } gb_printf("Done.\n"); } gb_internal void check_defines(BuildContext *bc, Checker *c) { for (auto const &entry : bc->defined_values) { String name = make_string_c(entry.key); ExactValue value = entry.value; GB_ASSERT(value.kind != ExactValue_Invalid); bool found = false; for_array(i, c->info.defineables) { Defineable *def = &c->info.defineables[i]; if (def->name == name) { found = true; break; } } if (!found) { ERROR_BLOCK(); warning(nullptr, "given -define:%.*s is unused in the project", LIT(name)); if (!global_ignore_warnings()) { error_line("\tSuggestion: use the -show-defineables flag for an overview of the possible defines\n"); } } } } gb_internal void temp_alloc_defineable_strings(Checker *c) { for_array(i, c->info.defineables) { Defineable *def = &c->info.defineables[i]; def->default_value_str = make_string_c(write_exact_value_to_string(gb_string_make(temporary_allocator(), ""), def->default_value)); def->pos_str = make_string_c(token_pos_to_string(def->pos)); } } gb_internal GB_COMPARE_PROC(defineables_cmp) { Defineable *x = (Defineable *)a; Defineable *y = (Defineable *)b; int cmp = 0; String x_file = get_file_path_string(x->pos.file_id); String y_file = get_file_path_string(y->pos.file_id); cmp = string_compare(x_file, y_file); if (cmp) { return cmp; } return i32_cmp(x->pos.offset, y->pos.offset); } gb_internal void sort_defineables_and_remove_duplicates(Checker *c) { if (c->info.defineables.count == 0) { return; } gb_sort_array(c->info.defineables.data, c->info.defineables.count, defineables_cmp); Defineable prev = c->info.defineables[0]; for (isize i = 1; i < c->info.defineables.count; ) { Defineable curr = c->info.defineables[i]; if (prev.pos == curr.pos) { array_ordered_remove(&c->info.defineables, i); continue; } prev = curr; i++; } } gb_internal void export_defineables(Checker *c, String path) { gbFile f = {}; gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, (char *)path.text); if (err != gbFileError_None) { gb_printf_err("Failed to export defineables to: %.*s\n", LIT(path)); gb_exit(1); return; } else { gb_printf("Exporting defineables to '%.*s'...\n", LIT(path)); } defer (gb_file_close(&f)); gbString docs = gb_string_make(heap_allocator(), ""); defer (gb_string_free(docs)); gb_fprintf(&f, "Defineable,Default Value,Docs,Location\n"); for_array(i, c->info.defineables) { Defineable *def = &c->info.defineables[i]; gb_string_clear(docs); if (def->docs) { docs = gb_string_appendc(docs, "\""); for (Token const &token : def->docs->list) { for (isize i = 0; i < token.string.len; i++) { u8 c = token.string.text[i]; if (c == '"') { docs = gb_string_appendc(docs, "\"\""); } else { docs = gb_string_append_length(docs, &c, 1); } } } docs = gb_string_appendc(docs, "\""); } gb_fprintf(&f,"%.*s,%.*s,%s,%.*s\n", LIT(def->name), LIT(def->default_value_str), docs, LIT(def->pos_str)); } } gb_internal void show_defineables(Checker *c) { for_array(i, c->info.defineables) { Defineable *def = &c->info.defineables[i]; if (has_ansi_terminal_colours()) { gb_printf("\x1b[0;90m"); } printf("%.*s\n", LIT(def->pos_str)); if (def->docs) { for (Token const &token : def->docs->list) { gb_printf("%.*s\n", LIT(token.string)); } } if (has_ansi_terminal_colours()) { gb_printf("\x1b[0m"); } gb_printf("%.*s :: %.*s\n\n", LIT(def->name), LIT(def->default_value_str)); } } gb_internal void show_timings(Checker *c, Timings *t) { Parser *p = c->parser; isize lines = p->total_line_count; isize tokens = p->total_token_count; isize files = 0; isize packages = p->packages.count; isize total_file_size = 0; f64 total_tokenizing_time = 0; f64 total_parsing_time = 0; for (AstPackage *pkg : p->packages) { files += pkg->files.count; for (AstFile *file : pkg->files) { total_tokenizing_time += file->time_to_tokenize; total_parsing_time += file->time_to_parse; total_file_size += file->tokenizer.end - file->tokenizer.start; } } timings_print_all(t); PRINT_PEAK_USAGE(); if (!(build_context.export_timings_format == TimingsExportUnspecified)) { timings_export_all(t, c, true); } if (build_context.show_debug_messages && build_context.show_more_timings) { { gb_printf_err("\n"); gb_printf_err("Total Lines - %td\n", lines); gb_printf_err("Total Tokens - %td\n", tokens); gb_printf_err("Total Files - %td\n", files); gb_printf_err("Total Packages - %td\n", packages); gb_printf_err("Total File Size - %td\n", total_file_size); gb_printf_err("\n"); } { f64 time = total_tokenizing_time; gb_printf_err("Tokenization Only\n"); gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/time); gb_printf_err("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines); gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/time); gb_printf_err("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens); gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/time); gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024)); gb_printf_err("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size); gb_printf_err("\n"); } { f64 time = total_parsing_time; gb_printf_err("Parsing Only\n"); gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/time); gb_printf_err("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines); gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/time); gb_printf_err("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens); gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/time); gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024)); gb_printf_err("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size); gb_printf_err("\n"); } { TimeStamp ts = {}; for (TimeStamp const &s : t->sections) { if (s.label == "parse files") { ts = s; break; } } GB_ASSERT(ts.label == "parse files"); f64 parse_time = time_stamp_as_s(ts, t->freq); gb_printf_err("Parse pass\n"); gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/parse_time); gb_printf_err("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines); gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/parse_time); gb_printf_err("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens); gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time); gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/parse_time)/(1024*1024)); gb_printf_err("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size); gb_printf_err("\n"); } { TimeStamp ts = {}; TimeStamp ts_end = {}; for (TimeStamp const &s : t->sections) { if (s.label == "type check") { ts = s; } if (s.label == "type check finish") { GB_ASSERT(ts.label != ""); ts_end = s; break; } } GB_ASSERT(ts.label != ""); GB_ASSERT(ts_end.label != ""); ts.finish = ts_end.finish; f64 parse_time = time_stamp_as_s(ts, t->freq); gb_printf_err("Checker pass\n"); gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/parse_time); gb_printf_err("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines); gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/parse_time); gb_printf_err("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens); gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time); gb_printf_err("MiB/s - %.3f\n", (cast(f64)total_file_size/parse_time)/(1024*1024)); gb_printf_err("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size); gb_printf_err("\n"); } { f64 total_time = t->total_time_seconds; gb_printf_err("Total pass\n"); gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/total_time); gb_printf_err("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines); gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/total_time); gb_printf_err("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens); gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/total_time); gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/total_time)/(1024*1024)); gb_printf_err("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size); gb_printf_err("\n"); } } } gb_internal GB_COMPARE_PROC(file_path_cmp) { AstFile *x = *(AstFile **)a; AstFile *y = *(AstFile **)b; return string_compare(x->fullpath, y->fullpath); } gb_internal void export_dependencies(Checker *c) { GB_ASSERT(build_context.export_dependencies_format != DependenciesExportUnspecified); if (build_context.export_dependencies_file.len <= 0) { gb_printf_err("No dependency file specified with `-export-dependencies-file`\n"); exit_with_errors(); return; } Parser *p = c->parser; gbFile f = {}; char * fileName = (char *)build_context.export_dependencies_file.text; gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, fileName); if (err != gbFileError_None) { gb_printf_err("Failed to export dependencies to: %s\n", fileName); exit_with_errors(); return; } defer (gb_file_close(&f)); auto files = array_make(heap_allocator()); for (AstPackage *pkg : p->packages) { for (AstFile *f : pkg->files) { array_add(&files, f); } } array_sort(files, file_path_cmp); auto load_files = array_make(heap_allocator()); for (auto const &entry : c->info.load_file_cache) { auto *cache = entry.value; if (!cache || !cache->exists) { continue; } array_add(&load_files, cache); } array_sort(files, file_cache_sort_cmp); if (build_context.export_dependencies_format == DependenciesExportMake) { String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]); defer (gb_free(heap_allocator(), exe_name.text)); gb_fprintf(&f, "%.*s:", LIT(exe_name)); isize current_line_length = exe_name.len + 1; for_array(i, files) { AstFile *file = files[i]; /* Arbitrary line break value. Maybe make this better? */ if (current_line_length >= 80-2) { gb_file_write(&f, " \\\n ", 4); current_line_length = 1; } gb_file_write(&f, " ", 1); current_line_length++; for (isize k = 0; k < file->fullpath.len; k++) { char part = file->fullpath.text[k]; if (part == ' ') { gb_file_write(&f, "\\", 1); current_line_length++; } gb_file_write(&f, &part, 1); current_line_length++; } } gb_fprintf(&f, "\n"); } else if (build_context.export_dependencies_format == DependenciesExportJson) { gb_fprintf(&f, "{\n"); gb_fprintf(&f, "\t\"source_files\": [\n"); for_array(i, files) { AstFile *file = files[i]; gb_fprintf(&f, "\t\t\"%.*s\"", LIT(file->fullpath)); if (i+1 < files.count) { gb_fprintf(&f, ","); } gb_fprintf(&f, "\n"); } gb_fprintf(&f, "\t],\n"); gb_fprintf(&f, "\t\"load_files\": [\n"); for_array(i, load_files) { LoadFileCache *cache = load_files[i]; gb_fprintf(&f, "\t\t\"%.*s\"", LIT(cache->path)); if (i+1 < load_files.count) { gb_fprintf(&f, ","); } gb_fprintf(&f, "\n"); } gb_fprintf(&f, "\t]\n"); gb_fprintf(&f, "}\n"); } } gb_internal void remove_temp_files(lbGenerator *gen) { if (build_context.keep_temp_files) return; switch (build_context.build_mode) { case BuildMode_Executable: case BuildMode_StaticLibrary: case BuildMode_DynamicLibrary: break; case BuildMode_Object: case BuildMode_Assembly: case BuildMode_LLVM_IR: return; } TIME_SECTION("remove temp files"); for (String const &path : gen->output_temp_paths) { gb_file_remove(cast(char const *)path.text); } if (!build_context.keep_object_files) { switch (build_context.build_mode) { case BuildMode_Executable: case BuildMode_StaticLibrary: case BuildMode_DynamicLibrary: for (String const &path : gen->output_object_paths) { gb_file_remove(cast(char const *)path.text); } break; } } } gb_internal int print_show_help(String const arg0, String command, String optional_flag = {}) { bool help_resolved = false; bool printed_usage_header = false; bool printed_flags_header = false; if (command == "help" && optional_flag.len != 0 && optional_flag[0] != '-') { command = optional_flag; optional_flag = {}; } auto const print_usage_header_once = [&help_resolved, &printed_usage_header, arg0, command]() { if (printed_usage_header) { return; } print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0)); print_usage_line(0, "Usage:"); print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command)); print_usage_line(0, ""); help_resolved = true; printed_usage_header = true; }; if (command == "build") { print_usage_header_once(); print_usage_line(1, "build Compiles directory of .odin files as an executable."); print_usage_line(2, "One must contain the program's entry point, all must be in the same package."); print_usage_line(2, "Use `-file` to build a single file instead."); print_usage_line(2, "Examples:"); print_usage_line(3, "odin build . Builds package in current directory."); print_usage_line(3, "odin build Builds package in ."); print_usage_line(3, "odin build filename.odin -file Builds single-file package, must contain entry point."); } else if (command == "run") { print_usage_header_once(); print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable."); print_usage_line(2, "Append an empty flag and then the args, '-- ', to specify args for the output."); print_usage_line(2, "Examples:"); print_usage_line(3, "odin run . Builds and runs package in current directory."); print_usage_line(3, "odin run Builds and runs package in ."); print_usage_line(3, "odin run filename.odin -file Builds and runs single-file package, must contain entry point."); } else if (command == "check") { print_usage_header_once(); print_usage_line(1, "check Parses and type checks directory of .odin files."); print_usage_line(2, "Examples:"); print_usage_line(3, "odin check . Type checks package in current directory."); print_usage_line(3, "odin check Type checks package in ."); print_usage_line(3, "odin check filename.odin -file Type checks single-file package, must contain entry point."); } else if (command == "test") { print_usage_header_once(); print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package."); } else if (command == "doc") { print_usage_header_once(); print_usage_line(1, "doc Generates documentation from a directory of .odin files."); print_usage_line(2, "Examples:"); print_usage_line(3, "odin doc . Generates documentation on package in current directory."); print_usage_line(3, "odin doc Generates documentation on package in ."); print_usage_line(3, "odin doc filename.odin -file Generates documentation on single-file package."); } else if (command == "version") { print_usage_header_once(); print_usage_line(1, "version Prints version."); } else if (command == "strip-semicolon") { print_usage_header_once(); print_usage_line(1, "strip-semicolon"); print_usage_line(2, "Parses and type checks .odin file(s) and then removes unneeded semicolons from the entire project."); } else if (command == "bundle") { print_usage_header_once(); print_usage_line(1, "bundle Bundles a directory in a specific layout for that platform"); print_usage_line(2, "Supported platforms:"); print_usage_line(3, "android"); } else if (command == "report") { print_usage_header_once(); print_usage_line(1, "report Prints information useful to reporting a bug."); } else if (command == "root") { print_usage_header_once(); print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections."); } bool doc = command == "doc"; bool build = command == "build"; bool run_or_build = command == "run" || command == "build" || command == "test"; bool test_only = command == "test"; bool strip_semicolon = command == "strip-semicolon"; bool check_only = command == "check" || strip_semicolon; bool check = run_or_build || check_only; bool bundle = command == "bundle"; if (command == "help") { doc = true; build = true; run_or_build = true; test_only = true; strip_semicolon = true; check_only = true; check = true; } auto const print_flag = [&optional_flag, &help_resolved, &printed_flags_header, print_usage_header_once](char const *flag) -> bool { if (optional_flag.len != 0) { String f = make_string_c(flag); isize i = string_index_byte(f, ':'); if (i >= 0) { f.len = i; } if (optional_flag != f) { return false; } } print_usage_header_once(); if (!printed_flags_header) { print_usage_line(0, ""); print_usage_line(1, "Flags"); print_usage_line(0, ""); printed_flags_header = true; } help_resolved = true; print_usage_line(0, ""); print_usage_line(1, flag); return true; }; if (doc) { if (print_flag("-all-packages")) { print_usage_line(2, "Generates documentation for all packages used in the current project."); } } if (test_only) { if (print_flag("-all-packages")) { print_usage_line(2, "Tests all packages imported into the given initial package."); } } if (build) { if (print_flag("-build-mode:")) { print_usage_line(2, "Sets the build mode."); print_usage_line(2, "Available options:"); print_usage_line(3, "-build-mode:exe Builds as an executable."); print_usage_line(3, "-build-mode:test Builds as an executable that executes tests."); print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library."); print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library."); print_usage_line(3, "-build-mode:dynamic Builds as a dynamically linked library."); print_usage_line(3, "-build-mode:lib Builds as a statically linked library."); print_usage_line(3, "-build-mode:static Builds as a statically linked library."); print_usage_line(3, "-build-mode:obj Builds as an object file."); print_usage_line(3, "-build-mode:object Builds as an object file."); print_usage_line(3, "-build-mode:assembly Builds as an assembly file."); print_usage_line(3, "-build-mode:assembler Builds as an assembly file."); print_usage_line(3, "-build-mode:asm Builds as an assembly file."); print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file."); print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file."); } } if (check) { if (print_flag("-collection:=")) { print_usage_line(2, "Defines a library collection used for imports."); print_usage_line(2, "Example: -collection:shared=dir/to/shared"); print_usage_line(2, "Usage in Code:"); print_usage_line(3, "import \"shared:foo\""); } if (print_flag("-custom-attribute:")) { print_usage_line(2, "Add a custom attribute which will be ignored if it is unknown."); print_usage_line(2, "This can be used with metaprogramming tools."); print_usage_line(2, "Examples:"); print_usage_line(3, "-custom-attribute:my_tag"); print_usage_line(3, "-custom-attribute:my_tag,the_other_thing"); print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing"); } } if (run_or_build) { if (print_flag("-debug")) { print_usage_line(2, "Enables debug information, and defines the global constant ODIN_DEBUG to be 'true'."); } } if (check) { if (print_flag("-default-to-nil-allocator")) { print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing."); } if (print_flag("-default-to-panic-allocator")) { print_usage_line(2, "Sets the default allocator to be the panic_allocator, an allocator which calls panic() on any allocation attempt."); } if (print_flag("-define:=")) { print_usage_line(2, "Defines a scalar boolean, integer or string as global constant."); print_usage_line(2, "Example: -define:SPAM=123"); print_usage_line(2, "Usage in code:"); print_usage_line(3, "#config(SPAM, default_value)"); } } if (run_or_build) { if (print_flag("-disable-assert")) { print_usage_line(2, "Disables the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'."); } if (print_flag("-disable-red-zone")) { print_usage_line(2, "Disables red zone on a supported freestanding target."); } } if (check) { if (print_flag("-disallow-do")) { print_usage_line(2, "Disallows the 'do' keyword in the project."); } } if (doc) { if (print_flag("-doc-format")) { print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)."); } } if (run_or_build) { if (print_flag("-dynamic-map-calls")) { print_usage_line(2, "Uses dynamic map calls to minimize code generation at the cost of runtime execution."); } } if (check) { if (print_flag("-error-pos-style:")) { print_usage_line(2, "Available options:"); print_usage_line(3, "-error-pos-style:unix file/path:45:3:"); print_usage_line(3, "-error-pos-style:odin file/path(45:3)"); print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)"); } if (print_flag("-export-defineables:")) { print_usage_line(2, "Exports an overview of all the #config/#defined usages in CSV format to the given file path."); print_usage_line(2, "Example: -export-defineables:defineables.csv"); } if (print_flag("-export-dependencies:")) { print_usage_line(2, "Exports dependencies to one of a few formats. Requires `-export-dependencies-file`."); print_usage_line(2, "Available options:"); print_usage_line(3, "-export-dependencies:make Exports in Makefile format"); print_usage_line(3, "-export-dependencies:json Exports in JSON format"); } if (print_flag("-export-dependencies-file:")) { print_usage_line(2, "Specifies the filename for `-export-dependencies`."); print_usage_line(2, "Example: -export-dependencies-file:dependencies.d"); } if (print_flag("-export-timings:")) { print_usage_line(2, "Exports timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`."); print_usage_line(2, "Available options:"); print_usage_line(3, "-export-timings:json Exports compile time stats to JSON."); print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV."); } if (print_flag("-export-timings-file:")) { print_usage_line(2, "Specifies the filename for `-export-timings`."); print_usage_line(2, "Example: -export-timings-file:timings.json"); } } if (run_or_build) { if (print_flag("-extra-assembler-flags:")) { print_usage_line(2, "Adds extra assembler specific flags in a string."); } if (print_flag("-extra-linker-flags:")) { print_usage_line(2, "Adds extra linker specific flags in a string."); } } if (check) { if (print_flag("-file")) { print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command)); print_usage_line(2, "This means that `/a.odin` won't have access to `/b.odin`'s contents."); } if (print_flag("-foreign-error-procedures")) { print_usage_line(2, "States that the error procedures used in the runtime are defined in a separate translation unit."); } if (print_flag("-ignore-unknown-attributes")) { print_usage_line(2, "Ignores unknown attributes."); print_usage_line(2, "This can be used with metaprogramming tools."); } } if (run_or_build) { #if defined(GB_SYSTEM_WINDOWS) if (print_flag("-ignore-vs-search")) { print_usage_line(2, "[Windows only]"); print_usage_line(2, "Ignores the Visual Studio search for library paths."); } #endif } if (check) { if (print_flag("-ignore-warnings")) { print_usage_line(2, "Ignores warning messages."); } } if (check) { if (print_flag("-integer-division-by-zero:")) { print_usage_line(2, "Specifies the default behaviour for integer division by zero."); print_usage_line(2, "Available Options:"); print_usage_line(3, "-integer-division-by-zero:trap Trap on division/modulo/remainder by zero"); print_usage_line(3, "-integer-division-by-zero:zero x/0 == 0 and x%%0 == x and x%%%%0 == x"); print_usage_line(3, "-integer-division-by-zero:self x/0 == x and x%%0 == 0 and x%%%%0 == 0"); print_usage_line(3, "-integer-division-by-zero:all-bits x/0 == ~T(0) and x%%0 == x and x%%%%0 == x"); } } if (check) { if (print_flag("-json-errors")) { print_usage_line(2, "Prints the error messages as json to stderr."); } } if (run_or_build) { if (print_flag("-keep-temp-files")) { print_usage_line(2, "Keeps the temporary files generated during compilation."); } } else if (strip_semicolon) { if (print_flag("-keep-temp-files")) { print_usage_line(2, "Keeps the temporary files generated during stripping the unneeded semicolons from files."); } } if (test_only || run_or_build) { if (print_flag("-keep-executable")) { print_usage_line(2, "Keep the executable generated by `odin test` or `odin run` after running it. We clean it up by default."); print_usage_line(2, "If you build your program or test using `odin build`, the compiler does not automatically execute"); print_usage_line(2, "the resulting program, and this option is not applicable."); } } if (run_or_build) { if (print_flag("-linker:")) { print_usage_line(2, "Specify the linker to use."); print_usage_line(2, "Choices:"); for (i32 i = 0; i < Linker_COUNT; i++) { print_usage_line(3, "%.*s", LIT(linker_choices[i])); } } if (print_flag("-lld")) { print_usage_line(2, "Uses the LLD linker rather than the default."); } } if (check) { if (print_flag("-max-error-count:")) { print_usage_line(2, "Sets the maximum number of errors that can be displayed before the compiler terminates."); print_usage_line(2, "Must be an integer >0."); print_usage_line(2, "If not set, the default max error count is %d.", DEFAULT_MAX_ERROR_COLLECTOR_COUNT); } } if (run_or_build) { if (print_flag("-microarch:")) { print_usage_line(2, "Specifies the specific micro-architecture for the build in a string."); print_usage_line(2, "Examples:"); print_usage_line(3, "-microarch:sandybridge"); print_usage_line(3, "-microarch:native"); print_usage_line(3, "-microarch:\"?\" for a list"); } } if (check) { if (print_flag("-min-link-libs")) { print_usage_line(2, "If set, the number of linked libraries will be minimized to prevent duplications."); print_usage_line(2, "This is useful for so called \"dumb\" linkers compared to \"smart\" linkers."); } } if (run_or_build || bundle) { if (print_flag("-minimum-os-version:")) { print_usage_line(2, "Sets the minimum OS version targeted by the application."); print_usage_line(2, "Default: -minimum-os-version:11.0.0"); print_usage_line(2, "Only used when target is Darwin or subtarget is Android, if given, linking mismatched versions will emit a warning."); } } if (run_or_build) { if (print_flag("-no-bounds-check")) { print_usage_line(2, "Disables bounds checking program wide."); } if (print_flag("-no-crt")) { print_usage_line(2, "Disables automatic linking with the C Run Time."); } } if (check && command != "test") { if (print_flag("-no-entry-point")) { print_usage_line(2, "Removes default requirement of an entry point (e.g. main procedure)."); } } if (run_or_build) { if (print_flag("-no-rpath")) { print_usage_line(2, "Disables automatic addition of an rpath linked to the executable directory."); } if (print_flag("-no-thread-local")) { print_usage_line(2, "Ignores @thread_local attribute, effectively treating the program as if it is single-threaded."); } if (print_flag("-no-threaded-checker")) { print_usage_line(2, "Disables multithreading in the semantic checker stage."); } if (print_flag("-no-type-assert")) { print_usage_line(2, "Disables type assertion checking program wide."); } } if (run_or_build) { if (print_flag("-o:")) { print_usage_line(2, "Sets the optimization mode for compilation."); print_usage_line(2, "Available options:"); print_usage_line(3, "-o:none"); print_usage_line(3, "-o:minimal"); print_usage_line(3, "-o:size"); print_usage_line(3, "-o:speed"); if (LB_USE_NEW_PASS_SYSTEM) { print_usage_line(3, "-o:aggressive (use this with caution)"); } print_usage_line(2, "The default is -o:minimal."); } if (print_flag("-source-code-locations:")) { print_usage_line(2, "Processes the file and procedure strings, and line and column numbers, stored with a 'runtime.Source_Code_Location' value."); print_usage_line(2, "Available options:"); print_usage_line(3, "-source-code-locations:normal"); print_usage_line(3, "-source-code-locations:obfuscated"); print_usage_line(3, "-source-code-locations:filename"); print_usage_line(3, "-source-code-locations:none"); print_usage_line(2, "The default is -source-code-locations:normal."); } if (print_flag("-out:")) { print_usage_line(2, "Sets the file name of the outputted executable."); print_usage_line(2, "Example: -out:foo.exe"); } } if (doc) { if (print_flag("-out:")) { print_usage_line(2, "Sets the base name of the resulting .odin-doc file."); print_usage_line(2, "The extension can be optionally included; the resulting file will always have an extension of '.odin-doc'."); print_usage_line(2, "Example: -out:foo"); } } if (run_or_build) { #if defined(GB_SYSTEM_WINDOWS) if (print_flag("-pdb-name:")) { print_usage_line(2, "[Windows only]"); print_usage_line(2, "Defines the generated PDB name when -debug is enabled."); print_usage_line(2, "Example: -pdb-name:different.pdb"); } #endif } if (build) { if (print_flag("-print-linker-flags")) { print_usage_line(2, "Prints the all of the flags/arguments that will be passed to the linker."); } } if (run_or_build) { if (print_flag("-radlink")) { print_usage_line(2, "Uses the RAD linker rather than the default."); } if (print_flag("-reloc-mode:")) { print_usage_line(2, "Specifies the reloc mode."); print_usage_line(2, "Available options:"); print_usage_line(3, "-reloc-mode:default"); print_usage_line(3, "-reloc-mode:static"); print_usage_line(3, "-reloc-mode:pic"); print_usage_line(3, "-reloc-mode:dynamic-no-pic"); } #if defined(GB_SYSTEM_WINDOWS) if (print_flag("-resource:")) { print_usage_line(2, "[Windows only]"); print_usage_line(2, "Defines the resource file for the executable."); print_usage_line(2, "Example: -resource:path/to/file.rc"); print_usage_line(2, "or: -resource:path/to/file.res for a precompiled one."); } #endif if (print_flag("-sanitize:")) { print_usage_line(2, "Enables sanitization analysis."); print_usage_line(2, "Available options:"); print_usage_line(3, "-sanitize:address"); print_usage_line(3, "-sanitize:memory"); print_usage_line(3, "-sanitize:thread"); } } if (doc) { if (print_flag("-short")) { print_usage_line(2, "Shows shortened documentation for the packages."); } } if (check) { if (print_flag("-show-defineables")) { print_usage_line(2, "Shows an overview of all the #config/#defined usages in the project."); } if (print_flag("-show-system-calls")) { print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler."); } if (print_flag("-show-timings")) { print_usage_line(2, "Shows basic overview of the timings of different stages within the compiler in milliseconds."); } if (print_flag("-show-more-timings")) { print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds."); } } if (check_only) { if (print_flag("-show-unused")) { print_usage_line(2, "Shows unused package declarations within the current project."); } if (print_flag("-show-unused-with-location")) { print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location."); } } if (check) { if (print_flag("-strict-style")) { print_usage_line(2, "This enforces parts of same style as the Odin compiler, prefer '-vet-style -vet-semicolon' if you do not want to match it exactly."); print_usage_line(2, ""); print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons."); print_usage_line(2, "Errs on missing trailing commas followed by a newline."); print_usage_line(2, "Errs on deprecated syntax."); print_usage_line(2, "Errs when the attached-brace style in not adhered to (also known as 1TBS)."); print_usage_line(2, "Errs when 'case' labels are not in the same column as the associated 'switch' token."); } } if (run_or_build) { if (print_flag("-strict-target-features")) { print_usage_line(2, "Makes @(enable_target_features=\"...\") behave the same way as @(require_target_features=\"...\")."); print_usage_line(2, "This enforces that all generated code uses features supported by the combination of -target, -microarch, and -target-features."); } #if defined(GB_SYSTEM_WINDOWS) if (print_flag("-subsystem: