main.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143
  1. // #define NO_ARRAY_BOUNDS_CHECK
  2. #include "common.cpp"
  3. #include "timings.cpp"
  4. #include "build_settings.cpp"
  5. #include "tokenizer.cpp"
  6. #include "big_int.cpp"
  7. #include "exact_value.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_BuildMode,
  169. BuildFlag_Debug,
  170. BuildFlag_CrossCompile,
  171. BuildFlag_CrossLibDir,
  172. BuildFlag_NoBoundsCheck,
  173. BuildFlag_NoCRT,
  174. BuildFlag_UseLLD,
  175. BuildFlag_Vet,
  176. BuildFlag_IgnoreUnknownAttributes,
  177. BuildFlag_COUNT,
  178. };
  179. enum BuildFlagParamKind {
  180. BuildFlagParam_None,
  181. BuildFlagParam_Boolean,
  182. BuildFlagParam_Integer,
  183. BuildFlagParam_Float,
  184. BuildFlagParam_String,
  185. BuildFlagParam_COUNT,
  186. };
  187. struct BuildFlag {
  188. BuildFlagKind kind;
  189. String name;
  190. BuildFlagParamKind param_kind;
  191. };
  192. void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) {
  193. BuildFlag flag = {kind, name, param_kind};
  194. array_add(build_flags, flag);
  195. }
  196. bool parse_build_flags(Array<String> args) {
  197. auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
  198. add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String);
  199. add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
  200. add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
  201. add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None);
  202. add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer);
  203. add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None);
  204. add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String);
  205. add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String);
  206. add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None);
  207. add_flag(&build_flags, BuildFlag_CrossCompile, str_lit("cross-compile"), BuildFlagParam_String);
  208. add_flag(&build_flags, BuildFlag_CrossLibDir, str_lit("cross-lib-dir"), BuildFlagParam_String);
  209. add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None);
  210. add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None);
  211. add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None);
  212. add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None);
  213. add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("-ignore-unknown-attributes"), BuildFlagParam_None);
  214. GB_ASSERT(args.count >= 3);
  215. Array<String> flag_args = array_slice(args, 3, args.count);
  216. bool set_flags[BuildFlag_COUNT] = {};
  217. bool bad_flags = false;
  218. for_array(i, flag_args) {
  219. String flag = flag_args[i];
  220. if (flag[0] != '-') {
  221. gb_printf_err("Invalid flag: %.*s\n", LIT(flag));
  222. continue;
  223. }
  224. String name = substring(flag, 1, flag.len);
  225. isize end = 0;
  226. for (; end < name.len; end++) {
  227. if (name[end] == '=') break;
  228. }
  229. name = substring(name, 0, end);
  230. String param = {};
  231. if (end < flag.len-1) param = substring(flag, 2+end, flag.len);
  232. bool found = false;
  233. for_array(build_flag_index, build_flags) {
  234. BuildFlag bf = build_flags[build_flag_index];
  235. if (bf.name == name) {
  236. found = true;
  237. if (set_flags[bf.kind]) {
  238. gb_printf_err("Previous flag set: '%.*s'\n", LIT(name));
  239. bad_flags = true;
  240. } else {
  241. ExactValue value = {};
  242. bool ok = false;
  243. if (bf.param_kind == BuildFlagParam_None) {
  244. if (param.len == 0) {
  245. ok = true;
  246. } else {
  247. gb_printf_err("Flag '%.*s' was not expecting a parameter '%.*s'\n", LIT(name), LIT(param));
  248. bad_flags = true;
  249. }
  250. } else if (param.len == 0) {
  251. gb_printf_err("Flag missing for '%.*s'\n", LIT(name));
  252. bad_flags = true;
  253. } else {
  254. ok = true;
  255. switch (bf.param_kind) {
  256. default: ok = false; break;
  257. case BuildFlagParam_Boolean: {
  258. if (str_eq_ignore_case(param, str_lit("t")) ||
  259. str_eq_ignore_case(param, str_lit("true")) ||
  260. param == "1") {
  261. value = exact_value_bool(true);
  262. } else if (str_eq_ignore_case(param, str_lit("f")) ||
  263. str_eq_ignore_case(param, str_lit("false")) ||
  264. param == "0") {
  265. value = exact_value_bool(false);
  266. } else {
  267. gb_printf_err("Invalid flag parameter for '%.*s' = '%.*s'\n", LIT(name), LIT(param));
  268. }
  269. } break;
  270. case BuildFlagParam_Integer:
  271. value = exact_value_integer_from_string(param);
  272. break;
  273. case BuildFlagParam_Float:
  274. value = exact_value_float_from_string(param);
  275. break;
  276. case BuildFlagParam_String: {
  277. value = exact_value_string(param);
  278. if (value.kind == ExactValue_String) {
  279. String s = value.value_string;
  280. if (s.len > 1 && s[0] == '"' && s[s.len-1] == '"') {
  281. value.value_string = substring(s, 1, s.len-1);
  282. }
  283. }
  284. break;
  285. }
  286. }
  287. }
  288. if (ok) {
  289. switch (bf.param_kind) {
  290. case BuildFlagParam_None:
  291. if (value.kind != ExactValue_Invalid) {
  292. gb_printf_err("%.*s expected no value, got %.*s", LIT(name), LIT(param));
  293. bad_flags = true;
  294. ok = false;
  295. }
  296. break;
  297. case BuildFlagParam_Boolean:
  298. if (value.kind != ExactValue_Bool) {
  299. gb_printf_err("%.*s expected a boolean, got %.*s", LIT(name), LIT(param));
  300. bad_flags = true;
  301. ok = false;
  302. }
  303. break;
  304. case BuildFlagParam_Integer:
  305. if (value.kind != ExactValue_Integer) {
  306. gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param));
  307. bad_flags = true;
  308. ok = false;
  309. }
  310. break;
  311. case BuildFlagParam_Float:
  312. if (value.kind != ExactValue_Float) {
  313. gb_printf_err("%.*s expected a floating pointer number, got %.*s", LIT(name), LIT(param));
  314. bad_flags = true;
  315. ok = false;
  316. }
  317. break;
  318. case BuildFlagParam_String:
  319. if (value.kind != ExactValue_String) {
  320. gb_printf_err("%.*s expected a string, got %.*s", LIT(name), LIT(param));
  321. bad_flags = true;
  322. ok = false;
  323. }
  324. break;
  325. }
  326. if (ok) switch (bf.kind) {
  327. case BuildFlag_OutFile: {
  328. GB_ASSERT(value.kind == ExactValue_String);
  329. String path = value.value_string;
  330. path = string_trim_whitespace(path);
  331. if (is_import_path_valid(path)) {
  332. #if defined(GB_SYSTEM_WINDOWS)
  333. String ext = path_extension(path);
  334. if (ext == ".exe") {
  335. path = substring(path, 0, string_extension_position(path));
  336. }
  337. #endif
  338. build_context.out_filepath = path;
  339. } else {
  340. gb_printf_err("Invalid -out path, got %.*s\n", LIT(path));
  341. bad_flags = true;
  342. }
  343. break;
  344. }
  345. case BuildFlag_ResourceFile: {
  346. GB_ASSERT(value.kind == ExactValue_String);
  347. String path = value.value_string;
  348. path = string_trim_whitespace(path);
  349. if (is_import_path_valid(path)) {
  350. if(!string_ends_with(path, str_lit(".rc"))) {
  351. gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
  352. bad_flags = true;
  353. break;
  354. }
  355. build_context.resource_filepath = substring(path, 0, string_extension_position(path));
  356. build_context.has_resource = true;
  357. } else {
  358. gb_printf_err("Invalid -resource path, got %.*s\n", LIT(path));
  359. bad_flags = true;
  360. }
  361. break;
  362. }
  363. case BuildFlag_OptimizationLevel:
  364. GB_ASSERT(value.kind == ExactValue_Integer);
  365. build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
  366. break;
  367. case BuildFlag_ShowTimings:
  368. GB_ASSERT(value.kind == ExactValue_Invalid);
  369. build_context.show_timings = true;
  370. break;
  371. case BuildFlag_ThreadCount: {
  372. GB_ASSERT(value.kind == ExactValue_Integer);
  373. isize count = cast(isize)big_int_to_i64(&value.value_integer);
  374. if (count <= 0) {
  375. gb_printf_err("%.*s expected a positive non-zero number, got %.*s\n", LIT(name), LIT(param));
  376. build_context.thread_count = 0;
  377. } else {
  378. build_context.thread_count = count;
  379. }
  380. break;
  381. }
  382. case BuildFlag_KeepTempFiles:
  383. GB_ASSERT(value.kind == ExactValue_Invalid);
  384. build_context.keep_temp_files = true;
  385. break;
  386. case BuildFlag_CrossCompile: {
  387. GB_ASSERT(value.kind == ExactValue_String);
  388. cross_compile_target = value.value_string;
  389. #if defined(GB_SYSTEM_UNIX) && defined(GB_ARCH_64_BIT)
  390. if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) {
  391. } else
  392. #endif
  393. {
  394. gb_printf_err("Unsupported cross compilation target '%.*s'\n", LIT(cross_compile_target));
  395. gb_printf_err("Currently supported targets: Essence (from 64-bit Unixes only)\n");
  396. bad_flags = true;
  397. }
  398. break;
  399. }
  400. case BuildFlag_CrossLibDir: {
  401. GB_ASSERT(value.kind == ExactValue_String);
  402. if (cross_compile_lib_dir.len) {
  403. gb_printf_err("Multiple cross compilation library directories\n");
  404. bad_flags = true;
  405. } else {
  406. cross_compile_lib_dir = concatenate_strings(heap_allocator(), str_lit("-L"), value.value_string);
  407. }
  408. break;
  409. }
  410. case BuildFlag_Collection: {
  411. GB_ASSERT(value.kind == ExactValue_String);
  412. String str = value.value_string;
  413. isize eq_pos = -1;
  414. for (isize i = 0; i < str.len; i++) {
  415. if (str[i] == '=') {
  416. eq_pos = i;
  417. break;
  418. }
  419. }
  420. if (eq_pos < 0) {
  421. gb_printf_err("Expected 'name=path', got '%.*s'\n", LIT(param));
  422. bad_flags = true;
  423. break;
  424. }
  425. String name = substring(str, 0, eq_pos);
  426. String path = substring(str, eq_pos+1, str.len);
  427. if (name.len == 0 || path.len == 0) {
  428. gb_printf_err("Expected 'name=path', got '%.*s'\n", LIT(param));
  429. bad_flags = true;
  430. break;
  431. }
  432. if (!string_is_valid_identifier(name)) {
  433. gb_printf_err("Library collection name '%.*s' must be a valid identifier\n", LIT(name));
  434. bad_flags = true;
  435. break;
  436. }
  437. if (name == "_") {
  438. gb_printf_err("Library collection name cannot be an underscore\n");
  439. bad_flags = true;
  440. break;
  441. }
  442. if (name == "system") {
  443. gb_printf_err("Library collection name 'system' is reserved\n");
  444. bad_flags = true;
  445. break;
  446. }
  447. String prev_path = {};
  448. bool found = find_library_collection_path(name, &prev_path);
  449. if (found) {
  450. gb_printf_err("Library collection '%.*s' already exists with path '%.*s'\n", LIT(name), LIT(prev_path));
  451. bad_flags = true;
  452. break;
  453. }
  454. gbAllocator a = heap_allocator();
  455. String fullpath = path_to_fullpath(a, path);
  456. if (!path_is_directory(fullpath)) {
  457. gb_printf_err("Library collection '%.*s' path must be a directory, got '%.*s'\n", LIT(name), LIT(fullpath));
  458. gb_free(a, fullpath.text);
  459. bad_flags = true;
  460. break;
  461. }
  462. add_library_collection(name, path);
  463. // NOTE(bill): Allow for multiple library collections
  464. continue;
  465. }
  466. case BuildFlag_BuildMode: {
  467. GB_ASSERT(value.kind == ExactValue_String);
  468. String str = value.value_string;
  469. if (build_context.command != "build") {
  470. gb_printf_err("'build-mode' can only be used with the 'build' command\n");
  471. bad_flags = true;
  472. break;
  473. }
  474. if (str == "dll") {
  475. build_context.is_dll = true;
  476. } else if (str == "exe") {
  477. build_context.is_dll = false;
  478. } else {
  479. gb_printf_err("Unknown build mode '%.*s'\n", LIT(str));
  480. bad_flags = true;
  481. break;
  482. }
  483. break;
  484. }
  485. case BuildFlag_Debug:
  486. build_context.ODIN_DEBUG = true;
  487. break;
  488. case BuildFlag_NoBoundsCheck:
  489. build_context.no_bounds_check = true;
  490. break;
  491. case BuildFlag_NoCRT:
  492. build_context.no_crt = true;
  493. break;
  494. case BuildFlag_UseLLD:
  495. build_context.use_lld = true;
  496. break;
  497. case BuildFlag_Vet:
  498. build_context.vet = true;
  499. break;
  500. case BuildFlag_IgnoreUnknownAttributes:
  501. build_context.ignore_unknown_attributes = true;
  502. break;
  503. }
  504. }
  505. set_flags[bf.kind] = ok;
  506. }
  507. break;
  508. }
  509. }
  510. if (!found) {
  511. gb_printf_err("Unknown flag: '%.*s'\n", LIT(name));
  512. bad_flags = true;
  513. }
  514. }
  515. return !bad_flags;
  516. }
  517. void show_timings(Checker *c, Timings *t) {
  518. Parser *p = c->parser;
  519. isize lines = p->total_line_count;
  520. isize tokens = p->total_token_count;
  521. isize files = 0;
  522. isize packages = p->packages.count;
  523. for_array(i, p->packages) {
  524. files += p->packages[i]->files.count;
  525. }
  526. #if 1
  527. timings_print_all(t);
  528. #else
  529. {
  530. timings_print_all(t);
  531. gb_printf("\n");
  532. gb_printf("Total Lines - %td\n", lines);
  533. gb_printf("Total Tokens - %td\n", tokens);
  534. gb_printf("Total Files - %td\n", files);
  535. gb_printf("Total Packages - %td\n", packages);
  536. gb_printf("\n");
  537. }
  538. {
  539. TimeStamp ts = t->sections[0];
  540. GB_ASSERT(ts.label == "parse files");
  541. f64 parse_time = time_stamp_as_s(ts, t->freq);
  542. gb_printf("Parse pass\n");
  543. gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
  544. gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
  545. gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
  546. gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
  547. gb_printf("\n");
  548. }
  549. {
  550. TimeStamp ts = t->sections[1];
  551. GB_ASSERT(ts.label == "type check");
  552. f64 parse_time = time_stamp_as_s(ts, t->freq);
  553. gb_printf("Checker pass\n");
  554. gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
  555. gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
  556. gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
  557. gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
  558. gb_printf("\n");
  559. }
  560. {
  561. f64 total_time = t->total_time_seconds;
  562. gb_printf("Total pass\n");
  563. gb_printf("LOC/s - %.3f\n", cast(f64)lines/total_time);
  564. gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
  565. gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
  566. gb_printf("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens);
  567. gb_printf("\n");
  568. }
  569. #endif
  570. }
  571. void remove_temp_files(String output_base) {
  572. if (build_context.keep_temp_files) return;
  573. auto data = array_make<u8>(heap_allocator(), output_base.len + 10);
  574. defer (array_free(&data));
  575. isize n = output_base.len;
  576. gb_memmove(data.data, output_base.text, n);
  577. #define EXT_REMOVE(s) do { \
  578. gb_memmove(data.data+n, s, gb_size_of(s)); \
  579. gb_file_remove(cast(char *)data.data); \
  580. } while (0)
  581. EXT_REMOVE(".ll");
  582. EXT_REMOVE(".bc");
  583. #if defined(GB_SYSTEM_WINDOWS)
  584. EXT_REMOVE(".obj");
  585. EXT_REMOVE(".res");
  586. #else
  587. EXT_REMOVE(".o");
  588. #endif
  589. #undef EXT_REMOVE
  590. }
  591. i32 exec_llvm_opt(String output_base) {
  592. #if defined(GB_SYSTEM_WINDOWS)
  593. // For more passes arguments: http://llvm.org/docs/Passes.html
  594. return system_exec_command_line_app("llvm-opt", false,
  595. "\"%.*sbin/opt\" \"%.*s.ll\" -o \"%.*s.bc\" %.*s "
  596. "",
  597. LIT(build_context.ODIN_ROOT),
  598. LIT(output_base), LIT(output_base),
  599. LIT(build_context.opt_flags));
  600. #else
  601. // NOTE(zangent): This is separate because it seems that LLVM tools are packaged
  602. // with the Windows version, while they will be system-provided on MacOS and GNU/Linux
  603. return system_exec_command_line_app("llvm-opt", false,
  604. "opt \"%.*s.ll\" -o \"%.*s.bc\" %.*s "
  605. "",
  606. LIT(output_base), LIT(output_base),
  607. LIT(build_context.opt_flags));
  608. #endif
  609. }
  610. i32 exec_llvm_llc(String output_base) {
  611. #if defined(GB_SYSTEM_WINDOWS)
  612. // For more arguments: http://llvm.org/docs/CommandGuide/llc.html
  613. return system_exec_command_line_app("llvm-llc", false,
  614. "\"%.*sbin\\llc\" \"%.*s.bc\" -filetype=obj -O%d "
  615. "-o \"%.*s.obj\" "
  616. "%.*s "
  617. "",
  618. LIT(build_context.ODIN_ROOT),
  619. LIT(output_base),
  620. build_context.optimization_level,
  621. LIT(output_base),
  622. LIT(build_context.llc_flags));
  623. #else
  624. // NOTE(zangent): Linux / Unix is unfinished and not tested very well.
  625. // For more arguments: http://llvm.org/docs/CommandGuide/llc.html
  626. return system_exec_command_line_app("llc", false,
  627. "llc \"%.*s.bc\" -filetype=obj -relocation-model=pic -O%d "
  628. "%.*s "
  629. "%s"
  630. "",
  631. LIT(output_base),
  632. build_context.optimization_level,
  633. LIT(build_context.llc_flags),
  634. str_eq_ignore_case(cross_compile_target, str_lit("Essence")) ? "-mtriple=x86_64-pc-none-elf" : "");
  635. #endif
  636. }
  637. int main(int arg_count, char **arg_ptr) {
  638. if (arg_count < 2) {
  639. usage(make_string_c(arg_ptr[0]));
  640. return 1;
  641. }
  642. Timings timings = {0};
  643. timings_init(&timings, str_lit("Total Time"), 128);
  644. defer (timings_destroy(&timings));
  645. init_string_buffer_memory();
  646. init_global_error_collector();
  647. global_big_int_init();
  648. arena_init(&global_ast_arena, heap_allocator());
  649. array_init(&library_collections, heap_allocator());
  650. // NOTE(bill): 'core' cannot be (re)defined by the user
  651. add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core")));
  652. Array<String> args = setup_args(arg_count, arg_ptr);
  653. String command = args[1];
  654. String init_filename = {};
  655. String run_args_string = {};
  656. bool run_output = false;
  657. if (command == "run") {
  658. if (args.count < 3) {
  659. usage(args[0]);
  660. return 1;
  661. }
  662. Array<String> run_args = array_make<String>(heap_allocator(), 0, arg_count);
  663. defer (array_free(&run_args));
  664. isize last_non_run_arg = args.count;
  665. for_array(i, args) {
  666. if (args[i] == "--") {
  667. last_non_run_arg = i;
  668. }
  669. if (i <= last_non_run_arg) {
  670. continue;
  671. }
  672. array_add(&run_args, args[i]);
  673. }
  674. args = array_slice(args, 0, last_non_run_arg);
  675. run_args_string = string_join_and_quote(heap_allocator(), run_args);
  676. init_filename = args[2];
  677. run_output = true;
  678. } else if (command == "build") {
  679. if (args.count < 3) {
  680. usage(args[0]);
  681. return 1;
  682. }
  683. init_filename = args[2];
  684. } else if (command == "check") {
  685. if (args.count < 3) {
  686. usage(args[0]);
  687. return 1;
  688. }
  689. build_context.no_output_files = true;
  690. init_filename = args[2];
  691. } else if (command == "docs") {
  692. if (args.count < 3) {
  693. usage(args[0]);
  694. return 1;
  695. }
  696. init_filename = args[2];
  697. build_context.generate_docs = true;
  698. #if 1
  699. print_usage_line(0, "Documentation generation is not yet supported");
  700. return 1;
  701. #endif
  702. } else if (command == "version") {
  703. gb_printf("%.*s version %.*s\n", LIT(args[0]), LIT(ODIN_VERSION));
  704. return 0;
  705. } else {
  706. usage(args[0]);
  707. return 1;
  708. }
  709. build_context.command = command;
  710. if (!parse_build_flags(args)) {
  711. return 1;
  712. }
  713. // NOTE(bill): add 'shared' directory if it is not already set
  714. if (!find_library_collection_path(str_lit("shared"), nullptr)) {
  715. add_library_collection(str_lit("shared"),
  716. get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
  717. }
  718. init_build_context();
  719. if (build_context.word_size == 4) {
  720. print_usage_line(0, "%s 32-bit is not yet supported", args[0]);
  721. return 1;
  722. }
  723. init_universal();
  724. // TODO(bill): prevent compiling without a linker
  725. timings_start_section(&timings, str_lit("parse files"));
  726. Parser parser = {0};
  727. if (!init_parser(&parser)) {
  728. return 1;
  729. }
  730. defer (destroy_parser(&parser));
  731. if (parse_packages(&parser, init_filename) != ParseFile_None) {
  732. return 1;
  733. }
  734. if (build_context.generate_docs) {
  735. // generate_documentation(&parser);
  736. return 0;
  737. }
  738. timings_start_section(&timings, str_lit("type check"));
  739. Checker checker = {0};
  740. init_checker(&checker, &parser);
  741. defer (destroy_checker(&checker));
  742. check_parsed_files(&checker);
  743. #if 1
  744. if (build_context.no_output_files) {
  745. if (build_context.show_timings) {
  746. show_timings(&checker, &timings);
  747. }
  748. if (global_error_collector.count != 0) {
  749. return 1;
  750. }
  751. return 0;
  752. }
  753. irGen ir_gen = {0};
  754. if (!ir_gen_init(&ir_gen, &checker)) {
  755. return 1;
  756. }
  757. // defer (ir_gen_destroy(&ir_gen));
  758. timings_start_section(&timings, str_lit("llvm ir gen"));
  759. ir_gen_tree(&ir_gen);
  760. timings_start_section(&timings, str_lit("llvm ir opt tree"));
  761. ir_opt_tree(&ir_gen);
  762. timings_start_section(&timings, str_lit("llvm ir print"));
  763. print_llvm_ir(&ir_gen);
  764. String output_name = ir_gen.output_name;
  765. String output_base = ir_gen.output_base;
  766. build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3);
  767. i32 exit_code = 0;
  768. timings_start_section(&timings, str_lit("llvm-opt"));
  769. exit_code = exec_llvm_opt(output_base);
  770. if (exit_code != 0) {
  771. return exit_code;
  772. }
  773. timings_start_section(&timings, str_lit("llvm-llc"));
  774. exit_code = exec_llvm_llc(output_base);
  775. if (exit_code != 0) {
  776. return exit_code;
  777. }
  778. #if defined(GB_SYSTEM_WINDOWS)
  779. timings_start_section(&timings, str_lit("msvc-link"));
  780. gbString lib_str = gb_string_make(heap_allocator(), "");
  781. defer (gb_string_free(lib_str));
  782. char lib_str_buf[1024] = {0};
  783. for_array(i, ir_gen.module.foreign_library_paths) {
  784. String lib = ir_gen.module.foreign_library_paths[i];
  785. GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
  786. isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
  787. " \"%.*s\"", LIT(lib));
  788. lib_str = gb_string_appendc(lib_str, lib_str_buf);
  789. }
  790. char *output_ext = "exe";
  791. gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
  792. defer (gb_string_free(link_settings));
  793. if (build_context.is_dll) {
  794. output_ext = "dll";
  795. link_settings = gb_string_append_fmt(link_settings, "/DLL");
  796. } else {
  797. link_settings = gb_string_append_fmt(link_settings, "/ENTRY:mainCRTStartup");
  798. }
  799. if (build_context.no_crt) {
  800. link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
  801. } else {
  802. link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
  803. }
  804. if (ir_gen.module.generate_debug_info) {
  805. link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
  806. }
  807. if (!build_context.use_lld) { // msvc
  808. if (build_context.has_resource) {
  809. exit_code = system_exec_command_line_app("msvc-link", true,
  810. "rc /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
  811. LIT(output_base),
  812. LIT(build_context.resource_filepath)
  813. );
  814. if (exit_code != 0) {
  815. return exit_code;
  816. }
  817. exit_code = system_exec_command_line_app("msvc-link", true,
  818. "link \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
  819. "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
  820. " %.*s "
  821. " %s "
  822. "",
  823. LIT(output_base), LIT(output_base), LIT(output_base), output_ext,
  824. lib_str, LIT(build_context.link_flags),
  825. link_settings
  826. );
  827. } else {
  828. exit_code = system_exec_command_line_app("msvc-link", true,
  829. "link \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
  830. "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
  831. " %.*s "
  832. " %s "
  833. "",
  834. LIT(output_base), LIT(output_base), output_ext,
  835. lib_str, LIT(build_context.link_flags),
  836. link_settings
  837. );
  838. }
  839. } else { // lld
  840. exit_code = system_exec_command_line_app("msvc-link", true,
  841. "\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
  842. "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
  843. " %.*s "
  844. " %s "
  845. "",
  846. LIT(build_context.ODIN_ROOT),
  847. LIT(output_base), LIT(output_base), output_ext,
  848. lib_str, LIT(build_context.link_flags),
  849. link_settings
  850. );
  851. }
  852. if (exit_code != 0) {
  853. return exit_code;
  854. }
  855. if (build_context.show_timings) {
  856. show_timings(&checker, &timings);
  857. }
  858. remove_temp_files(output_base);
  859. if (run_output) {
  860. system_exec_command_line_app("odin run", false, "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string));
  861. }
  862. #else
  863. timings_start_section(&timings, str_lit("ld-link"));
  864. // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
  865. char cwd[256];
  866. getcwd(&cwd[0], 256);
  867. //printf("%s\n", cwd);
  868. // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
  869. // files can be passed with -l:
  870. gbString lib_str = gb_string_make(heap_allocator(), "-L/");
  871. defer (gb_string_free(lib_str));
  872. for_array(i, ir_gen.module.foreign_library_paths) {
  873. String lib = ir_gen.module.foreign_library_paths[i];
  874. // NOTE(zangent): Sometimes, you have to use -framework on MacOS.
  875. // This allows you to specify '-f' in a #foreign_system_library,
  876. // without having to implement any new syntax specifically for MacOS.
  877. #if defined(GB_SYSTEM_OSX)
  878. if (lib.len > 2 && lib[0] == '-' && lib[1] == 'f') {
  879. // framework thingie
  880. lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", (int)(lib.len) - 2, lib.text + 2);
  881. } else if (string_ends_with(lib, str_lit(".a"))) {
  882. // static libs, absolute full path relative to the file in which the lib was imported from
  883. lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
  884. } else if (string_ends_with(lib, str_lit(".dylib"))) {
  885. // dynamic lib, relative path to executable
  886. lib_str = gb_string_append_fmt(lib_str, " -l:%s/%.*s ", cwd, LIT(lib));
  887. } else {
  888. // dynamic or static system lib, just link regularly searching system library paths
  889. lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
  890. }
  891. #else
  892. // NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
  893. // since those are statically linked to at link time. shared libraries (.so) has to be
  894. // available at runtime wherever the executable is run, so we make require those to be
  895. // local to the executable (unless the system collection is used, in which case we search
  896. // the system library paths for the library file).
  897. if (string_ends_with(lib, str_lit(".a"))) {
  898. // static libs, absolute full path relative to the file in which the lib was imported from
  899. lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
  900. } else if (string_ends_with(lib, str_lit(".so"))) {
  901. // dynamic lib, relative path to executable
  902. // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
  903. // at runtimeto the executable
  904. lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
  905. } else {
  906. // dynamic or static system lib, just link regularly searching system library paths
  907. lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
  908. }
  909. #endif
  910. }
  911. // Unlike the Win32 linker code, the output_ext includes the dot, because
  912. // typically executable files on *NIX systems don't have extensions.
  913. char *output_ext = "";
  914. char *link_settings = "";
  915. char *linker;
  916. if (build_context.is_dll) {
  917. // Shared libraries are .dylib on MacOS and .so on Linux.
  918. #if defined(GB_SYSTEM_OSX)
  919. output_ext = ".dylib";
  920. #else
  921. output_ext = ".so";
  922. #endif
  923. link_settings = "-shared";
  924. } else {
  925. // TODO: Do I need anything here?
  926. link_settings = "";
  927. }
  928. #if defined(GB_SYSTEM_OSX)
  929. linker = "ld";
  930. #else
  931. // TODO(zangent): Figure out how to make ld work on Linux.
  932. // It probably has to do with including the entire CRT, but
  933. // that's quite a complicated issue to solve while remaining distro-agnostic.
  934. // Clang can figure out linker flags for us, and that's good enough _for now_.
  935. if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) {
  936. 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";
  937. } else {
  938. linker = "clang -Wno-unused-command-line-argument";
  939. }
  940. #endif
  941. exit_code = system_exec_command_line_app("ld-link", true,
  942. "%s \"%.*s.o\" -o \"%.*s%s\" %s "
  943. " %s "
  944. " %.*s "
  945. " %s "
  946. " %.*s "
  947. #if defined(GB_SYSTEM_OSX)
  948. // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
  949. // NOTE: If you change this (although this minimum is as low as you can go with Odin working)
  950. // make sure to also change the 'mtriple' param passed to 'opt'
  951. " -macosx_version_min 10.8.0 "
  952. // This points the linker to where the entry point is
  953. " -e _main "
  954. #endif
  955. , linker, LIT(output_base), LIT(output_base), output_ext,
  956. lib_str,
  957. str_eq_ignore_case(cross_compile_target, str_lit("Essence")) ? "-lfreetype -lglue" : "-lc -lm",
  958. LIT(build_context.link_flags),
  959. link_settings,
  960. LIT(cross_compile_lib_dir)
  961. );
  962. if (exit_code != 0) {
  963. return exit_code;
  964. }
  965. #if defined(GB_SYSTEM_OSX)
  966. if (build_context.ODIN_DEBUG) {
  967. // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
  968. // to the symbols in the object file
  969. exit_code = system_exec_command_line_app("dsymutil", true,
  970. "dsymutil %.*s%s", LIT(output_base), output_ext
  971. );
  972. if (exit_code != 0) {
  973. return exit_code;
  974. }
  975. }
  976. #endif
  977. if (build_context.show_timings) {
  978. show_timings(&checker, &timings);
  979. }
  980. remove_temp_files(output_base);
  981. if (run_output) {
  982. output_base = path_to_full_path(heap_allocator(), output_base);
  983. system_exec_command_line_app("odin run", false, "\"%.*s\" %.*s", LIT(output_base), LIT(run_args_string));
  984. }
  985. #endif
  986. #endif
  987. return 0;
  988. }