diff options
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 561 |
1 files changed, 378 insertions, 183 deletions
diff --git a/src/main.cpp b/src/main.cpp index 95491d6b2..b55908aef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,15 +11,19 @@ gb_global Timings global_timings = {0}; #if defined(LLVM_BACKEND_SUPPORT) +#if defined(GB_SYSTEM_WINDOWS) #include "llvm-c/Types.h" +#else +#include <llvm-c/Types.h> +#endif #endif #include "parser.hpp" #include "checker.hpp" #include "parser.cpp" -#include "docs.cpp" #include "checker.cpp" +#include "docs.cpp" #if defined(LLVM_BACKEND_SUPPORT) @@ -85,6 +89,10 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { exit_code = -1; } + if (exit_code) { + exit(exit_code); + } + return exit_code; #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) @@ -133,6 +141,10 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { // exit_code = status; + if (exit_code) { + exit(exit_code); + } + return exit_code; #endif } @@ -141,29 +153,23 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { #if defined(LLVM_BACKEND_SUPPORT) -i32 linker_stage(lbGenerator *gen) { - i32 exit_code = 0; - +void linker_stage(lbGenerator *gen) { Timings *timings = &global_timings; String output_base = gen->output_base; if (build_context.metrics.os == TargetOs_js) { timings_start_section(timings, str_lit("wasm-ld")); - exit_code = system_exec_command_line_app("wasm-ld", + system_exec_command_line_app("wasm-ld", "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm-obj\" -o \"%.*s.wasm\" %.*s %.*s", LIT(build_context.ODIN_ROOT), LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); - if (exit_code != 0) { - return exit_code; - } - return exit_code; } if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { #ifdef GB_SYSTEM_UNIX system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s", - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); #else gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n", LIT(target_os_names[build_context.metrics.os]), @@ -194,7 +200,7 @@ i32 linker_stage(lbGenerator *gen) { if (find_result.windows_sdk_version == 0) { gb_printf_err("Windows SDK not found.\n"); - return 1; + exit(1); } if (build_context.ignore_microsoft_magic) { @@ -259,17 +265,13 @@ i32 linker_stage(lbGenerator *gen) { char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE"; if (!build_context.use_lld) { // msvc if (build_context.has_resource) { - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"", LIT(output_base), LIT(build_context.resource_filepath) ); - if (exit_code != 0) { - return exit_code; - } - - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -284,7 +286,7 @@ i32 linker_stage(lbGenerator *gen) { lib_str ); } else { - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -300,7 +302,7 @@ i32 linker_stage(lbGenerator *gen) { ); } } else { // lld - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -341,11 +343,11 @@ i32 linker_stage(lbGenerator *gen) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a"))) { - // static libs, absolute full path relative to the file in which the lib was imported from - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else if (string_ends_with(lib, str_lit(".dylib"))) { + } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { + // For: + // object // dynamic lib + // static libs, absolute full path relative to the file in which the lib was imported from lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); } else { // dynamic or static system lib, just link regularly searching system library paths @@ -382,20 +384,33 @@ i32 linker_stage(lbGenerator *gen) { // Unlike the Win32 linker code, the output_ext includes the dot, because // typically executable files on *NIX systems don't have extensions. String output_ext = {}; - char const *link_settings = ""; + gbString link_settings = gb_string_make_reserve(heap_allocator(), 32); char const *linker; if (build_context.build_mode == BuildMode_DynamicLibrary) { + // NOTE(tetra, 2020-11-06): __$startup_runtime must be called at DLL load time. + // Clang, for some reason, won't let us pass the '-init' flag that lets us do this, + // so use ld instead. + // :UseLDForShared + linker = "ld"; + link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' "); // Shared libraries are .dylib on MacOS and .so on Linux. #if defined(GB_SYSTEM_OSX) output_ext = STR_LIT(".dylib"); - link_settings = "-dylib -dynamic"; + link_settings = gb_string_appendc(link_settings, "-dylib -dynamic "); #else output_ext = STR_LIT(".so"); - link_settings = "-shared"; + link_settings = gb_string_appendc(link_settings, "-shared "); #endif } else { - // TODO: Do I need anything here? - link_settings = ""; + #if defined(GB_SYSTEM_OSX) + linker = "ld"; + #else + // TODO(zangent): Figure out how to make ld work on Linux. + // It probably has to do with including the entire CRT, but + // that's quite a complicated issue to solve while remaining distro-agnostic. + // Clang can figure out linker flags for us, and that's good enough _for now_. + linker = "clang -Wno-unused-command-line-argument"; + #endif } if (build_context.out_filepath.len > 0) { @@ -406,17 +421,7 @@ i32 linker_stage(lbGenerator *gen) { } } - #if defined(GB_SYSTEM_OSX) - linker = "ld"; - #else - // TODO(zangent): Figure out how to make ld work on Linux. - // It probably has to do with including the entire CRT, but - // that's quite a complicated issue to solve while remaining distro-agnostic. - // Clang can figure out linker flags for us, and that's good enough _for now_. - linker = "clang -Wno-unused-command-line-argument"; - #endif - - exit_code = system_exec_command_line_app("ld-link", + system_exec_command_line_app("ld-link", "%s %s -o \"%.*s%.*s\" %s " " %s " " %.*s " @@ -431,37 +436,30 @@ i32 linker_stage(lbGenerator *gen) { " -e _main " #endif , linker, object_files, LIT(output_base), LIT(output_ext), - lib_str, - #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk", - #else - "-lc -lm", - #endif + #if defined(GB_SYSTEM_OSX) + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", + #else + "-lc -lm", + #endif + lib_str, LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); - if (exit_code != 0) { - return exit_code; - } #if defined(GB_SYSTEM_OSX) if (build_context.ODIN_DEBUG) { // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe // to the symbols in the object file - exit_code = system_exec_command_line_app("dsymutil", + system_exec_command_line_app("dsymutil", "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext) ); - - if (exit_code != 0) { - return exit_code; - } } #endif #endif } - return exit_code; + return; } #endif @@ -519,7 +517,7 @@ void usage(String argv0) { print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); print_usage_line(1, "check parse and type check .odin file"); print_usage_line(1, "query parse, type check, and output a .json file containing information about the program"); - print_usage_line(1, "docs generate documentation for a .odin file"); + print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files"); print_usage_line(1, "version print version"); print_usage_line(0, ""); print_usage_line(0, "For more information of flags, apply the flag to see what is possible"); @@ -565,6 +563,8 @@ enum BuildFlagKind { BuildFlag_OutFile, BuildFlag_OptimizationLevel, BuildFlag_ShowTimings, + BuildFlag_ShowUnused, + BuildFlag_ShowUnusedWithLocation, BuildFlag_ShowMoreTimings, BuildFlag_ShowSystemCalls, BuildFlag_ThreadCount, @@ -578,6 +578,7 @@ enum BuildFlagKind { BuildFlag_NoBoundsCheck, BuildFlag_NoDynamicLiterals, BuildFlag_NoCRT, + BuildFlag_NoEntryPoint, BuildFlag_UseLLD, BuildFlag_Vet, BuildFlag_UseLLVMApi, @@ -593,6 +594,9 @@ enum BuildFlagKind { BuildFlag_GlobalDefinitions, BuildFlag_GoToDefinitions, + BuildFlag_Short, + BuildFlag_AllPackages, + #if defined(GB_SYSTEM_WINDOWS) BuildFlag_IgnoreVsSearch, BuildFlag_ResourceFile, @@ -618,10 +622,13 @@ struct BuildFlag { BuildFlagKind kind; String name; BuildFlagParamKind param_kind; + u32 command_support; + bool allow_mulitple; }; -void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) { - BuildFlag flag = {kind, name, param_kind}; + +void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u32 command_support, bool allow_mulitple=false) { + BuildFlag flag = {kind, name, param_kind, command_support, allow_mulitple}; array_add(build_flags, flag); } @@ -662,44 +669,50 @@ ExactValue build_param_to_exact_value(String name, String param) { bool parse_build_flags(Array<String> args) { auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT); - add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer); - add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer); - add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String); - - add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None); - - add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None); + add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test); + add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, 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_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); + 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_Target, str_lit("target"), BuildFlagParam_String, Command__does_build); + 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_NoDynamicLiterals, str_lit("no-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_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_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build); + 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_Microarch, str_lit("microarch"), BuildFlagParam_String, 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_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None); + add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query); + add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query); + add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query); + + 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); + #if defined(GB_SYSTEM_WINDOWS) - add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String); + 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 GB_ASSERT(args.count >= 3); @@ -729,11 +742,19 @@ bool parse_build_flags(Array<String> args) { 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_array(build_flag_index, build_flags) { BuildFlag bf = build_flags[build_flag_index]; 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; @@ -835,7 +856,7 @@ bool parse_build_flags(Array<String> args) { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; path = string_trim_whitespace(path); - if (is_import_path_valid(path)) { + if (is_build_flag_path_valid(path)) { #if defined(GB_SYSTEM_WINDOWS) String ext = path_extension(path); if (ext == ".exe") { @@ -857,6 +878,15 @@ bool parse_build_flags(Array<String> args) { 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; @@ -1094,6 +1124,10 @@ bool parse_build_flags(Array<String> args) { build_context.no_crt = true; break; + case BuildFlag_NoEntryPoint: + build_context.no_entry_point = true; + break; + case BuildFlag_UseLLD: build_context.use_lld = true; break; @@ -1165,6 +1199,15 @@ bool parse_build_flags(Array<String> args) { } break; + case BuildFlag_Short: + build_context.cmd_doc_flags |= CmdDocFlag_Short; + break; + case BuildFlag_AllPackages: + build_context.cmd_doc_flags |= CmdDocFlag_AllPackages; + break; + + + #if defined(GB_SYSTEM_WINDOWS) case BuildFlag_IgnoreVsSearch: GB_ASSERT(value.kind == ExactValue_Invalid); @@ -1175,7 +1218,7 @@ bool parse_build_flags(Array<String> args) { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; path = string_trim_whitespace(path); - if (is_import_path_valid(path)) { + if (is_build_flag_path_valid(path)) { if(!string_ends_with(path, str_lit(".rc"))) { gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path)); bad_flags = true; @@ -1193,7 +1236,7 @@ bool parse_build_flags(Array<String> args) { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; path = string_trim_whitespace(path); - if (is_import_path_valid(path)) { + if (is_build_flag_path_valid(path)) { // #if defined(GB_SYSTEM_WINDOWS) // String ext = path_extension(path); // if (ext != ".pdb") { @@ -1228,26 +1271,35 @@ bool parse_build_flags(Array<String> args) { } } - - switch (bf.kind) { - case BuildFlag_Define: - // Allow for multiple - break; - default: + if (!bf.allow_mulitple) { set_flags[bf.kind] = ok; - break; } } break; } } - if (!found) { + 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 (u32 i = 0; i < 32; i++) { + if (found_bf.command_support & (1<<i)) { + if (count > 0) { + gb_printf_err(", "); + } + gb_printf_err("%s", odin_command_strings[i]); + count += 1; + } + } + gb_printf_err("\n"); + bad_flags = true; + } else if (!found) { gb_printf_err("Unknown flag: '%.*s'\n", LIT(name)); bad_flags = true; } } - if (build_context.query_data_set_settings.ok) { if (build_context.query_data_set_settings.kind == QueryDataSet_Invalid) { gb_printf_err("'odin query' requires a flag determining the kind of query data set to be returned\n"); @@ -1474,22 +1526,39 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); } else if (command == "check") { print_usage_line(1, "check parse and type check .odin file"); + } else if (command == "test") { + print_usage_line(1, "test build ands runs 'test_*' procedures in the initial package"); } else if (command == "query") { print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program"); - } else if (command == "docs") { - print_usage_line(1, "docs generate documentation for a .odin file"); + } else if (command == "doc") { + print_usage_line(1, "doc generate documentation from a .odin file, or directory of .odin files"); + print_usage_line(2, "Examples:"); + print_usage_line(3, "odin doc core/path"); + print_usage_line(3, "odin doc core/path core/path/filepath"); } else if (command == "version") { print_usage_line(1, "version print version"); } + bool doc = command == "doc"; bool build = command == "build"; - bool run_or_build = command == "run" || command == "build"; - bool check = command == "run" || command == "build" || command == "check"; + bool run_or_build = command == "run" || command == "build" || command == "test"; + bool check_only = command == "check"; + bool check = run_or_build || command == "check"; print_usage_line(0, ""); print_usage_line(1, "Flags"); print_usage_line(0, ""); + if (doc) { + print_usage_line(1, "-short"); + print_usage_line(2, "Show shortened documentation for the packages"); + print_usage_line(0, ""); + + print_usage_line(1, "-all-packages"); + print_usage_line(2, "Generates documentation for all packages used in the current project"); + print_usage_line(0, ""); + } + if (run_or_build) { print_usage_line(1, "-out:<filepath>"); print_usage_line(2, "Set the file name of the outputted executable"); @@ -1518,6 +1587,15 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(0, ""); } + if (check_only) { + print_usage_line(1, "-show-unused"); + print_usage_line(2, "Shows unused package declarations within the current project"); + print_usage_line(0, ""); + print_usage_line(1, "-show-unused-with-location"); + print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location"); + print_usage_line(0, ""); + } + if (run_or_build) { print_usage_line(1, "-keep-temp-files"); print_usage_line(2, "Keeps the temporary files generated during compilation"); @@ -1596,6 +1674,23 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-extra-linker-flags:<string>"); print_usage_line(2, "Adds extra linker specific flags in a string"); print_usage_line(0, ""); + + print_usage_line(1, "-microarch:<string>"); + 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(0, ""); + } + + if (check) { + print_usage_line(1, "-disallow-do"); + print_usage_line(2, "Disallows the 'do' keyword in the project"); + print_usage_line(0, ""); + + print_usage_line(1, "-default-to-nil-allocator"); + print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing"); + print_usage_line(0, ""); } if (run_or_build) { @@ -1629,6 +1724,80 @@ void print_show_help(String const arg0, String const &command) { } } +void print_show_unused(Checker *c) { + CheckerInfo *info = &c->info; + + auto unused = array_make<Entity *>(permanent_allocator(), 0, info->entities.count); + for_array(i, info->entities) { + Entity *e = info->entities[i]; + if (e == nullptr) { + continue; + } + if (e->pkg == nullptr || e->pkg->scope == nullptr) { + continue; + } + if (e->pkg->scope->flags & ScopeFlag_Builtin) { + continue; + } + switch (e->kind) { + case Entity_Invalid: + case Entity_Builtin: + case Entity_Nil: + case Entity_Label: + continue; + case Entity_Constant: + case Entity_Variable: + case Entity_TypeName: + case Entity_Procedure: + case Entity_ProcGroup: + case Entity_ImportName: + case Entity_LibraryName: + // Fine + break; + } + if ((e->scope->flags & (ScopeFlag_Pkg|ScopeFlag_File)) == 0) { + continue; + } + if (e->token.string.len == 0) { + continue; + } + if (e->token.string == "_") { + continue; + } + if (ptr_set_exists(&info->minimum_dependency_set, e)) { + continue; + } + array_add(&unused, e); + } + + gb_sort_array(unused.data, unused.count, cmp_entities_for_printing); + + print_usage_line(0, "Unused Package Declarations"); + + AstPackage *curr_pkg = nullptr; + EntityKind curr_entity_kind = Entity_Invalid; + for_array(i, unused) { + Entity *e = unused[i]; + if (curr_pkg != e->pkg) { + curr_pkg = e->pkg; + curr_entity_kind = Entity_Invalid; + print_usage_line(0, ""); + print_usage_line(0, "package %.*s", LIT(curr_pkg->name)); + } + if (curr_entity_kind != e->kind) { + curr_entity_kind = e->kind; + print_usage_line(1, "%s", print_entity_names[e->kind]); + } + if (build_context.show_unused_with_location) { + TokenPos pos = e->token.pos; + print_usage_line(2, "%.*s(%td:%td) %.*s", LIT(pos.file), pos.line, pos.column, LIT(e->token.string)); + } else { + print_usage_line(2, "%.*s", LIT(e->token.string)); + } + } + print_usage_line(0, ""); +} + int main(int arg_count, char const **arg_ptr) { if (arg_count < 2) { usage(make_string_c(arg_ptr[0])); @@ -1640,18 +1809,24 @@ int main(int arg_count, char const **arg_ptr) { timings_init(timings, str_lit("Total Time"), 128); defer (timings_destroy(timings)); + arena_init(&permanent_arena, heap_allocator()); + temp_allocator_init(&temporary_allocator_data, 16*1024*1024); + arena_init(&global_ast_arena, heap_allocator()); + permanent_arena.use_mutex = true; + init_string_buffer_memory(); init_string_interner(); init_global_error_collector(); init_keyword_hash_table(); global_big_int_init(); - arena_init(&global_ast_arena, heap_allocator()); array_init(&library_collections, heap_allocator()); // NOTE(bill): 'core' cannot be (re)defined by the user add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core"))); map_init(&build_context.defined_values, heap_allocator()); + build_context.extra_packages.allocator = heap_allocator(); + Array<String> args = setup_args(arg_count, arg_ptr); @@ -1660,11 +1835,15 @@ int main(int arg_count, char const **arg_ptr) { String run_args_string = {}; bool run_output = false; - if (command == "run") { + if (command == "run" || command == "test") { if (args.count < 3) { usage(args[0]); return 1; } + build_context.command_kind = Command_run; + if (command == "test") { + build_context.command_kind = Command_test; + } Array<String> run_args = array_make<String>(heap_allocator(), 0, arg_count); defer (array_free(&run_args)); @@ -1684,17 +1863,20 @@ int main(int arg_count, char const **arg_ptr) { run_args_string = string_join_and_quote(heap_allocator(), run_args); init_filename = args[2]; run_output = true; + } else if (command == "build") { if (args.count < 3) { usage(args[0]); return 1; } + build_context.command_kind = Command_build; init_filename = args[2]; } else if (command == "check") { if (args.count < 3) { usage(args[0]); return 1; } + build_context.command_kind = Command_check; build_context.no_output_files = true; init_filename = args[2]; } else if (command == "query") { @@ -1702,22 +1884,41 @@ int main(int arg_count, char const **arg_ptr) { usage(args[0]); return 1; } + build_context.command_kind = Command_query; build_context.no_output_files = true; build_context.query_data_set_settings.ok = true; init_filename = args[2]; - } else if (command == "docs") { + } else if (command == "doc") { if (args.count < 3) { usage(args[0]); return 1; } + build_context.command_kind = Command_doc; init_filename = args[2]; + for (isize i = 3; i < args.count; i++) { + auto arg = args[i]; + if (string_starts_with(arg, str_lit("-"))) { + break; + } + array_add(&build_context.extra_packages, arg); + } + isize extra_count = build_context.extra_packages.count; + if (extra_count > 0) { + gb_memmove(args.data + 3, args.data + 3 + extra_count, extra_count * gb_size_of(*args.data)); + args.count -= extra_count; + } + + + build_context.no_output_files = true; build_context.generate_docs = true; - #if 1 + build_context.no_entry_point = true; // ignore entry point + #if 0 print_usage_line(0, "Documentation generation is not yet supported"); return 1; #endif } else if (command == "version") { + build_context.command_kind = Command_version; gb_printf("%.*s version %.*s", LIT(args[0]), LIT(ODIN_VERSION)); #ifdef NIGHTLY @@ -1792,10 +1993,8 @@ int main(int arg_count, char const **arg_ptr) { return 1; } - if (build_context.generate_docs) { - // generate_documentation(&parser); - return 0; - } + temp_allocator_free_all(&temporary_allocator_data); + timings_start_section(timings, str_lit("type check")); Checker checker = {0}; @@ -1809,8 +2008,21 @@ int main(int arg_count, char const **arg_ptr) { check_parsed_files(&checker); } + temp_allocator_free_all(&temporary_allocator_data); + + if (build_context.generate_docs) { + if (global_error_collector.count != 0) { + return 1; + } + generate_documentation(&checker); + return 0; + } if (build_context.no_output_files) { + if (build_context.show_unused) { + print_show_unused(&checker); + } + if (build_context.query_data_set_settings.ok) { generate_and_print_query_data(&checker, timings); } else { @@ -1839,15 +2051,12 @@ int main(int arg_count, char const **arg_ptr) { } lb_generate_code(&gen); + temp_allocator_free_all(&temporary_allocator_data); + switch (build_context.build_mode) { case BuildMode_Executable: case BuildMode_DynamicLibrary: - { - i32 linker_stage_exit_count = linker_stage(&gen); - if (linker_stage_exit_count != 0) { - return linker_stage_exit_count; - } - } + linker_stage(&gen); break; } @@ -1864,9 +2073,6 @@ int main(int arg_count, char const **arg_ptr) { SIZE_T virtual_mem_used_by_me = pmc.PrivateUsage; gb_printf_err("virtual_memory_used: %tu B\n", virtual_mem_used_by_me); - gb_printf_err("total_allocated_node_memory: %lld B\n", total_allocated_node_memory.value); - gb_printf_err("total_subtype_node_memory_test: %lld B\n", total_subtype_node_memory_test.value); - gb_printf_err("fraction: %.6f\n", (f64)total_subtype_node_memory_test.value/(f64)total_allocated_node_memory.value); Parser *p = checker.parser; isize lines = p->total_line_count; isize tokens = p->total_token_count; @@ -1916,31 +2122,29 @@ int main(int arg_count, char const **arg_ptr) { timings_start_section(timings, str_lit("llvm ir gen")); ir_gen_tree(&ir_gen); + temp_allocator_free_all(&temporary_allocator_data); + timings_start_section(timings, str_lit("llvm ir opt tree")); ir_opt_tree(&ir_gen); + temp_allocator_free_all(&temporary_allocator_data); + timings_start_section(timings, str_lit("llvm ir print")); print_llvm_ir(&ir_gen); + temp_allocator_free_all(&temporary_allocator_data); + String output_name = ir_gen.output_name; String output_base = ir_gen.output_base; build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3); - i32 exit_code = 0; - timings_start_section(timings, str_lit("llvm-opt")); - exit_code = exec_llvm_opt(output_base); - if (exit_code != 0) { - return exit_code; - } + exec_llvm_opt(output_base); timings_start_section(timings, str_lit("llvm-llc")); - exit_code = exec_llvm_llc(output_base); - if (exit_code != 0) { - return exit_code; - } + exec_llvm_llc(output_base); if (build_context.build_mode == BuildMode_Object) { // Ignore the linker @@ -1949,7 +2153,7 @@ int main(int arg_count, char const **arg_ptr) { } remove_temp_files(output_base); - return exit_code; + return 0; } if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { @@ -2046,17 +2250,13 @@ int main(int arg_count, char const **arg_ptr) { if (!build_context.use_lld) { // msvc if (build_context.has_resource) { - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"", LIT(output_base), LIT(build_context.resource_filepath) ); - if (exit_code != 0) { - return exit_code; - } - - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -2071,7 +2271,7 @@ int main(int arg_count, char const **arg_ptr) { lib_str ); } else { - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -2087,7 +2287,7 @@ int main(int arg_count, char const **arg_ptr) { ); } } else { // lld - exit_code = system_exec_command_line_app("msvc-link", + system_exec_command_line_app("msvc-link", "\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " @@ -2104,10 +2304,6 @@ int main(int arg_count, char const **arg_ptr) { ); } - if (exit_code != 0) { - return exit_code; - } - if (build_context.show_timings) { show_timings(&checker, timings); } @@ -2142,13 +2338,14 @@ int main(int arg_count, char const **arg_ptr) { String lib_name = lib; lib_name = remove_extension_from_path(lib_name); lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a"))) { - // static libs, absolute full path relative to the file in which the lib was imported from - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else if (string_ends_with(lib, str_lit(".dylib"))) { - // dynamic lib - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else { + + } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { + // For: + // object + // dynamic lib + // static libs, absolute full path relative to the file in which the lib was imported from + lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); + } else { // dynamic or static system lib, just link regularly searching system library paths lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); } @@ -2176,22 +2373,36 @@ int main(int arg_count, char const **arg_ptr) { // Unlike the Win32 linker code, the output_ext includes the dot, because // typically executable files on *NIX systems don't have extensions. String output_ext = {}; - char const *link_settings = ""; + gbString link_settings = gb_string_make_reserve(heap_allocator(), 32); char const *linker; if (build_context.build_mode == BuildMode_DynamicLibrary) { + // NOTE(tetra, 2020-11-06): __$startup_runtime must be called at DLL load time. + // Clang, for some reason, won't let us pass the '-init' flag that lets us do this, + // so use ld instead. + // :UseLDForShared + linker = "ld"; + link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' "); // Shared libraries are .dylib on MacOS and .so on Linux. #if defined(GB_SYSTEM_OSX) output_ext = STR_LIT(".dylib"); - link_settings = "-dylib -dynamic"; + link_settings = gb_string_appendc(link_settings, "-dylib -dynamic "); #else output_ext = STR_LIT(".so"); - link_settings = "-shared"; + link_settings = gb_string_appendc(link_settings, "-shared "); #endif } else { - // TODO: Do I need anything here? - link_settings = ""; + #if defined(GB_SYSTEM_OSX) + linker = "ld"; + #else + // TODO(zangent): Figure out how to make ld work on Linux. + // It probably has to do with including the entire CRT, but + // that's quite a complicated issue to solve while remaining distro-agnostic. + // Clang can figure out linker flags for us, and that's good enough _for now_. + linker = "clang -Wno-unused-command-line-argument"; + #endif } + if (build_context.out_filepath.len > 0) { //NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that isize pos = string_extension_position(build_context.out_filepath); @@ -2200,17 +2411,8 @@ int main(int arg_count, char const **arg_ptr) { } } - #if defined(GB_SYSTEM_OSX) - linker = "ld"; - #else - // TODO(zangent): Figure out how to make ld work on Linux. - // It probably has to do with including the entire CRT, but - // that's quite a complicated issue to solve while remaining distro-agnostic. - // Clang can figure out linker flags for us, and that's good enough _for now_. - linker = "clang -Wno-unused-command-line-argument"; - #endif - exit_code = system_exec_command_line_app("ld-link", + system_exec_command_line_app("ld-link", "%s \"%.*s.o\" -o \"%.*s%.*s\" %s " " %s " " %.*s " @@ -2226,29 +2428,22 @@ int main(int arg_count, char const **arg_ptr) { #endif , linker, LIT(output_base), LIT(output_base), LIT(output_ext), lib_str, - #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk", - #else - "-lc -lm", - #endif + #if defined(GB_SYSTEM_OSX) + "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", + #else + "-lc -lm", + #endif LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), link_settings); - if (exit_code != 0) { - return exit_code; - } #if defined(GB_SYSTEM_OSX) if (build_context.ODIN_DEBUG) { // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe // to the symbols in the object file - exit_code = system_exec_command_line_app("dsymutil", + system_exec_command_line_app("dsymutil", "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext) ); - - if (exit_code != 0) { - return exit_code; - } } #endif |