| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637 |
- // #define NO_ARRAY_BOUNDS_CHECK
- #include "common.cpp"
- #include "timings.cpp"
- #include "tokenizer.cpp"
- #include "big_int.cpp"
- #include "exact_value.cpp"
- #include "build_settings.cpp"
- 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 "checker.cpp"
- #include "docs.cpp"
- #if defined(LLVM_BACKEND_SUPPORT)
- #include "llvm_backend.cpp"
- #if defined(GB_SYSTEM_OSX)
- #include <llvm/Config/llvm-config.h>
- #if LLVM_VERSION_MAJOR < 11
- #error LLVM Version 11+ is required => "brew install llvm@11"
- #endif
- #endif
- #endif
- #include "ir.cpp"
- #include "ir_opt.cpp"
- #include "ir_print.cpp"
- #include "query_data.cpp"
- #if defined(GB_SYSTEM_WINDOWS)
- // NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
- #include "microsoft_craziness.h"
- #endif
- // NOTE(bill): 'name' is used in debugging and profiling modes
- i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
- #if defined(GB_SYSTEM_WINDOWS)
- STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
- PROCESS_INFORMATION pi = {0};
- isize cmd_len = 0;
- isize const cmd_cap = 4096;
- char cmd_line[cmd_cap] = {};
- va_list va;
- gbTempArenaMemory tmp;
- String16 cmd;
- i32 exit_code = 0;
- 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);
- va_start(va, fmt);
- cmd_len = gb_snprintf_va(cmd_line, cmd_cap-1, fmt, va);
- va_end(va);
- 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);
- }
- tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
- defer (gb_temp_arena_memory_end(tmp));
- cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
- if (CreateProcessW(nullptr, cmd.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;
- }
- if (exit_code) {
- exit(exit_code);
- }
- return exit_code;
- #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
- char cmd_line[4096] = {0};
- isize cmd_len;
- va_list va;
- String cmd;
- i32 exit_code = 0;
- va_start(va, fmt);
- cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
- va_end(va);
- cmd = make_string(cast(u8 *)&cmd_line, cmd_len-1);
- 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);
- // pid_t pid = fork();
- // int status = 0;
- // if(pid == 0) {
- // // in child, pid == 0.
- // int ret = execvp(cmd.text, (char* const*) cmd.text);
- // if(ret == -1) {
- // gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
- // // we're in the child, so returning won't do us any good -- just quit.
- // exit(-1);
- // }
- // // unreachable
- // abort();
- // } else {
- // // wait for child to finish, then we can continue cleanup
- // int s = 0;
- // waitpid(pid, &s, 0);
- // status = WEXITSTATUS(s);
- // }
- // exit_code = status
- return exit_code;
- #endif
- }
- #if defined(LLVM_BACKEND_SUPPORT)
- i32 linker_stage(lbGenerator *gen) {
- i32 result = 0;
- 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"));
- 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 (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
- #ifdef GB_SYSTEM_UNIX
- result = 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));
- #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]),
- LIT(target_arch_names[build_context.metrics.arch])
- );
- #endif
- } else if (build_context.cross_compiling && build_context.different_os) {
- 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]),
- LIT(target_arch_names[build_context.metrics.arch])
- );
- build_context.keep_object_files = true;
- } else {
- #if defined(GB_SYSTEM_WINDOWS)
- timings_start_section(timings, str_lit("msvc-link"));
- gbString lib_str = gb_string_make(heap_allocator(), "");
- defer (gb_string_free(lib_str));
- char lib_str_buf[1024] = {0};
- char const *output_ext = "exe";
- gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
- defer (gb_string_free(link_settings));
- // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
- Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
- if (find_result.windows_sdk_version == 0) {
- gb_printf_err("Windows SDK not found.\n");
- exit(1);
- }
- if (build_context.ignore_microsoft_magic) {
- find_result = {};
- }
- // Add library search paths.
- if (find_result.vs_library_path.len > 0) {
- GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
- GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
- String path = {};
- auto add_path = [&](String path) {
- if (path[path.len-1] == '\\') {
- path.len -= 1;
- }
- link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
- };
- add_path(find_result.windows_sdk_um_library_path);
- add_path(find_result.windows_sdk_ucrt_library_path);
- add_path(find_result.vs_library_path);
- }
- for_array(i, gen->module.foreign_library_paths) {
- String lib = gen->module.foreign_library_paths[i];
- GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
- isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
- " \"%.*s\"", LIT(lib));
- lib_str = gb_string_appendc(lib_str, lib_str_buf);
- }
- if (build_context.build_mode == BuildMode_DynamicLibrary) {
- output_ext = "dll";
- link_settings = gb_string_append_fmt(link_settings, " /DLL");
- } else {
- link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
- }
- if (build_context.pdb_filepath != "") {
- link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
- }
- if (build_context.no_crt) {
- link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
- } else {
- link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
- }
- if (build_context.ODIN_DEBUG) {
- link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
- }
- gbString object_files = gb_string_make(heap_allocator(), "");
- defer (gb_string_free(object_files));
- for_array(i, gen->output_object_paths) {
- String object_path = gen->output_object_paths[i];
- object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
- }
- char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
- if (!build_context.use_lld) { // msvc
- if (build_context.has_resource) {
- result = system_exec_command_line_app("msvc-link",
- "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
- LIT(output_base),
- LIT(build_context.resource_filepath)
- );
- if(result == 0) {
- result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:%s "
- " %.*s "
- " %.*s "
- " %s "
- "",
- LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext,
- link_settings,
- subsystem_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- lib_str
- );
- }
- } else {
- result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:%s "
- " %.*s "
- " %.*s "
- " %s "
- "",
- LIT(find_result.vs_exe_path), object_files, LIT(output_base), output_ext,
- link_settings,
- subsystem_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- lib_str
- );
- }
- } else { // lld
- result = system_exec_command_line_app("msvc-link",
- "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:%s "
- " %.*s "
- " %.*s "
- " %s "
- "",
- LIT(build_context.ODIN_ROOT),
- LIT(output_base), object_files, output_ext,
- link_settings,
- subsystem_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- lib_str
- );
- }
- #else
- timings_start_section(timings, str_lit("ld-link"));
- // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
- char cwd[256];
- getcwd(&cwd[0], 256);
- //printf("%s\n", cwd);
- // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
- // files can be passed with -l:
- gbString lib_str = gb_string_make(heap_allocator(), "-L/");
- defer (gb_string_free(lib_str));
- for_array(i, gen->module.foreign_library_paths) {
- String lib = gen->module.foreign_library_paths[i];
- // NOTE(zangent): Sometimes, you have to use -framework on MacOS.
- // This allows you to specify '-f' in a #foreign_system_library,
- // without having to implement any new syntax specifically for MacOS.
- #if defined(GB_SYSTEM_OSX)
- if (string_ends_with(lib, str_lit(".framework"))) {
- // framework thingie
- 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")) || 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));
- }
- #else
- // NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
- // since those are statically linked to at link time. shared libraries (.so) has to be
- // available at runtime wherever the executable is run, so we make require those to be
- // local to the executable (unless the system collection is used, in which case we search
- // the system library paths for the library file).
- 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, " -l:\"%.*s\" ", LIT(lib));
- } else if (string_ends_with(lib, str_lit(".so"))) {
- // dynamic lib, relative path to executable
- // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
- // at runtimeto the executable
- lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, 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));
- }
- #endif
- }
- gbString object_files = gb_string_make(heap_allocator(), "");
- defer (gb_string_free(object_files));
- for_array(i, gen->output_object_paths) {
- String object_path = gen->output_object_paths[i];
- object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
- }
- // 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 = {};
- 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 = gb_string_appendc(link_settings, "-dylib -dynamic ");
- #else
- output_ext = STR_LIT(".so");
- link_settings = gb_string_appendc(link_settings, "-shared ");
- #endif
- } else {
- #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);
- if (pos > 0) {
- output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
- }
- }
- result = system_exec_command_line_app("ld-link",
- "%s %s -o \"%.*s%.*s\" %s "
- " %s "
- " %.*s "
- " %.*s "
- " %s "
- #if defined(GB_SYSTEM_OSX)
- // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
- // NOTE: If you change this (although this minimum is as low as you can go with Odin working)
- // make sure to also change the 'mtriple' param passed to 'opt'
- #if defined(GB_CPU_ARM)
- " -macosx_version_min 11.0.0 "
- #else
- " -macosx_version_min 10.8.0 "
- #endif
- // This points the linker to where the entry point is
- " -e _main "
- #endif
- , linker, object_files, LIT(output_base), LIT(output_ext),
- #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 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
- system_exec_command_line_app("dsymutil",
- "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
- );
- }
- #endif
- #endif
- }
- return result;
- }
- #endif
- Array<String> 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<String>(a, 0, wargc);
- for (isize i = 0; i < wargc; i++) {
- wchar_t *warg = 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<String>(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
- }
- void print_usage_line(i32 indent, char const *fmt, ...) {
- while (indent --> 0) {
- gb_printf_err("\t");
- }
- va_list va;
- va_start(va, fmt);
- gb_printf_err_va(fmt, va);
- va_end(va);
- gb_printf_err("\n");
- }
- void usage(String argv0) {
- 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 compile .odin file, or 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, "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, "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");
- print_usage_line(1, "-help");
- }
- bool string_is_valid_identifier(String str) {
- if (str.len <= 0) return false;
- isize rune_count = 0;
- isize w = 0;
- isize offset = 0;
- while (offset < str.len) {
- Rune r = 0;
- w = gb_utf8_decode(str.text, str.len, &r);
- if (r == GB_RUNE_INVALID) {
- return false;
- }
- if (rune_count == 0) {
- if (!rune_is_letter(r)) {
- return false;
- }
- } else {
- if (!rune_is_letter(r) && !rune_is_digit(r)) {
- return false;
- }
- }
- rune_count += 1;
- offset += w;
- }
- return true;
- }
- enum BuildFlagKind {
- BuildFlag_Invalid,
- BuildFlag_Help,
- BuildFlag_OutFile,
- BuildFlag_OptimizationLevel,
- BuildFlag_OptimizationMode,
- BuildFlag_ShowTimings,
- BuildFlag_ShowUnused,
- BuildFlag_ShowUnusedWithLocation,
- BuildFlag_ShowMoreTimings,
- BuildFlag_ShowSystemCalls,
- BuildFlag_ThreadCount,
- BuildFlag_KeepTempFiles,
- BuildFlag_Collection,
- BuildFlag_Define,
- BuildFlag_BuildMode,
- BuildFlag_Target,
- BuildFlag_Debug,
- BuildFlag_DisableAssert,
- BuildFlag_NoBoundsCheck,
- BuildFlag_NoDynamicLiterals,
- BuildFlag_NoCRT,
- BuildFlag_NoEntryPoint,
- BuildFlag_UseLLD,
- BuildFlag_Vet,
- BuildFlag_VetExtra,
- BuildFlag_UseLLVMApi,
- BuildFlag_IgnoreUnknownAttributes,
- BuildFlag_ExtraLinkerFlags,
- BuildFlag_Microarch,
- BuildFlag_DisallowDo,
- BuildFlag_DefaultToNilAllocator,
- BuildFlag_InsertSemicolon,
- BuildFlag_StrictStyle,
- BuildFlag_Compact,
- BuildFlag_GlobalDefinitions,
- BuildFlag_GoToDefinitions,
- BuildFlag_Short,
- BuildFlag_AllPackages,
- BuildFlag_DocFormat,
- BuildFlag_IgnoreWarnings,
- BuildFlag_WarningsAsErrors,
- #if defined(GB_SYSTEM_WINDOWS)
- BuildFlag_IgnoreVsSearch,
- BuildFlag_ResourceFile,
- BuildFlag_WindowsPdbName,
- BuildFlag_Subsystem,
- #endif
- 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;
- u32 command_support;
- bool allow_mulitple;
- };
- 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);
- }
- ExactValue build_param_to_exact_value(String name, String param) {
- ExactValue value = {};
- if (str_eq_ignore_case(param, str_lit("t")) ||
- str_eq_ignore_case(param, str_lit("true"))) {
- value = exact_value_bool(true);
- } else if (str_eq_ignore_case(param, str_lit("f")) ||
- str_eq_ignore_case(param, str_lit("false"))) {
- value = exact_value_bool(false);
- } else if (param.len > 0) {
- if (param[0] == '"') {
- 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);
- }
- }
- } else 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) {
- gb_printf_err("Invalid flag parameter for '%.*s' = '%.*s'\n", LIT(name), LIT(param));
- }
- }
- } else {
- gb_printf_err("Invalid flag parameter for '%.*s' = '%.*s'\n", LIT(name), LIT(param));
- }
- return value;
- }
- 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, 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_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
- 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_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_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_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_VetExtra, str_lit("vet-extra"), 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, Command__does_check);
- add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check);
- 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);
- 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);
- #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
- GB_ASSERT(args.count >= 3);
- Array<String> flag_args = array_slice(args, 3, args.count);
- bool set_flags[BuildFlag_COUNT] = {};
- bool bad_flags = false;
- for_array(i, flag_args) {
- String flag = flag_args[i];
- 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;
- for (; end < name.len; end++) {
- if (name[end] == ':') break;
- if (name[end] == '=') break; // IMPORTANT TODO(bill): DEPRECATE THIS!!!!
- }
- 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_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;
- } 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", 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", 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", 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", 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", 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_OptimizationLevel:
- GB_ASSERT(value.kind == ExactValue_Integer);
- if (set_flags[BuildFlag_OptimizationMode]) {
- gb_printf_err("Mixture of -opt and -o is not allowed\n");
- bad_flags = true;
- break;
- }
- build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
- break;
- case BuildFlag_OptimizationMode:
- GB_ASSERT(value.kind == ExactValue_String);
- if (set_flags[BuildFlag_OptimizationLevel]) {
- gb_printf_err("Mixture of -opt and -o is not allowed\n");
- bad_flags = true;
- break;
- }
- if (value.value_string == "minimal") {
- build_context.optimization_level = 0;
- } else if (value.value_string == "size") {
- build_context.optimization_level = 1;
- } else if (value.value_string == "speed") {
- build_context.optimization_level = 2;
- } else {
- gb_printf_err("Invalid optimization mode for -o:<string>, 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");
- 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_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();
- String fullpath = path_to_fullpath(a, path);
- if (!path_is_directory(fullpath)) {
- gb_printf_err("Library collection '%.*s' path must be a directory, got '%.*s'\n", LIT(name), LIT(fullpath));
- 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;
- }
- HashKey key = hash_pointer(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) {
- struct DistanceAndTarget {
- isize distance;
- isize target_index;
- };
- DistanceAndTarget 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(DistanceAndTarget, distance)));
- gb_printf_err("Unknown target '%.*s'\n", LIT(str));
- enum {MAX_SMALLEST_DISTANCE = 3};
- if (distances[0].distance <= MAX_SMALLEST_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_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_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") {
- build_context.build_mode = BuildMode_DynamicLibrary;
- } else if (str == "obj" || str == "object") {
- build_context.build_mode = BuildMode_Object;
- } 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 {
- gb_printf_err("Unknown build mode '%.*s'\n", LIT(str));
- gb_printf_err("Valid build modes:\n");
- gb_printf_err("\tdll, shared\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");
- bad_flags = true;
- break;
- }
- 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_NoDynamicLiterals:
- build_context.no_dynamic_literals = true;
- break;
- case BuildFlag_NoCRT:
- 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;
- case BuildFlag_Vet:
- build_context.vet = true;
- break;
- case BuildFlag_VetExtra:
- build_context.vet = true;
- build_context.vet_extra = true;
- break;
- case BuildFlag_UseLLVMApi:
- build_context.use_llvm_api = true;
- 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_Microarch:
- GB_ASSERT(value.kind == ExactValue_String);
- build_context.microarch = value.value_string;
- string_to_lower(&build_context.microarch);
- break;
- case BuildFlag_DisallowDo:
- build_context.disallow_do = true;
- break;
- case BuildFlag_DefaultToNilAllocator:
- build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true;
- break;
- case BuildFlag_InsertSemicolon:
- build_context.insert_semicolon = true;
- break;
- case BuildFlag_StrictStyle:
- build_context.insert_semicolon = true;
- build_context.strict_style = true;
- break;
- case BuildFlag_Compact:
- if (!build_context.query_data_set_settings.ok) {
- gb_printf_err("Invalid use of -compact flag, only allowed with 'odin query'\n");
- bad_flags = true;
- } else {
- build_context.query_data_set_settings.compact = true;
- }
- break;
- case BuildFlag_GlobalDefinitions:
- if (!build_context.query_data_set_settings.ok) {
- gb_printf_err("Invalid use of -global-definitions flag, only allowed with 'odin query'\n");
- bad_flags = true;
- } else if (build_context.query_data_set_settings.kind != QueryDataSet_Invalid) {
- gb_printf_err("Invalid use of -global-definitions flag, a previous flag for 'odin query' was set\n");
- bad_flags = true;
- } else {
- build_context.query_data_set_settings.kind = QueryDataSet_GlobalDefinitions;
- }
- break;
- case BuildFlag_GoToDefinitions:
- if (!build_context.query_data_set_settings.ok) {
- gb_printf_err("Invalid use of -go-to-definitions flag, only allowed with 'odin query'\n");
- bad_flags = true;
- } else if (build_context.query_data_set_settings.kind != QueryDataSet_Invalid) {
- gb_printf_err("Invalid use of -global-definitions flag, a previous flag for 'odin query' was set\n");
- bad_flags = true;
- } else {
- build_context.query_data_set_settings.kind = QueryDataSet_GoToDefinitions;
- }
- break;
- case BuildFlag_Short:
- build_context.cmd_doc_flags |= CmdDocFlag_Short;
- break;
- case BuildFlag_AllPackages:
- build_context.cmd_doc_flags |= CmdDocFlag_AllPackages;
- 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;
- #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)) {
- if(!string_ends_with(path, str_lit(".rc"))) {
- gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
- bad_flags = true;
- break;
- }
- build_context.resource_filepath = substring(path, 0, string_extension_position(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 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: {
- GB_ASSERT(value.kind == ExactValue_String);
- String subsystem = value.value_string;
- if (str_eq_ignore_case(subsystem, str_lit("console"))) {
- build_context.use_subsystem_windows = false;
- } else if (str_eq_ignore_case(subsystem, str_lit("window"))) {
- build_context.use_subsystem_windows = true;
- } else if (str_eq_ignore_case(subsystem, str_lit("windows"))) {
- build_context.use_subsystem_windows = true;
- } else {
- gb_printf_err("Invalid -subsystem string, got %.*s, expected either 'console' or 'windows'\n", LIT(subsystem));
- bad_flags = true;
- }
- break;
- }
- #endif
- }
- }
- if (!bf.allow_mulitple) {
- 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 (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");
- gb_printf_err("\t-global-definitions : outputs a JSON file of global definitions\n");
- gb_printf_err("\t-go-to-definitions : outputs a OGTD binary file of go to definitions for identifiers within an Odin project\n");
- bad_flags = true;
- }
- }
- return !bad_flags;
- }
- 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_array(i, p->packages) {
- files += p->packages[i]->files.count;
- for_array(j, p->packages[i]->files) {
- AstFile *file = p->packages[i]->files[j];
- 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);
- if (build_context.show_more_timings) {
- {
- gb_printf("\n");
- gb_printf("Total Lines - %td\n", lines);
- gb_printf("Total Tokens - %td\n", tokens);
- gb_printf("Total Files - %td\n", files);
- gb_printf("Total Packages - %td\n", packages);
- gb_printf("Total File Size - %td\n", total_file_size);
- gb_printf("\n");
- }
- {
- f64 time = total_tokenizing_time;
- gb_printf("Tokenization Only\n");
- gb_printf("LOC/s - %.3f\n", cast(f64)lines/time);
- gb_printf("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
- gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/time);
- gb_printf("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
- gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/time);
- gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024));
- gb_printf("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
- gb_printf("\n");
- }
- {
- f64 time = total_parsing_time;
- gb_printf("Parsing Only\n");
- gb_printf("LOC/s - %.3f\n", cast(f64)lines/time);
- gb_printf("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
- gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/time);
- gb_printf("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
- gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/time);
- gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024));
- gb_printf("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
- gb_printf("\n");
- }
- {
- TimeStamp ts = {};
- for_array(i, t->sections) {
- TimeStamp s = t->sections[i];
- 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("Parse pass\n");
- gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
- gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
- gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
- gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
- gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
- gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/parse_time)/(1024*1024));
- gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
- gb_printf("\n");
- }
- {
- TimeStamp ts = {};
- TimeStamp ts_end = {};
- for_array(i, t->sections) {
- TimeStamp s = t->sections[i];
- 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("Checker pass\n");
- gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
- gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
- gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
- gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
- gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
- gb_printf("MiB/s - %.3f\n", (cast(f64)total_file_size/parse_time)/(1024*1024));
- gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
- gb_printf("\n");
- }
- {
- f64 total_time = t->total_time_seconds;
- gb_printf("Total pass\n");
- gb_printf("LOC/s - %.3f\n", cast(f64)lines/total_time);
- gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
- gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
- gb_printf("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens);
- gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/total_time);
- gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/total_time)/(1024*1024));
- gb_printf("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size);
- gb_printf("\n");
- }
- }
- }
- void remove_temp_files(String output_base) {
- if (build_context.keep_temp_files) return;
- auto data = array_make<u8>(heap_allocator(), output_base.len + 30);
- defer (array_free(&data));
- isize n = output_base.len;
- gb_memmove(data.data, output_base.text, n);
- #define EXT_REMOVE(s) do { \
- gb_memmove(data.data+n, s, gb_size_of(s)); \
- gb_file_remove(cast(char const *)data.data); \
- } while (0)
- EXT_REMOVE(".ll");
- EXT_REMOVE(".bc");
- EXT_REMOVE("_memcpy_pass.bc");
- if (build_context.build_mode != BuildMode_Object && !build_context.keep_object_files) {
- #if defined(GB_SYSTEM_WINDOWS)
- EXT_REMOVE(".obj");
- EXT_REMOVE(".res");
- #else
- EXT_REMOVE(".o");
- #endif
- }
- #undef EXT_REMOVE
- }
- i32 exec_llvm_opt(String output_base) {
- #if defined(GB_SYSTEM_WINDOWS)
- // For more passes arguments: http://llvm.org/docs/Passes.html
- return system_exec_command_line_app("llvm-opt",
- "\"%.*sbin/opt\" \"%.*s.ll\" -o \"%.*s_memcpy_pass.bc\" -memcpyopt"
- "",
- LIT(build_context.ODIN_ROOT),
- LIT(output_base), LIT(output_base))
- || system_exec_command_line_app("llvm-opt",
- "\"%.*sbin/opt\" \"%.*s_memcpy_pass.bc\" -o \"%.*s.bc\" %.*s "
- "",
- LIT(build_context.ODIN_ROOT),
- LIT(output_base), LIT(output_base),
- LIT(build_context.opt_flags));
- #else
- // NOTE(zangent): This is separate because it seems that LLVM tools are packaged
- // with the Windows version, while they will be system-provided on MacOS and GNU/Linux
- return system_exec_command_line_app("llvm-opt",
- "opt \"%.*s.ll\" -o \"%.*s_memcpy_pass.bc\" -memcpyopt"
- "",
- LIT(output_base), LIT(output_base))
- || system_exec_command_line_app("llvm-opt",
- "opt \"%.*s_memcpy_pass.bc\" -o \"%.*s.bc\" %.*s "
- "",
- LIT(output_base), LIT(output_base),
- LIT(build_context.opt_flags));
- #endif
- }
- i32 exec_llvm_llc(String output_base) {
- // For more arguments: http://llvm.org/docs/CommandGuide/llc.html
- #if defined(GB_SYSTEM_WINDOWS)
- return system_exec_command_line_app("llvm-llc",
- "\"%.*sbin\\llc\" \"%.*s.bc\" -filetype=obj -O%d "
- "-o \"%.*s.obj\" "
- "%.*s"
- "",
- LIT(build_context.ODIN_ROOT),
- LIT(output_base),
- build_context.optimization_level,
- LIT(output_base),
- LIT(build_context.llc_flags));
- #else
- // NOTE(zangent): Linux / Unix is unfinished and not tested very well.
- return system_exec_command_line_app("llc",
- "llc \"%.*s.bc\" -filetype=obj -relocation-model=pic -O%d "
- "%.*s "
- "%s%.*s",
- LIT(output_base),
- build_context.optimization_level,
- LIT(build_context.llc_flags),
- build_context.cross_compiling ? "-mtriple=" : "",
- cast(int)(build_context.cross_compiling ? build_context.metrics.target_triplet.len : 0),
- build_context.metrics.target_triplet.text);
- #endif
- }
- void print_show_help(String const arg0, String const &command) {
- 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, "");
- if (command == "build") {
- print_usage_line(1, "build compile .odin file, or 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.");
- } else if (command == "run") {
- 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 == "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" || 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, "");
- print_usage_line(1, "-doc-format");
- print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)");
- 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");
- print_usage_line(2, "Example: -out:foo.exe");
- print_usage_line(0, "");
- print_usage_line(1, "-opt:<integer>");
- print_usage_line(2, "Set the optimization level for compilation");
- print_usage_line(2, "Accepted values: 0, 1, 2, 3");
- print_usage_line(2, "Example: -opt:2");
- print_usage_line(0, "");
- print_usage_line(1, "-o:<string>");
- print_usage_line(2, "Set the optimization mode for compilation");
- print_usage_line(2, "Accepted values: minimal, size, speed");
- print_usage_line(2, "Example: -o:speed");
- print_usage_line(0, "");
- }
- if (check) {
- print_usage_line(1, "-show-timings");
- print_usage_line(2, "Shows basic overview of the timings of different stages within the compiler in milliseconds");
- print_usage_line(0, "");
- print_usage_line(1, "-show-more-timings");
- print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds");
- print_usage_line(0, "");
- print_usage_line(1, "-thread-count:<integer>");
- print_usage_line(2, "Override the number of threads the compiler will use to compile with");
- print_usage_line(2, "Example: -thread-count:2");
- 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");
- print_usage_line(0, "");
- }
- if (check) {
- print_usage_line(1, "-collection:<name>=<filepath>");
- 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\"");
- print_usage_line(0, "");
- print_usage_line(1, "-define:<name>=<expression>");
- print_usage_line(2, "Defines a global constant with a value");
- print_usage_line(2, "Example: -define:SPAM=123");
- print_usage_line(0, "");
- }
- if (build) {
- print_usage_line(1, "-build-mode:<mode>");
- print_usage_line(2, "Sets the build mode");
- print_usage_line(2, "Available options:");
- print_usage_line(3, "-build-mode:exe Build as an executable");
- print_usage_line(3, "-build-mode:dll Build as a dynamically linked library");
- print_usage_line(3, "-build-mode:shared Build as a dynamically linked library");
- print_usage_line(3, "-build-mode:obj Build as an object file");
- print_usage_line(3, "-build-mode:object Build as an object file");
- print_usage_line(0, "");
- }
- if (check) {
- print_usage_line(1, "-target:<string>");
- print_usage_line(2, "Sets the target for the executable to be built in");
- print_usage_line(0, "");
- }
- if (run_or_build) {
- print_usage_line(1, "-debug");
- print_usage_line(2, "Enabled debug information, and defines the global constant ODIN_DEBUG to be 'true'");
- print_usage_line(0, "");
- print_usage_line(1, "-disable-assert");
- print_usage_line(2, "Disable the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'");
- print_usage_line(0, "");
- print_usage_line(1, "-no-bounds-check");
- print_usage_line(2, "Disables bounds checking program wide");
- print_usage_line(0, "");
- print_usage_line(1, "-no-crt");
- print_usage_line(2, "Disables automatic linking with the C Run Time");
- print_usage_line(0, "");
- print_usage_line(1, "-use-lld");
- print_usage_line(2, "Use the LLD linker rather than the default");
- print_usage_line(0, "");
- }
- if (check) {
- print_usage_line(1, "-vet");
- print_usage_line(2, "Do extra checks on the code");
- print_usage_line(2, "Extra checks include:");
- print_usage_line(3, "Variable shadowing within procedures");
- print_usage_line(3, "Unused declarations");
- print_usage_line(0, "");
- print_usage_line(1, "-vet-extra");
- print_usage_line(2, "Do even more checks than standard vet on the code");
- print_usage_line(2, "To treat the extra warnings as errors, use -warnings-as-errors");
- print_usage_line(0, "");
- print_usage_line(1, "-ignore-unknown-attributes");
- print_usage_line(2, "Ignores unknown attributes");
- print_usage_line(2, "This can be used with metaprogramming tools");
- print_usage_line(0, "");
- if (command != "test") {
- print_usage_line(1, "-no-entry-point");
- print_usage_line(2, "Removes default requirement of an entry point (e.g. main procedure)");
- print_usage_line(0, "");
- }
- }
- if (run_or_build) {
- 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, "");
- print_usage_line(1, "-insert-semicolon");
- print_usage_line(2, "Inserts semicolons on newlines during tokenization using a basic rule");
- print_usage_line(0, "");
- print_usage_line(1, "-strict-style");
- print_usage_line(2, "Enforces code style stricter whilst parsing, requiring such things as trailing commas");
- print_usage_line(0, "");
- print_usage_line(1, "-ignore-warnings");
- print_usage_line(2, "Ignores warning messages");
- print_usage_line(0, "");
- print_usage_line(1, "-warnings-as-errors");
- print_usage_line(2, "Treats warning messages as error messages");
- print_usage_line(0, "");
- }
- if (run_or_build) {
- #if defined(GB_SYSTEM_WINDOWS)
- print_usage_line(1, "-ignore-vs-search");
- print_usage_line(2, "[Windows only]");
- print_usage_line(2, "Ignores the Visual Studio search for library paths");
- print_usage_line(0, "");
- print_usage_line(1, "-resource:<filepath>");
- 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(0, "");
- print_usage_line(1, "-pdb-name:<filepath>");
- 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");
- print_usage_line(0, "");
- print_usage_line(1, "-subsystem:<option>");
- print_usage_line(2, "[Windows only]");
- print_usage_line(2, "Defines the subsystem for the application");
- print_usage_line(2, "Available options:");
- print_usage_line(3, "console");
- print_usage_line(3, "windows");
- print_usage_line(0, "");
- #endif
- }
- }
- 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 %.*s", token_pos_to_string(pos), LIT(e->token.string));
- } else {
- print_usage_line(2, "%.*s", LIT(e->token.string));
- }
- }
- print_usage_line(0, "");
- }
- void enforce_platform_settings(void) {
- #if defined(GB_SYSTEM_OSX) && defined(GB_CPU_ARM)
- build_context.use_llvm_api = true;
- #endif
- }
- int main(int arg_count, char const **arg_ptr) {
- if (arg_count < 2) {
- usage(make_string_c(arg_ptr[0]));
- return 1;
- }
- Timings *timings = &global_timings;
- 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();
- 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);
- String command = args[1];
- String init_filename = {};
- String run_args_string = {};
- bool run_output = false;
- 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));
- isize last_non_run_arg = args.count;
- for_array(i, args) {
- if (args[i] == "--") {
- last_non_run_arg = i;
- }
- if (i <= last_non_run_arg) {
- continue;
- }
- array_add(&run_args, args[i]);
- }
- args = array_slice(args, 0, last_non_run_arg);
- 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") {
- if (args.count < 3) {
- 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 == "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;
- 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
- gb_printf("-nightly");
- #endif
- #ifdef GIT_SHA
- gb_printf("-%s", GIT_SHA);
- #endif
- gb_printf("\n");
- return 0;
- } else {
- usage(args[0]);
- return 1;
- }
- if (init_filename == "-help") {
- build_context.show_help = true;
- }
- build_context.command = command;
- if (!parse_build_flags(args)) {
- return 1;
- }
- if (build_context.show_help) {
- print_show_help(args[0], command);
- return 0;
- }
- enforce_platform_settings();
- // NOTE(bill): add 'shared' directory if it is not already set
- if (!find_library_collection_path(str_lit("shared"), nullptr)) {
- add_library_collection(str_lit("shared"),
- get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
- }
- init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr);
- // if (build_context.word_size == 4 && build_context.metrics.os != TargetOs_js) {
- // print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0]));
- // return 1;
- // }
- if (build_context.metrics.os == TargetOs_js) {
- if (!build_context.use_llvm_api) {
- print_usage_line(0, "%.*s - js platform only supported with the -llvm-api backend", LIT(args[0]));
- return 1;
- }
- }
- if (!build_context.use_llvm_api) {
- if (build_context.build_mode == BuildMode_Assembly) {
- print_usage_line(0, "-build-mode:assembly is only supported with the -llvm-api backend", LIT(args[0]));
- return 1;
- }
- }
- init_universal();
- // TODO(bill): prevent compiling without a linker
- timings_start_section(timings, str_lit("parse files"));
- Parser parser = {0};
- if (!init_parser(&parser)) {
- return 1;
- }
- defer (destroy_parser(&parser));
- if (parse_packages(&parser, init_filename) != ParseFile_None) {
- return 1;
- }
- if (any_errors()) {
- return 1;
- }
- temp_allocator_free_all(&temporary_allocator_data);
- timings_start_section(timings, str_lit("type check"));
- Checker checker = {0};
- bool checked_inited = init_checker(&checker, &parser);
- defer (if (checked_inited) {
- destroy_checker(&checker);
- });
- if (checked_inited) {
- check_parsed_files(&checker);
- }
- if (any_errors()) {
- return 1;
- }
- 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 {
- if (build_context.show_timings) {
- show_timings(&checker, timings);
- }
- }
- if (global_error_collector.count != 0) {
- return 1;
- }
- return 0;
- }
- if (!checked_inited) {
- return 1;
- }
- if (build_context.use_llvm_api) {
- #if defined(LLVM_BACKEND_SUPPORT)
- timings_start_section(timings, str_lit("LLVM API Code Gen"));
- lbGenerator gen = {};
- if (!lb_init_generator(&gen, &checker)) {
- return 1;
- }
- lb_generate_code(&gen);
- temp_allocator_free_all(&temporary_allocator_data);
- switch (build_context.build_mode) {
- case BuildMode_Executable:
- case BuildMode_DynamicLibrary:
- i32 result = linker_stage(&gen);
- if(result != 0) {
- return 1;
- }
- break;
- }
- if (build_context.show_timings) {
- show_timings(&checker, timings);
- }
- remove_temp_files(gen.output_base);
- #if defined(GB_COMPILER_MSVC)
- if (false) {
- PROCESS_MEMORY_COUNTERS_EX pmc = {};
- GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc));
- SIZE_T virtual_mem_used_by_me = pmc.PrivateUsage;
- gb_printf_err("virtual_memory_used: %tu B\n", virtual_mem_used_by_me);
- Parser *p = checker.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_array(i, p->packages) {
- files += p->packages[i]->files.count;
- for_array(j, p->packages[i]->files) {
- AstFile *file = p->packages[i]->files[j];
- total_file_size += file->tokenizer.end - file->tokenizer.start;
- }
- }
- gb_printf_err("total_file_size: %lld B\n", total_file_size);
- gb_printf_err("lines: %lld\n", lines);
- gb_printf_err("files: %lld\n", files);
- gb_printf_err("tokens: %lld\n", tokens);
- gb_printf_err("packages: %lld\n", packages);
- }
- #endif
- if (run_output) {
- #if defined(GB_SYSTEM_WINDOWS)
- return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen.output_base), LIT(run_args_string));
- #else
- //NOTE(thebirk): This whole thing is a little leaky
- String output_ext = {};
- String complete_path = concatenate_strings(heap_allocator(), gen.output_base, output_ext);
- complete_path = path_to_full_path(heap_allocator(), complete_path);
- return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
- #endif
- }
- return 0;
- #else
- gb_printf_err("LLVM C API backend is not supported on this platform yet\n");
- return 1;
- #endif
- } else {
- irGen ir_gen = {0};
- if (!ir_gen_init(&ir_gen, &checker)) {
- return 1;
- }
- // defer (ir_gen_destroy(&ir_gen));
- 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);
- timings_start_section(timings, str_lit("llvm-opt"));
- exec_llvm_opt(output_base);
- timings_start_section(timings, str_lit("llvm-llc"));
- exec_llvm_llc(output_base);
- if (build_context.build_mode == BuildMode_Object) {
- // Ignore the linker
- if (build_context.show_timings) {
- show_timings(&checker, timings);
- }
- remove_temp_files(output_base);
- return 0;
- }
- 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 -ffreestanding -nostdlib \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
- 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]),
- LIT(target_arch_names[build_context.metrics.arch])
- );
- #endif
- } else if (build_context.cross_compiling && build_context.different_os) {
- 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]),
- LIT(target_arch_names[build_context.metrics.arch])
- );
- build_context.keep_object_files = true;
- } else {
- #if defined(GB_SYSTEM_WINDOWS)
- timings_start_section(timings, str_lit("msvc-link"));
- gbString lib_str = gb_string_make(heap_allocator(), "");
- defer (gb_string_free(lib_str));
- char lib_str_buf[1024] = {0};
- char const *output_ext = "exe";
- gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
- defer (gb_string_free(link_settings));
- // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
- Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
- if (find_result.windows_sdk_version == 0) {
- gb_printf_err("Windows SDK not found.\n");
- return 1;
- }
- if (build_context.ignore_microsoft_magic) {
- find_result = {};
- }
- // Add library search paths.
- if (find_result.vs_library_path.len > 0) {
- GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
- GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
- String path = {};
- auto add_path = [&](String path) {
- if (path[path.len-1] == '\\') {
- path.len -= 1;
- }
- link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
- };
- add_path(find_result.windows_sdk_um_library_path);
- add_path(find_result.windows_sdk_ucrt_library_path);
- add_path(find_result.vs_library_path);
- }
- for_array(i, ir_gen.module.foreign_library_paths) {
- String lib = ir_gen.module.foreign_library_paths[i];
- GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
- isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
- " \"%.*s\"", LIT(lib));
- lib_str = gb_string_appendc(lib_str, lib_str_buf);
- }
- if (build_context.build_mode == BuildMode_DynamicLibrary) {
- output_ext = "dll";
- link_settings = gb_string_append_fmt(link_settings, " /DLL");
- } else {
- link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
- }
- if (build_context.pdb_filepath != "") {
- link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
- }
- if (build_context.no_crt) {
- link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
- } else {
- link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
- }
- if (ir_gen.module.generate_debug_info) {
- link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
- }
- char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
- if (!build_context.use_lld) { // msvc
- if (build_context.has_resource) {
- i32 result = system_exec_command_line_app("msvc-link",
- "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
- LIT(output_base),
- LIT(build_context.resource_filepath)
- );
- if(result != 0) {
- return 1;
- }
- result = 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 "
- " %.*s "
- " %s "
- "",
- LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), LIT(output_base), output_ext,
- link_settings,
- subsystem_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- lib_str
- );
- if(result != 0) {
- return 1;
- }
- } else {
- i32 result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:%s "
- " %.*s "
- " %.*s "
- " %s "
- "",
- LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), output_ext,
- link_settings,
- subsystem_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- lib_str
- );
- if(result != 0) {
- return 1;
- }
- }
- } else { // lld
- i32 result = 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 "
- " %.*s "
- " %s "
- "",
- LIT(build_context.ODIN_ROOT),
- LIT(output_base), LIT(output_base), output_ext,
- link_settings,
- subsystem_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- lib_str
- );
- if(result != 0) {
- return 1;
- }
- }
- if (build_context.show_timings) {
- show_timings(&checker, timings);
- }
- remove_temp_files(output_base);
- if (run_output) {
- return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string));
- }
- #else
- timings_start_section(timings, str_lit("ld-link"));
- // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
- char cwd[256];
- getcwd(&cwd[0], 256);
- //printf("%s\n", cwd);
- // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
- // files can be passed with -l:
- gbString lib_str = gb_string_make(heap_allocator(), "-L/");
- defer (gb_string_free(lib_str));
- for_array(i, ir_gen.module.foreign_library_paths) {
- String lib = ir_gen.module.foreign_library_paths[i];
- // NOTE(zangent): Sometimes, you have to use -framework on MacOS.
- // This allows you to specify '-f' in a #foreign_system_library,
- // without having to implement any new syntax specifically for MacOS.
- #if defined(GB_SYSTEM_OSX)
- if (string_ends_with(lib, str_lit(".framework"))) {
- // framework thingie
- 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")) || 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));
- }
- #else
- // NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
- // since those are statically linked to at link time. shared libraries (.so) has to be
- // available at runtime wherever the executable is run, so we make require those to be
- // local to the executable (unless the system collection is used, in which case we search
- // the system library paths for the library file).
- 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, " -l:\"%.*s\" ", LIT(lib));
- } else if (string_ends_with(lib, str_lit(".so"))) {
- // dynamic lib, relative path to executable
- // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
- // at runtimeto the executable
- lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, 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));
- }
- #endif
- }
- // 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 = {};
- 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";
- // NOTE(tetra, 2021-02-24): On Darwin, the symbol has _3_ underscores; on Linux, it only has 2.
- link_settings = gb_string_append_fmt(link_settings, "-init '%s$startup_runtime' ", build_context.metrics.os == TargetOs_darwin ? "___" : "__");
- // Shared libraries are .dylib on MacOS and .so on Linux.
- #if defined(GB_SYSTEM_OSX)
- output_ext = STR_LIT(".dylib");
- link_settings = gb_string_appendc(link_settings, "-dylib -dynamic ");
- #else
- output_ext = STR_LIT(".so");
- link_settings = gb_string_appendc(link_settings, "-shared ");
- #endif
- } else {
- #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);
- if (pos > 0) {
- output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
- }
- }
- i32 result = system_exec_command_line_app("ld-link",
- "%s \"%.*s.o\" -o \"%.*s%.*s\" %s "
- " %s "
- " %.*s "
- " %.*s "
- " %s "
- #if defined(GB_SYSTEM_OSX)
- // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
- // NOTE: If you change this (although this minimum is as low as you can go with Odin working)
- // make sure to also change the 'mtriple' param passed to 'opt'
- " -macosx_version_min 10.8.0 "
- // This points the linker to where the entry point is
- " -e _main "
- #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 -L/usr/local/lib",
- #else
- "-lc -lm",
- #endif
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- link_settings);
- if(result != 0) {
- return 1;
- }
- #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
- system_exec_command_line_app("dsymutil",
- "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
- );
- }
- #endif
- if (build_context.show_timings) {
- show_timings(&checker, timings);
- }
- remove_temp_files(output_base);
- if (run_output) {
- //NOTE(thebirk): This whole thing is a little leaky
- String complete_path = concatenate_strings(heap_allocator(), output_base, output_ext);
- complete_path = path_to_full_path(heap_allocator(), complete_path);
- return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
- }
- #endif
- }
- }
- return 0;
- }
|