main.cpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264
  1. // #define NO_ARRAY_BOUNDS_CHECK
  2. #include "common.cpp"
  3. #include "timings.cpp"
  4. #include "tokenizer.cpp"
  5. #include "big_int.cpp"
  6. #include "exact_value.cpp"
  7. #include "build_settings.cpp"
  8. #include "parser.hpp"
  9. #include "checker.hpp"
  10. #include "parser.cpp"
  11. #include "docs.cpp"
  12. #include "checker.cpp"
  13. #include "ir.cpp"
  14. #include "ir_opt.cpp"
  15. #include "ir_print.cpp"
  16. // NOTE(bill): 'name' is used in debugging and profiling modes
  17. i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
  18. #if defined(GB_SYSTEM_WINDOWS)
  19. STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
  20. PROCESS_INFORMATION pi = {0};
  21. char cmd_line[4*1024] = {0};
  22. isize cmd_len;
  23. va_list va;
  24. gbTempArenaMemory tmp;
  25. String16 cmd;
  26. i32 exit_code = 0;
  27. start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  28. start_info.wShowWindow = SW_SHOW;
  29. start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  30. start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  31. start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
  32. va_start(va, fmt);
  33. cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
  34. va_end(va);
  35. // gb_printf_err("%.*s\n", cast(int)cmd_len, cmd_line);
  36. tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
  37. defer (gb_temp_arena_memory_end(tmp));
  38. cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
  39. if (CreateProcessW(nullptr, cmd.text,
  40. nullptr, nullptr, true, 0, nullptr, nullptr,
  41. &start_info, &pi)) {
  42. WaitForSingleObject(pi.hProcess, INFINITE);
  43. GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
  44. CloseHandle(pi.hProcess);
  45. CloseHandle(pi.hThread);
  46. } else {
  47. // NOTE(bill): failed to create process
  48. gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
  49. exit_code = -1;
  50. }
  51. return exit_code;
  52. #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
  53. char cmd_line[4096] = {0};
  54. isize cmd_len;
  55. va_list va;
  56. String cmd;
  57. i32 exit_code = 0;
  58. va_start(va, fmt);
  59. cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
  60. va_end(va);
  61. cmd = make_string(cast(u8 *)&cmd_line, cmd_len-1);
  62. //printf("do: %s\n", cmd_line);
  63. exit_code = system(&cmd_line[0]);
  64. // pid_t pid = fork();
  65. // int status = 0;
  66. // if(pid == 0) {
  67. // // in child, pid == 0.
  68. // int ret = execvp(cmd.text, (char* const*) cmd.text);
  69. // if(ret == -1) {
  70. // gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
  71. // // we're in the child, so returning won't do us any good -- just quit.
  72. // exit(-1);
  73. // }
  74. // // unreachable
  75. // abort();
  76. // } else {
  77. // // wait for child to finish, then we can continue cleanup
  78. // int s = 0;
  79. // waitpid(pid, &s, 0);
  80. // status = WEXITSTATUS(s);
  81. // }
  82. // exit_code = status;
  83. return exit_code;
  84. #endif
  85. }
  86. Array<String> setup_args(int argc, char **argv) {
  87. gbAllocator a = heap_allocator();
  88. #if defined(GB_SYSTEM_WINDOWS)
  89. int wargc = 0;
  90. wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc);
  91. auto args = array_make<String>(a, 0, wargc);
  92. for (isize i = 0; i < wargc; i++) {
  93. wchar_t *warg = wargv[i];
  94. isize wlen = string16_len(warg);
  95. String16 wstr = make_string16(warg, wlen);
  96. String arg = string16_to_string(a, wstr);
  97. if (arg.len > 0) {
  98. array_add(&args, arg);
  99. }
  100. }
  101. return args;
  102. #else
  103. auto args = array_make<String>(a, 0, argc);
  104. for (isize i = 0; i < argc; i++) {
  105. String arg = make_string_c(argv[i]);
  106. if (arg.len > 0) {
  107. array_add(&args, arg);
  108. }
  109. }
  110. return args;
  111. #endif
  112. }
  113. void print_usage_line(i32 indent, char *fmt, ...) {
  114. while (indent --> 0) {
  115. gb_printf_err("\t");
  116. }
  117. va_list va;
  118. va_start(va, fmt);
  119. gb_printf_err_va(fmt, va);
  120. va_end(va);
  121. gb_printf_err("\n");
  122. }
  123. void usage(String argv0) {
  124. print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(argv0));
  125. print_usage_line(0, "Usage:");
  126. print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
  127. print_usage_line(0, "Commands:");
  128. print_usage_line(1, "build compile .odin file as executable");
  129. print_usage_line(1, "run compile and run .odin file");
  130. print_usage_line(1, "check parse and type check .odin file");
  131. print_usage_line(1, "docs generate documentation for a .odin file");
  132. print_usage_line(1, "version print version");
  133. }
  134. bool string_is_valid_identifier(String str) {
  135. if (str.len <= 0) return false;
  136. isize rune_count = 0;
  137. isize w = 0;
  138. isize offset = 0;
  139. while (offset < str.len) {
  140. Rune r = 0;
  141. w = gb_utf8_decode(str.text, str.len, &r);
  142. if (r == GB_RUNE_INVALID) {
  143. return false;
  144. }
  145. if (rune_count == 0) {
  146. if (!rune_is_letter(r)) {
  147. return false;
  148. }
  149. } else {
  150. if (!rune_is_letter(r) && !rune_is_digit(r)) {
  151. return false;
  152. }
  153. }
  154. rune_count += 1;
  155. offset += w;
  156. }
  157. return true;
  158. }
  159. enum BuildFlagKind {
  160. BuildFlag_Invalid,
  161. BuildFlag_OutFile,
  162. BuildFlag_ResourceFile,
  163. BuildFlag_OptimizationLevel,
  164. BuildFlag_ShowTimings,
  165. BuildFlag_ThreadCount,
  166. BuildFlag_KeepTempFiles,
  167. BuildFlag_Collection,
  168. BuildFlag_Define,
  169. BuildFlag_BuildMode,
  170. BuildFlag_Debug,
  171. BuildFlag_CrossCompile,
  172. BuildFlag_CrossLibDir,
  173. BuildFlag_NoBoundsCheck,
  174. BuildFlag_NoCRT,
  175. BuildFlag_UseLLD,
  176. BuildFlag_Vet,
  177. BuildFlag_IgnoreUnknownAttributes,
  178. BuildFlag_COUNT,
  179. };
  180. enum BuildFlagParamKind {
  181. BuildFlagParam_None,
  182. BuildFlagParam_Boolean,
  183. BuildFlagParam_Integer,
  184. BuildFlagParam_Float,
  185. BuildFlagParam_String,
  186. BuildFlagParam_COUNT,
  187. };
  188. struct BuildFlag {
  189. BuildFlagKind kind;
  190. String name;
  191. BuildFlagParamKind param_kind;
  192. };
  193. void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) {
  194. BuildFlag flag = {kind, name, param_kind};
  195. array_add(build_flags, flag);
  196. }
  197. ExactValue build_param_to_exact_value(String name, String param) {
  198. ExactValue value = {};
  199. if (str_eq_ignore_case(param, str_lit("t")) ||
  200. str_eq_ignore_case(param, str_lit("true"))) {
  201. value = exact_value_bool(true);
  202. } else if (str_eq_ignore_case(param, str_lit("f")) ||
  203. str_eq_ignore_case(param, str_lit("false"))) {
  204. value = exact_value_bool(false);
  205. } else if (param.len > 0) {
  206. if (param[0] == '"') {
  207. value = exact_value_string(param);
  208. if (value.kind == ExactValue_String) {
  209. String s = value.value_string;
  210. if (s.len > 1 && s[0] == '"' && s[s.len-1] == '"') {
  211. value.value_string = substring(s, 1, s.len-1);
  212. }
  213. }
  214. } else if (param[0] == '-' || param[0] == '+' || gb_is_between(param[0], '0', '9')) {
  215. if (string_contains_char(param, '.')) {
  216. value = exact_value_float_from_string(param);
  217. } else {
  218. value = exact_value_integer_from_string(param);
  219. }
  220. if (value.kind == ExactValue_Invalid) {
  221. gb_printf_err("Invalid flag parameter for '%.*s' = '%.*s'\n", LIT(name), LIT(param));
  222. }
  223. }
  224. } else {
  225. gb_printf_err("Invalid flag parameter for '%.*s' = '%.*s'\n", LIT(name), LIT(param));
  226. }
  227. return value;
  228. }
  229. bool parse_build_flags(Array<String> args) {
  230. auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
  231. add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String);
  232. add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
  233. add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
  234. add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None);
  235. add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer);
  236. add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None);
  237. add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String);
  238. add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String);
  239. add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String);
  240. add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None);
  241. add_flag(&build_flags, BuildFlag_CrossCompile, str_lit("cross-compile"), BuildFlagParam_String);
  242. add_flag(&build_flags, BuildFlag_CrossLibDir, str_lit("cross-lib-dir"), BuildFlagParam_String);
  243. add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None);
  244. add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None);
  245. add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None);
  246. add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None);
  247. add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("-ignore-unknown-attributes"), BuildFlagParam_None);
  248. GB_ASSERT(args.count >= 3);
  249. Array<String> flag_args = array_slice(args, 3, args.count);
  250. bool set_flags[BuildFlag_COUNT] = {};
  251. bool bad_flags = false;
  252. for_array(i, flag_args) {
  253. String flag = flag_args[i];
  254. if (flag[0] != '-') {
  255. gb_printf_err("Invalid flag: %.*s\n", LIT(flag));
  256. continue;
  257. }
  258. String name = substring(flag, 1, flag.len);
  259. isize end = 0;
  260. for (; end < name.len; end++) {
  261. if (name[end] == ':') break;
  262. if (name[end] == '=') break; // IMPORTANT TODO(bill): DEPRECATE THIS!!!!
  263. }
  264. name = substring(name, 0, end);
  265. String param = {};
  266. if (end < flag.len-1) param = substring(flag, 2+end, flag.len);
  267. bool found = false;
  268. for_array(build_flag_index, build_flags) {
  269. BuildFlag bf = build_flags[build_flag_index];
  270. if (bf.name == name) {
  271. found = true;
  272. if (set_flags[bf.kind]) {
  273. gb_printf_err("Previous flag set: '%.*s'\n", LIT(name));
  274. bad_flags = true;
  275. } else {
  276. ExactValue value = {};
  277. bool ok = false;
  278. if (bf.param_kind == BuildFlagParam_None) {
  279. if (param.len == 0) {
  280. ok = true;
  281. } else {
  282. gb_printf_err("Flag '%.*s' was not expecting a parameter '%.*s'\n", LIT(name), LIT(param));
  283. bad_flags = true;
  284. }
  285. } else if (param.len == 0) {
  286. gb_printf_err("Flag missing for '%.*s'\n", LIT(name));
  287. bad_flags = true;
  288. } else {
  289. ok = true;
  290. switch (bf.param_kind) {
  291. default:
  292. ok = false;
  293. break;
  294. case BuildFlagParam_Boolean: {
  295. if (str_eq_ignore_case(param, str_lit("t")) ||
  296. str_eq_ignore_case(param, str_lit("true")) ||
  297. param == "1") {
  298. value = exact_value_bool(true);
  299. } else if (str_eq_ignore_case(param, str_lit("f")) ||
  300. str_eq_ignore_case(param, str_lit("false")) ||
  301. param == "0") {
  302. value = exact_value_bool(false);
  303. } else {
  304. gb_printf_err("Invalid flag parameter for '%.*s' = '%.*s'\n", LIT(name), LIT(param));
  305. }
  306. } break;
  307. case BuildFlagParam_Integer:
  308. value = exact_value_integer_from_string(param);
  309. break;
  310. case BuildFlagParam_Float:
  311. value = exact_value_float_from_string(param);
  312. break;
  313. case BuildFlagParam_String: {
  314. value = exact_value_string(param);
  315. if (value.kind == ExactValue_String) {
  316. String s = value.value_string;
  317. if (s.len > 1 && s[0] == '"' && s[s.len-1] == '"') {
  318. value.value_string = substring(s, 1, s.len-1);
  319. }
  320. }
  321. break;
  322. }
  323. }
  324. }
  325. if (ok) {
  326. switch (bf.param_kind) {
  327. case BuildFlagParam_None:
  328. if (value.kind != ExactValue_Invalid) {
  329. gb_printf_err("%.*s expected no value, got %.*s", LIT(name), LIT(param));
  330. bad_flags = true;
  331. ok = false;
  332. }
  333. break;
  334. case BuildFlagParam_Boolean:
  335. if (value.kind != ExactValue_Bool) {
  336. gb_printf_err("%.*s expected a boolean, got %.*s", LIT(name), LIT(param));
  337. bad_flags = true;
  338. ok = false;
  339. }
  340. break;
  341. case BuildFlagParam_Integer:
  342. if (value.kind != ExactValue_Integer) {
  343. gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param));
  344. bad_flags = true;
  345. ok = false;
  346. }
  347. break;
  348. case BuildFlagParam_Float:
  349. if (value.kind != ExactValue_Float) {
  350. gb_printf_err("%.*s expected a floating pointer number, got %.*s", LIT(name), LIT(param));
  351. bad_flags = true;
  352. ok = false;
  353. }
  354. break;
  355. case BuildFlagParam_String:
  356. if (value.kind != ExactValue_String) {
  357. gb_printf_err("%.*s expected a string, got %.*s", LIT(name), LIT(param));
  358. bad_flags = true;
  359. ok = false;
  360. }
  361. break;
  362. }
  363. if (ok) switch (bf.kind) {
  364. case BuildFlag_OutFile: {
  365. GB_ASSERT(value.kind == ExactValue_String);
  366. String path = value.value_string;
  367. path = string_trim_whitespace(path);
  368. if (is_import_path_valid(path)) {
  369. #if defined(GB_SYSTEM_WINDOWS)
  370. String ext = path_extension(path);
  371. if (ext == ".exe") {
  372. path = substring(path, 0, string_extension_position(path));
  373. }
  374. #endif
  375. build_context.out_filepath = path;
  376. } else {
  377. gb_printf_err("Invalid -out path, got %.*s\n", LIT(path));
  378. bad_flags = true;
  379. }
  380. break;
  381. }
  382. case BuildFlag_ResourceFile: {
  383. GB_ASSERT(value.kind == ExactValue_String);
  384. String path = value.value_string;
  385. path = string_trim_whitespace(path);
  386. if (is_import_path_valid(path)) {
  387. if(!string_ends_with(path, str_lit(".rc"))) {
  388. gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
  389. bad_flags = true;
  390. break;
  391. }
  392. build_context.resource_filepath = substring(path, 0, string_extension_position(path));
  393. build_context.has_resource = true;
  394. } else {
  395. gb_printf_err("Invalid -resource path, got %.*s\n", LIT(path));
  396. bad_flags = true;
  397. }
  398. break;
  399. }
  400. case BuildFlag_OptimizationLevel:
  401. GB_ASSERT(value.kind == ExactValue_Integer);
  402. build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
  403. break;
  404. case BuildFlag_ShowTimings:
  405. GB_ASSERT(value.kind == ExactValue_Invalid);
  406. build_context.show_timings = true;
  407. break;
  408. case BuildFlag_ThreadCount: {
  409. GB_ASSERT(value.kind == ExactValue_Integer);
  410. isize count = cast(isize)big_int_to_i64(&value.value_integer);
  411. if (count <= 0) {
  412. gb_printf_err("%.*s expected a positive non-zero number, got %.*s\n", LIT(name), LIT(param));
  413. build_context.thread_count = 0;
  414. } else {
  415. build_context.thread_count = count;
  416. }
  417. break;
  418. }
  419. case BuildFlag_KeepTempFiles:
  420. GB_ASSERT(value.kind == ExactValue_Invalid);
  421. build_context.keep_temp_files = true;
  422. break;
  423. case BuildFlag_CrossCompile: {
  424. GB_ASSERT(value.kind == ExactValue_String);
  425. cross_compile_target = value.value_string;
  426. #if defined(GB_SYSTEM_UNIX) && defined(GB_ARCH_64_BIT)
  427. if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) {
  428. } else
  429. #endif
  430. {
  431. gb_printf_err("Unsupported cross compilation target '%.*s'\n", LIT(cross_compile_target));
  432. gb_printf_err("Currently supported targets: Essence (from 64-bit Unixes only)\n");
  433. bad_flags = true;
  434. }
  435. break;
  436. }
  437. case BuildFlag_CrossLibDir: {
  438. GB_ASSERT(value.kind == ExactValue_String);
  439. if (cross_compile_lib_dir.len) {
  440. gb_printf_err("Multiple cross compilation library directories\n");
  441. bad_flags = true;
  442. } else {
  443. cross_compile_lib_dir = concatenate_strings(heap_allocator(), str_lit("-L"), value.value_string);
  444. }
  445. break;
  446. }
  447. case BuildFlag_Collection: {
  448. GB_ASSERT(value.kind == ExactValue_String);
  449. String str = value.value_string;
  450. isize eq_pos = -1;
  451. for (isize i = 0; i < str.len; i++) {
  452. if (str[i] == '=') {
  453. eq_pos = i;
  454. break;
  455. }
  456. }
  457. if (eq_pos < 0) {
  458. gb_printf_err("Expected 'name=path', got '%.*s'\n", LIT(param));
  459. bad_flags = true;
  460. break;
  461. }
  462. String name = substring(str, 0, eq_pos);
  463. String path = substring(str, eq_pos+1, str.len);
  464. if (name.len == 0 || path.len == 0) {
  465. gb_printf_err("Expected 'name=path', got '%.*s'\n", LIT(param));
  466. bad_flags = true;
  467. break;
  468. }
  469. if (!string_is_valid_identifier(name)) {
  470. gb_printf_err("Library collection name '%.*s' must be a valid identifier\n", LIT(name));
  471. bad_flags = true;
  472. break;
  473. }
  474. if (name == "_") {
  475. gb_printf_err("Library collection name cannot be an underscore\n");
  476. bad_flags = true;
  477. break;
  478. }
  479. if (name == "system") {
  480. gb_printf_err("Library collection name 'system' is reserved\n");
  481. bad_flags = true;
  482. break;
  483. }
  484. String prev_path = {};
  485. bool found = find_library_collection_path(name, &prev_path);
  486. if (found) {
  487. gb_printf_err("Library collection '%.*s' already exists with path '%.*s'\n", LIT(name), LIT(prev_path));
  488. bad_flags = true;
  489. break;
  490. }
  491. gbAllocator a = heap_allocator();
  492. String fullpath = path_to_fullpath(a, path);
  493. if (!path_is_directory(fullpath)) {
  494. gb_printf_err("Library collection '%.*s' path must be a directory, got '%.*s'\n", LIT(name), LIT(fullpath));
  495. gb_free(a, fullpath.text);
  496. bad_flags = true;
  497. break;
  498. }
  499. add_library_collection(name, path);
  500. // NOTE(bill): Allow for multiple library collections
  501. continue;
  502. }
  503. case BuildFlag_Define: {
  504. GB_ASSERT(value.kind == ExactValue_String);
  505. String str = value.value_string;
  506. isize eq_pos = -1;
  507. for (isize i = 0; i < str.len; i++) {
  508. if (str[i] == '=') {
  509. eq_pos = i;
  510. break;
  511. }
  512. }
  513. if (eq_pos < 0) {
  514. gb_printf_err("Expected 'name=value', got '%.*s'\n", LIT(param));
  515. bad_flags = true;
  516. break;
  517. }
  518. String name = substring(str, 0, eq_pos);
  519. String value = substring(str, eq_pos+1, str.len);
  520. if (name.len == 0 || value.len == 0) {
  521. gb_printf_err("Expected 'name=value', got '%.*s'\n", LIT(param));
  522. bad_flags = true;
  523. break;
  524. }
  525. if (!string_is_valid_identifier(name)) {
  526. gb_printf_err("Defined constant name '%.*s' must be a valid identifier\n", LIT(name));
  527. bad_flags = true;
  528. break;
  529. }
  530. if (name == "_") {
  531. gb_printf_err("Defined constant name cannot be an underscore\n");
  532. bad_flags = true;
  533. break;
  534. }
  535. HashKey key = hash_string(name);
  536. if (map_get(&build_context.defined_values, key) != nullptr) {
  537. gb_printf_err("Defined constant '%.*s' already exists\n", LIT(name));
  538. bad_flags = true;
  539. break;
  540. }
  541. ExactValue v = build_param_to_exact_value(name, value);
  542. if (v.kind != ExactValue_Invalid) {
  543. map_set(&build_context.defined_values, key, v);
  544. } else {
  545. bad_flags = true;
  546. }
  547. break;
  548. }
  549. case BuildFlag_BuildMode: {
  550. GB_ASSERT(value.kind == ExactValue_String);
  551. String str = value.value_string;
  552. if (build_context.command != "build") {
  553. gb_printf_err("'build-mode' can only be used with the 'build' command\n");
  554. bad_flags = true;
  555. break;
  556. }
  557. if (str == "dll") {
  558. build_context.is_dll = true;
  559. } else if (str == "exe") {
  560. build_context.is_dll = false;
  561. } else {
  562. gb_printf_err("Unknown build mode '%.*s'\n", LIT(str));
  563. bad_flags = true;
  564. break;
  565. }
  566. break;
  567. }
  568. case BuildFlag_Debug:
  569. build_context.ODIN_DEBUG = true;
  570. break;
  571. case BuildFlag_NoBoundsCheck:
  572. build_context.no_bounds_check = true;
  573. break;
  574. case BuildFlag_NoCRT:
  575. build_context.no_crt = true;
  576. break;
  577. case BuildFlag_UseLLD:
  578. build_context.use_lld = true;
  579. break;
  580. case BuildFlag_Vet:
  581. build_context.vet = true;
  582. break;
  583. case BuildFlag_IgnoreUnknownAttributes:
  584. build_context.ignore_unknown_attributes = true;
  585. break;
  586. }
  587. }
  588. set_flags[bf.kind] = ok;
  589. }
  590. break;
  591. }
  592. }
  593. if (!found) {
  594. gb_printf_err("Unknown flag: '%.*s'\n", LIT(name));
  595. bad_flags = true;
  596. }
  597. }
  598. return !bad_flags;
  599. }
  600. void show_timings(Checker *c, Timings *t) {
  601. Parser *p = c->parser;
  602. isize lines = p->total_line_count;
  603. isize tokens = p->total_token_count;
  604. isize files = 0;
  605. isize packages = p->packages.count;
  606. isize total_file_size = 0;
  607. for_array(i, p->packages) {
  608. files += p->packages[i]->files.count;
  609. for_array(j, p->packages[i]->files) {
  610. AstFile *file = p->packages[i]->files[j];
  611. total_file_size += file->tokenizer.end - file->tokenizer.start;
  612. }
  613. }
  614. #if 1
  615. timings_print_all(t);
  616. #else
  617. {
  618. timings_print_all(t);
  619. gb_printf("\n");
  620. gb_printf("Total Lines - %td\n", lines);
  621. gb_printf("Total Tokens - %td\n", tokens);
  622. gb_printf("Total Files - %td\n", files);
  623. gb_printf("Total Packages - %td\n", packages);
  624. gb_printf("Total File Size - %td\n", total_file_size);
  625. gb_printf("\n");
  626. }
  627. {
  628. TimeStamp ts = t->sections[0];
  629. GB_ASSERT(ts.label == "parse files");
  630. f64 parse_time = time_stamp_as_s(ts, t->freq);
  631. gb_printf("Parse pass\n");
  632. gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
  633. gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
  634. gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
  635. gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
  636. gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
  637. gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
  638. gb_printf("\n");
  639. }
  640. {
  641. TimeStamp ts = t->sections[1];
  642. GB_ASSERT(ts.label == "type check");
  643. f64 parse_time = time_stamp_as_s(ts, t->freq);
  644. gb_printf("Checker pass\n");
  645. gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
  646. gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
  647. gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
  648. gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
  649. gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
  650. gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
  651. gb_printf("\n");
  652. }
  653. {
  654. f64 total_time = t->total_time_seconds;
  655. gb_printf("Total pass\n");
  656. gb_printf("LOC/s - %.3f\n", cast(f64)lines/total_time);
  657. gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
  658. gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
  659. gb_printf("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens);
  660. gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/total_time);
  661. gb_printf("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size);
  662. gb_printf("\n");
  663. }
  664. #endif
  665. }
  666. void remove_temp_files(String output_base) {
  667. if (build_context.keep_temp_files) return;
  668. auto data = array_make<u8>(heap_allocator(), output_base.len + 10);
  669. defer (array_free(&data));
  670. isize n = output_base.len;
  671. gb_memmove(data.data, output_base.text, n);
  672. #define EXT_REMOVE(s) do { \
  673. gb_memmove(data.data+n, s, gb_size_of(s)); \
  674. gb_file_remove(cast(char *)data.data); \
  675. } while (0)
  676. EXT_REMOVE(".ll");
  677. EXT_REMOVE(".bc");
  678. #if defined(GB_SYSTEM_WINDOWS)
  679. EXT_REMOVE(".obj");
  680. EXT_REMOVE(".res");
  681. #else
  682. EXT_REMOVE(".o");
  683. #endif
  684. #undef EXT_REMOVE
  685. }
  686. i32 exec_llvm_opt(String output_base) {
  687. #if defined(GB_SYSTEM_WINDOWS)
  688. // For more passes arguments: http://llvm.org/docs/Passes.html
  689. return system_exec_command_line_app("llvm-opt", false,
  690. "\"%.*sbin/opt\" \"%.*s.ll\" -o \"%.*s.bc\" %.*s "
  691. "",
  692. LIT(build_context.ODIN_ROOT),
  693. LIT(output_base), LIT(output_base),
  694. LIT(build_context.opt_flags));
  695. #else
  696. // NOTE(zangent): This is separate because it seems that LLVM tools are packaged
  697. // with the Windows version, while they will be system-provided on MacOS and GNU/Linux
  698. return system_exec_command_line_app("llvm-opt", false,
  699. "opt \"%.*s.ll\" -o \"%.*s.bc\" %.*s "
  700. "",
  701. LIT(output_base), LIT(output_base),
  702. LIT(build_context.opt_flags));
  703. #endif
  704. }
  705. i32 exec_llvm_llc(String output_base) {
  706. #if defined(GB_SYSTEM_WINDOWS)
  707. // For more arguments: http://llvm.org/docs/CommandGuide/llc.html
  708. return system_exec_command_line_app("llvm-llc", false,
  709. "\"%.*sbin\\llc\" \"%.*s.bc\" -filetype=obj -O%d "
  710. "-o \"%.*s.obj\" "
  711. "%.*s "
  712. "",
  713. LIT(build_context.ODIN_ROOT),
  714. LIT(output_base),
  715. build_context.optimization_level,
  716. LIT(output_base),
  717. LIT(build_context.llc_flags));
  718. #else
  719. // NOTE(zangent): Linux / Unix is unfinished and not tested very well.
  720. // For more arguments: http://llvm.org/docs/CommandGuide/llc.html
  721. return system_exec_command_line_app("llc", false,
  722. "llc \"%.*s.bc\" -filetype=obj -relocation-model=pic -O%d "
  723. "%.*s "
  724. "%s"
  725. "",
  726. LIT(output_base),
  727. build_context.optimization_level,
  728. LIT(build_context.llc_flags),
  729. str_eq_ignore_case(cross_compile_target, str_lit("Essence")) ? "-mtriple=x86_64-pc-none-elf" : "");
  730. #endif
  731. }
  732. int main(int arg_count, char **arg_ptr) {
  733. if (arg_count < 2) {
  734. usage(make_string_c(arg_ptr[0]));
  735. return 1;
  736. }
  737. Timings timings = {0};
  738. timings_init(&timings, str_lit("Total Time"), 128);
  739. defer (timings_destroy(&timings));
  740. init_string_buffer_memory();
  741. init_global_error_collector();
  742. global_big_int_init();
  743. arena_init(&global_ast_arena, heap_allocator());
  744. array_init(&library_collections, heap_allocator());
  745. // NOTE(bill): 'core' cannot be (re)defined by the user
  746. add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core")));
  747. map_init(&build_context.defined_values, heap_allocator());
  748. Array<String> args = setup_args(arg_count, arg_ptr);
  749. String command = args[1];
  750. String init_filename = {};
  751. String run_args_string = {};
  752. bool run_output = false;
  753. if (command == "run") {
  754. if (args.count < 3) {
  755. usage(args[0]);
  756. return 1;
  757. }
  758. Array<String> run_args = array_make<String>(heap_allocator(), 0, arg_count);
  759. defer (array_free(&run_args));
  760. isize last_non_run_arg = args.count;
  761. for_array(i, args) {
  762. if (args[i] == "--") {
  763. last_non_run_arg = i;
  764. }
  765. if (i <= last_non_run_arg) {
  766. continue;
  767. }
  768. array_add(&run_args, args[i]);
  769. }
  770. args = array_slice(args, 0, last_non_run_arg);
  771. run_args_string = string_join_and_quote(heap_allocator(), run_args);
  772. init_filename = args[2];
  773. run_output = true;
  774. } else if (command == "build") {
  775. if (args.count < 3) {
  776. usage(args[0]);
  777. return 1;
  778. }
  779. init_filename = args[2];
  780. } else if (command == "check") {
  781. if (args.count < 3) {
  782. usage(args[0]);
  783. return 1;
  784. }
  785. build_context.no_output_files = true;
  786. init_filename = args[2];
  787. } else if (command == "docs") {
  788. if (args.count < 3) {
  789. usage(args[0]);
  790. return 1;
  791. }
  792. init_filename = args[2];
  793. build_context.generate_docs = true;
  794. #if 1
  795. print_usage_line(0, "Documentation generation is not yet supported");
  796. return 1;
  797. #endif
  798. } else if (command == "version") {
  799. gb_printf("%.*s version %.*s\n", LIT(args[0]), LIT(ODIN_VERSION));
  800. return 0;
  801. } else {
  802. usage(args[0]);
  803. return 1;
  804. }
  805. build_context.command = command;
  806. if (!parse_build_flags(args)) {
  807. return 1;
  808. }
  809. // NOTE(bill): add 'shared' directory if it is not already set
  810. if (!find_library_collection_path(str_lit("shared"), nullptr)) {
  811. add_library_collection(str_lit("shared"),
  812. get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
  813. }
  814. init_build_context();
  815. if (build_context.word_size == 4) {
  816. print_usage_line(0, "%s 32-bit is not yet supported", args[0]);
  817. return 1;
  818. }
  819. init_universal();
  820. // TODO(bill): prevent compiling without a linker
  821. timings_start_section(&timings, str_lit("parse files"));
  822. Parser parser = {0};
  823. if (!init_parser(&parser)) {
  824. return 1;
  825. }
  826. defer (destroy_parser(&parser));
  827. if (parse_packages(&parser, init_filename) != ParseFile_None) {
  828. return 1;
  829. }
  830. if (build_context.generate_docs) {
  831. // generate_documentation(&parser);
  832. return 0;
  833. }
  834. timings_start_section(&timings, str_lit("type check"));
  835. Checker checker = {0};
  836. init_checker(&checker, &parser);
  837. defer (destroy_checker(&checker));
  838. check_parsed_files(&checker);
  839. #if 1
  840. if (build_context.no_output_files) {
  841. if (build_context.show_timings) {
  842. show_timings(&checker, &timings);
  843. }
  844. if (global_error_collector.count != 0) {
  845. return 1;
  846. }
  847. return 0;
  848. }
  849. irGen ir_gen = {0};
  850. if (!ir_gen_init(&ir_gen, &checker)) {
  851. return 1;
  852. }
  853. // defer (ir_gen_destroy(&ir_gen));
  854. timings_start_section(&timings, str_lit("llvm ir gen"));
  855. ir_gen_tree(&ir_gen);
  856. timings_start_section(&timings, str_lit("llvm ir opt tree"));
  857. ir_opt_tree(&ir_gen);
  858. timings_start_section(&timings, str_lit("llvm ir print"));
  859. print_llvm_ir(&ir_gen);
  860. String output_name = ir_gen.output_name;
  861. String output_base = ir_gen.output_base;
  862. build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3);
  863. i32 exit_code = 0;
  864. timings_start_section(&timings, str_lit("llvm-opt"));
  865. exit_code = exec_llvm_opt(output_base);
  866. if (exit_code != 0) {
  867. return exit_code;
  868. }
  869. timings_start_section(&timings, str_lit("llvm-llc"));
  870. exit_code = exec_llvm_llc(output_base);
  871. if (exit_code != 0) {
  872. return exit_code;
  873. }
  874. #if defined(GB_SYSTEM_WINDOWS)
  875. timings_start_section(&timings, str_lit("msvc-link"));
  876. gbString lib_str = gb_string_make(heap_allocator(), "");
  877. defer (gb_string_free(lib_str));
  878. char lib_str_buf[1024] = {0};
  879. for_array(i, ir_gen.module.foreign_library_paths) {
  880. String lib = ir_gen.module.foreign_library_paths[i];
  881. GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
  882. isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
  883. " \"%.*s\"", LIT(lib));
  884. lib_str = gb_string_appendc(lib_str, lib_str_buf);
  885. }
  886. char *output_ext = "exe";
  887. gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
  888. defer (gb_string_free(link_settings));
  889. if (build_context.is_dll) {
  890. output_ext = "dll";
  891. link_settings = gb_string_append_fmt(link_settings, "/DLL");
  892. } else {
  893. link_settings = gb_string_append_fmt(link_settings, "/ENTRY:mainCRTStartup");
  894. }
  895. if (build_context.no_crt) {
  896. link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
  897. } else {
  898. link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
  899. }
  900. if (ir_gen.module.generate_debug_info) {
  901. link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
  902. }
  903. if (!build_context.use_lld) { // msvc
  904. if (build_context.has_resource) {
  905. exit_code = system_exec_command_line_app("msvc-link", true,
  906. "rc /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
  907. LIT(output_base),
  908. LIT(build_context.resource_filepath)
  909. );
  910. if (exit_code != 0) {
  911. return exit_code;
  912. }
  913. exit_code = system_exec_command_line_app("msvc-link", true,
  914. "link \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
  915. "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
  916. " %.*s "
  917. " %s "
  918. "",
  919. LIT(output_base), LIT(output_base), LIT(output_base), output_ext,
  920. lib_str, LIT(build_context.link_flags),
  921. link_settings
  922. );
  923. } else {
  924. exit_code = system_exec_command_line_app("msvc-link", true,
  925. "link \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
  926. "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
  927. " %.*s "
  928. " %s "
  929. "",
  930. LIT(output_base), LIT(output_base), output_ext,
  931. lib_str, LIT(build_context.link_flags),
  932. link_settings
  933. );
  934. }
  935. } else { // lld
  936. exit_code = system_exec_command_line_app("msvc-link", true,
  937. "\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
  938. "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
  939. " %.*s "
  940. " %s "
  941. "",
  942. LIT(build_context.ODIN_ROOT),
  943. LIT(output_base), LIT(output_base), output_ext,
  944. lib_str, LIT(build_context.link_flags),
  945. link_settings
  946. );
  947. }
  948. if (exit_code != 0) {
  949. return exit_code;
  950. }
  951. if (build_context.show_timings) {
  952. show_timings(&checker, &timings);
  953. }
  954. remove_temp_files(output_base);
  955. if (run_output) {
  956. system_exec_command_line_app("odin run", false, "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string));
  957. }
  958. #else
  959. timings_start_section(&timings, str_lit("ld-link"));
  960. // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
  961. char cwd[256];
  962. getcwd(&cwd[0], 256);
  963. //printf("%s\n", cwd);
  964. // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
  965. // files can be passed with -l:
  966. gbString lib_str = gb_string_make(heap_allocator(), "-L/");
  967. defer (gb_string_free(lib_str));
  968. for_array(i, ir_gen.module.foreign_library_paths) {
  969. String lib = ir_gen.module.foreign_library_paths[i];
  970. // NOTE(zangent): Sometimes, you have to use -framework on MacOS.
  971. // This allows you to specify '-f' in a #foreign_system_library,
  972. // without having to implement any new syntax specifically for MacOS.
  973. #if defined(GB_SYSTEM_OSX)
  974. if (lib.len > 2 && lib[0] == '-' && lib[1] == 'f') {
  975. // framework thingie
  976. lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", (int)(lib.len) - 2, lib.text + 2);
  977. } else if (string_ends_with(lib, str_lit(".a"))) {
  978. // static libs, absolute full path relative to the file in which the lib was imported from
  979. lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
  980. } else if (string_ends_with(lib, str_lit(".dylib"))) {
  981. // dynamic lib, relative path to executable
  982. lib_str = gb_string_append_fmt(lib_str, " -l:%s/%.*s ", cwd, LIT(lib));
  983. } else {
  984. // dynamic or static system lib, just link regularly searching system library paths
  985. lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
  986. }
  987. #else
  988. // NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
  989. // since those are statically linked to at link time. shared libraries (.so) has to be
  990. // available at runtime wherever the executable is run, so we make require those to be
  991. // local to the executable (unless the system collection is used, in which case we search
  992. // the system library paths for the library file).
  993. if (string_ends_with(lib, str_lit(".a"))) {
  994. // static libs, absolute full path relative to the file in which the lib was imported from
  995. lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
  996. } else if (string_ends_with(lib, str_lit(".so"))) {
  997. // dynamic lib, relative path to executable
  998. // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
  999. // at runtimeto the executable
  1000. lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
  1001. } else {
  1002. // dynamic or static system lib, just link regularly searching system library paths
  1003. lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
  1004. }
  1005. #endif
  1006. }
  1007. // Unlike the Win32 linker code, the output_ext includes the dot, because
  1008. // typically executable files on *NIX systems don't have extensions.
  1009. String output_ext = {};
  1010. char *link_settings = "";
  1011. char *linker;
  1012. if (build_context.is_dll) {
  1013. // Shared libraries are .dylib on MacOS and .so on Linux.
  1014. #if defined(GB_SYSTEM_OSX)
  1015. output_ext = STR_LIT(".dylib");
  1016. #else
  1017. output_ext = STR_LIT(".so");
  1018. #endif
  1019. link_settings = "-shared";
  1020. } else {
  1021. // TODO: Do I need anything here?
  1022. link_settings = "";
  1023. }
  1024. if (build_context.out_filepath.len > 0) {
  1025. //NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
  1026. isize pos = string_extension_position(build_context.out_filepath);
  1027. if (pos > 0) {
  1028. output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
  1029. }
  1030. }
  1031. #if defined(GB_SYSTEM_OSX)
  1032. linker = "ld";
  1033. #else
  1034. // TODO(zangent): Figure out how to make ld work on Linux.
  1035. // It probably has to do with including the entire CRT, but
  1036. // that's quite a complicated issue to solve while remaining distro-agnostic.
  1037. // Clang can figure out linker flags for us, and that's good enough _for now_.
  1038. if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) {
  1039. linker = "x86_64-elf-gcc -T core/sys/essence_linker_userland64.ld -ffreestanding -nostdlib -lgcc -g -z max-page-size=0x1000 -Wno-unused-command-line-argument";
  1040. } else {
  1041. linker = "clang -Wno-unused-command-line-argument";
  1042. }
  1043. #endif
  1044. exit_code = system_exec_command_line_app("ld-link", true,
  1045. "%s \"%.*s.o\" -o \"%.*s%.*s\" %s "
  1046. " %s "
  1047. " %.*s "
  1048. " %s "
  1049. " %.*s "
  1050. #if defined(GB_SYSTEM_OSX)
  1051. // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
  1052. // NOTE: If you change this (although this minimum is as low as you can go with Odin working)
  1053. // make sure to also change the 'mtriple' param passed to 'opt'
  1054. " -macosx_version_min 10.8.0 "
  1055. // This points the linker to where the entry point is
  1056. " -e _main "
  1057. #endif
  1058. , linker, LIT(output_base), LIT(output_base), LIT(output_ext),
  1059. lib_str,
  1060. str_eq_ignore_case(cross_compile_target, str_lit("Essence")) ? "-lfreetype -lglue" : "-lc -lm",
  1061. LIT(build_context.link_flags),
  1062. link_settings,
  1063. LIT(cross_compile_lib_dir)
  1064. );
  1065. if (exit_code != 0) {
  1066. return exit_code;
  1067. }
  1068. #if defined(GB_SYSTEM_OSX)
  1069. if (build_context.ODIN_DEBUG) {
  1070. // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
  1071. // to the symbols in the object file
  1072. exit_code = system_exec_command_line_app("dsymutil", true,
  1073. "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
  1074. );
  1075. if (exit_code != 0) {
  1076. return exit_code;
  1077. }
  1078. }
  1079. #endif
  1080. if (build_context.show_timings) {
  1081. show_timings(&checker, &timings);
  1082. }
  1083. remove_temp_files(output_base);
  1084. if (run_output) {
  1085. //NOTE(thebirk): This whole thing is a little leaky
  1086. String complete_path = concatenate_strings(heap_allocator(), output_base, output_ext);
  1087. complete_path = path_to_full_path(heap_allocator(), complete_path);
  1088. system_exec_command_line_app("odin run", false, "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
  1089. }
  1090. #endif
  1091. #endif
  1092. return 0;
  1093. }