build_settings.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. enum TargetOsKind {
  2. TargetOs_Invalid,
  3. TargetOs_windows,
  4. TargetOs_osx,
  5. TargetOs_linux,
  6. TargetOs_essence,
  7. TargetOs_COUNT,
  8. };
  9. enum TargetArchKind {
  10. TargetArch_Invalid,
  11. TargetArch_amd64,
  12. TargetArch_386,
  13. TargetArch_COUNT,
  14. };
  15. enum TargetEndianKind {
  16. TargetEndian_Invalid,
  17. TargetEndian_Little,
  18. TargetEndian_Big,
  19. TargetEndian_COUNT,
  20. };
  21. String target_os_names[TargetOs_COUNT] = {
  22. str_lit(""),
  23. str_lit("windows"),
  24. str_lit("osx"),
  25. str_lit("linux"),
  26. str_lit("essence"),
  27. };
  28. String target_arch_names[TargetArch_COUNT] = {
  29. str_lit(""),
  30. str_lit("amd64"),
  31. str_lit("386"),
  32. };
  33. String target_endian_names[TargetEndian_COUNT] = {
  34. str_lit(""),
  35. str_lit("little"),
  36. str_lit("big"),
  37. };
  38. TargetEndianKind target_endians[TargetArch_COUNT] = {
  39. TargetEndian_Invalid,
  40. TargetEndian_Little,
  41. TargetEndian_Little,
  42. };
  43. String const ODIN_VERSION = str_lit("0.9.2");
  44. String cross_compile_target = str_lit("");
  45. String cross_compile_lib_dir = str_lit("");
  46. struct TargetMetrics {
  47. TargetOsKind os;
  48. TargetArchKind arch;
  49. isize word_size;
  50. isize max_align;
  51. };
  52. // This stores the information for the specify architecture of this build
  53. struct BuildContext {
  54. // Constants
  55. String ODIN_OS; // target operating system
  56. String ODIN_ARCH; // target architecture
  57. String ODIN_ENDIAN; // target endian
  58. String ODIN_VENDOR; // compiler vendor
  59. String ODIN_VERSION; // compiler version
  60. String ODIN_ROOT; // Odin ROOT
  61. bool ODIN_DEBUG; // Odin in debug mode
  62. TargetEndianKind endian_kind;
  63. // In bytes
  64. i64 word_size; // Size of a pointer, must be >= 4
  65. i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
  66. String command;
  67. TargetMetrics metrics;
  68. String out_filepath;
  69. String resource_filepath;
  70. String pdb_filepath;
  71. bool has_resource;
  72. String opt_flags;
  73. String llc_flags;
  74. String link_flags;
  75. bool is_dll;
  76. bool generate_docs;
  77. i32 optimization_level;
  78. bool show_timings;
  79. bool keep_temp_files;
  80. bool ignore_unknown_attributes;
  81. bool no_bounds_check;
  82. bool no_output_files;
  83. bool print_query_data;
  84. bool no_crt;
  85. bool use_lld;
  86. bool vet;
  87. gbAffinity affinity;
  88. isize thread_count;
  89. Map<ExactValue> defined_values; // Key:
  90. };
  91. gb_global BuildContext build_context = {0};
  92. gb_global TargetMetrics target_windows_386 = {
  93. TargetOs_windows,
  94. TargetArch_386,
  95. 4,
  96. 8,
  97. };
  98. gb_global TargetMetrics target_windows_amd64 = {
  99. TargetOs_windows,
  100. TargetArch_amd64,
  101. 8,
  102. 16,
  103. };
  104. gb_global TargetMetrics target_linux_386 = {
  105. TargetOs_linux,
  106. TargetArch_386,
  107. 4,
  108. 8,
  109. };
  110. gb_global TargetMetrics target_linux_amd64 = {
  111. TargetOs_linux,
  112. TargetArch_amd64,
  113. 8,
  114. 16,
  115. };
  116. gb_global TargetMetrics target_osx_amd64 = {
  117. TargetOs_osx,
  118. TargetArch_amd64,
  119. 8,
  120. 16,
  121. };
  122. TargetOsKind get_target_os_from_string(String str) {
  123. for (isize i = 0; i < TargetOs_COUNT; i++) {
  124. if (str_eq_ignore_case(target_os_names[i], str)) {
  125. return cast(TargetOsKind)i;
  126. }
  127. }
  128. return TargetOs_Invalid;
  129. }
  130. TargetArchKind get_target_arch_from_string(String str) {
  131. for (isize i = 0; i < TargetArch_COUNT; i++) {
  132. if (str_eq_ignore_case(target_arch_names[i], str)) {
  133. return cast(TargetArchKind)i;
  134. }
  135. }
  136. return TargetArch_Invalid;
  137. }
  138. bool is_excluded_target_filename(String name) {
  139. String const ext = str_lit(".odin");
  140. String original_name = name;
  141. GB_ASSERT(string_ends_with(name, ext));
  142. name = substring(name, 0, name.len-ext.len);
  143. String str1 = {};
  144. String str2 = {};
  145. isize n = 0;
  146. str1 = name;
  147. n = str1.len;
  148. for (isize i = str1.len-1; i >= 0 && str1[i] != '_'; i--) {
  149. n -= 1;
  150. }
  151. str1 = substring(str1, n, str1.len);
  152. str2 = substring(name, 0, gb_max(n-1, 0));
  153. n = str2.len;
  154. for (isize i = str2.len-1; i >= 0 && str2[i] != '_'; i--) {
  155. n -= 1;
  156. }
  157. str2 = substring(str2, n, str2.len);
  158. if (str1 == name) {
  159. return false;
  160. }
  161. TargetOsKind os1 = get_target_os_from_string(str1);
  162. TargetArchKind arch1 = get_target_arch_from_string(str1);
  163. TargetOsKind os2 = get_target_os_from_string(str2);
  164. TargetArchKind arch2 = get_target_arch_from_string(str2);
  165. if (os1 != TargetOs_Invalid && arch2 != TargetArch_Invalid) {
  166. return os1 != build_context.metrics.os || arch2 != build_context.metrics.arch;
  167. } else if (arch1 != TargetArch_Invalid && os2 != TargetOs_Invalid) {
  168. return arch1 != build_context.metrics.arch || os2 != build_context.metrics.os;
  169. } else if (os1 != TargetOs_Invalid) {
  170. return os1 != build_context.metrics.os;
  171. } else if (arch1 != TargetArch_Invalid) {
  172. return arch1 != build_context.metrics.arch;
  173. }
  174. return false;
  175. }
  176. struct LibraryCollections {
  177. String name;
  178. String path;
  179. };
  180. gb_global Array<LibraryCollections> library_collections = {0};
  181. void add_library_collection(String name, String path) {
  182. // TODO(bill): Check the path is valid and a directory
  183. LibraryCollections lc = {name, string_trim_whitespace(path)};
  184. array_add(&library_collections, lc);
  185. }
  186. bool find_library_collection_path(String name, String *path) {
  187. for_array(i, library_collections) {
  188. if (library_collections[i].name == name) {
  189. if (path) *path = library_collections[i].path;
  190. return true;
  191. }
  192. }
  193. return false;
  194. }
  195. // TODO(bill): OS dependent versions for the BuildContext
  196. // join_path
  197. // is_dir
  198. // is_file
  199. // is_abs_path
  200. // has_subdir
  201. String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
  202. String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
  203. #if defined(GB_SYSTEM_WINDOWS)
  204. String odin_root_dir(void) {
  205. String path = global_module_path;
  206. isize len, i;
  207. gbTempArenaMemory tmp;
  208. wchar_t *text;
  209. if (global_module_path_set) {
  210. return global_module_path;
  211. }
  212. auto path_buf = array_make<wchar_t>(heap_allocator(), 300);
  213. len = 0;
  214. for (;;) {
  215. len = GetModuleFileNameW(nullptr, &path_buf[0], cast(int)path_buf.count);
  216. if (len == 0) {
  217. return make_string(nullptr, 0);
  218. }
  219. if (len < path_buf.count) {
  220. break;
  221. }
  222. array_resize(&path_buf, 2*path_buf.count + 300);
  223. }
  224. len += 1; // NOTE(bill): It needs an extra 1 for some reason
  225. gb_mutex_lock(&string_buffer_mutex);
  226. defer (gb_mutex_unlock(&string_buffer_mutex));
  227. tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
  228. defer (gb_temp_arena_memory_end(tmp));
  229. text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
  230. GetModuleFileNameW(nullptr, text, cast(int)len);
  231. path = string16_to_string(heap_allocator(), make_string16(text, len));
  232. for (i = path.len-1; i >= 0; i--) {
  233. u8 c = path[i];
  234. if (c == '/' || c == '\\') {
  235. break;
  236. }
  237. path.len--;
  238. }
  239. global_module_path = path;
  240. global_module_path_set = true;
  241. array_free(&path_buf);
  242. return path;
  243. }
  244. #elif defined(GB_SYSTEM_OSX)
  245. #include <mach-o/dyld.h>
  246. String odin_root_dir(void) {
  247. String path = global_module_path;
  248. isize len, i;
  249. gbTempArenaMemory tmp;
  250. u8 *text;
  251. if (global_module_path_set) {
  252. return global_module_path;
  253. }
  254. auto path_buf = array_make<char>(heap_allocator(), 300);
  255. len = 0;
  256. for (;;) {
  257. u32 sz = path_buf.count;
  258. int res = _NSGetExecutablePath(&path_buf[0], &sz);
  259. if(res == 0) {
  260. len = sz;
  261. break;
  262. } else {
  263. array_resize(&path_buf, sz + 1);
  264. }
  265. }
  266. gb_mutex_lock(&string_buffer_mutex);
  267. defer (gb_mutex_unlock(&string_buffer_mutex));
  268. tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
  269. defer (gb_temp_arena_memory_end(tmp));
  270. text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
  271. gb_memmove(text, &path_buf[0], len);
  272. path = make_string(text, len);
  273. for (i = path.len-1; i >= 0; i--) {
  274. u8 c = path[i];
  275. if (c == '/' || c == '\\') {
  276. break;
  277. }
  278. path.len--;
  279. }
  280. global_module_path = path;
  281. global_module_path_set = true;
  282. // array_free(&path_buf);
  283. return path;
  284. }
  285. #else
  286. // NOTE: Linux / Unix is unfinished and not tested very well.
  287. #include <sys/stat.h>
  288. String odin_root_dir(void) {
  289. String path = global_module_path;
  290. isize len, i;
  291. gbTempArenaMemory tmp;
  292. u8 *text;
  293. if (global_module_path_set) {
  294. return global_module_path;
  295. }
  296. auto path_buf = array_make<char>(heap_allocator(), 300);
  297. defer (array_free(&path_buf));
  298. len = 0;
  299. for (;;) {
  300. // This is not a 100% reliable system, but for the purposes
  301. // of this compiler, it should be _good enough_.
  302. // That said, there's no solid 100% method on Linux to get the program's
  303. // path without checking this link. Sorry.
  304. len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
  305. if(len == 0) {
  306. return make_string(nullptr, 0);
  307. }
  308. if (len < path_buf.count) {
  309. break;
  310. }
  311. array_resize(&path_buf, 2*path_buf.count + 300);
  312. }
  313. gb_mutex_lock(&string_buffer_mutex);
  314. defer (gb_mutex_unlock(&string_buffer_mutex));
  315. tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
  316. defer (gb_temp_arena_memory_end(tmp));
  317. text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
  318. gb_memmove(text, &path_buf[0], len);
  319. path = make_string(text, len);
  320. for (i = path.len-1; i >= 0; i--) {
  321. u8 c = path[i];
  322. if (c == '/' || c == '\\') {
  323. break;
  324. }
  325. path.len--;
  326. }
  327. global_module_path = path;
  328. global_module_path_set = true;
  329. return path;
  330. }
  331. #endif
  332. #if defined(GB_SYSTEM_WINDOWS)
  333. String path_to_fullpath(gbAllocator a, String s) {
  334. String result = {};
  335. gb_mutex_lock(&string_buffer_mutex);
  336. defer (gb_mutex_unlock(&string_buffer_mutex));
  337. gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
  338. defer (gb_temp_arena_memory_end(tmp));
  339. String16 string16 = string_to_string16(string_buffer_allocator, s);
  340. DWORD len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr);
  341. if (len != 0) {
  342. wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
  343. GetFullPathNameW(&string16[0], len, text, nullptr);
  344. text[len] = 0;
  345. result = string16_to_string(a, make_string16(text, len));
  346. result = string_trim_whitespace(result);
  347. // Replace Windows style separators
  348. for (isize i = 0; i < result.len; i++) {
  349. if (result[i] == '\\') {
  350. result[i] = '/';
  351. }
  352. }
  353. }
  354. return result;
  355. }
  356. #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
  357. String path_to_fullpath(gbAllocator a, String s) {
  358. char *p;
  359. gb_mutex_lock(&string_buffer_mutex);
  360. p = realpath(cast(char *)s.text, 0);
  361. gb_mutex_unlock(&string_buffer_mutex);
  362. if(p == nullptr) return String{};
  363. return make_string_c(p);
  364. }
  365. #else
  366. #error Implement system
  367. #endif
  368. String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
  369. u8 *str = gb_alloc_array(heap_allocator(), u8, base_dir.len+1+path.len+1);
  370. defer (gb_free(heap_allocator(), str));
  371. isize i = 0;
  372. gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len;
  373. gb_memmove(str+i, "/", 1); i += 1;
  374. gb_memmove(str+i, path.text, path.len); i += path.len;
  375. str[i] = 0;
  376. String res = make_string(str, i);
  377. res = string_trim_whitespace(res);
  378. return path_to_fullpath(a, res);
  379. }
  380. String get_fullpath_core(gbAllocator a, String path) {
  381. String module_dir = odin_root_dir();
  382. String core = str_lit("core/");
  383. isize str_len = module_dir.len + core.len + path.len;
  384. u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
  385. defer (gb_free(heap_allocator(), str));
  386. isize i = 0;
  387. gb_memmove(str+i, module_dir.text, module_dir.len); i += module_dir.len;
  388. gb_memmove(str+i, core.text, core.len); i += core.len;
  389. gb_memmove(str+i, path.text, path.len); i += path.len;
  390. str[i] = 0;
  391. String res = make_string(str, i);
  392. res = string_trim_whitespace(res);
  393. return path_to_fullpath(a, res);
  394. }
  395. void init_build_context(void) {
  396. BuildContext *bc = &build_context;
  397. gb_affinity_init(&bc->affinity);
  398. if (bc->thread_count == 0) {
  399. bc->thread_count = gb_max(bc->affinity.thread_count, 1);
  400. }
  401. bc->ODIN_VENDOR = str_lit("odin");
  402. bc->ODIN_VERSION = ODIN_VERSION;
  403. bc->ODIN_ROOT = odin_root_dir();
  404. TargetMetrics metrics = {};
  405. #if defined(GB_ARCH_64_BIT)
  406. #if defined(GB_SYSTEM_WINDOWS)
  407. metrics = target_windows_amd64;
  408. #elif defined(GB_SYSTEM_OSX)
  409. metrics = target_osx_amd64;
  410. #else
  411. metrics = target_linux_amd64;
  412. #endif
  413. #else
  414. #if defined(GB_SYSTEM_WINDOWS)
  415. metrics = target_windows_386;
  416. #elif defined(GB_SYSTEM_OSX)
  417. #error "Unsupported architecture"
  418. #else
  419. metrics = target_linux_386;
  420. #endif
  421. #endif
  422. if (cross_compile_target.len) {
  423. bc->ODIN_OS = cross_compile_target;
  424. }
  425. GB_ASSERT(metrics.os != TargetOs_Invalid);
  426. GB_ASSERT(metrics.arch != TargetArch_Invalid);
  427. GB_ASSERT(metrics.word_size > 1);
  428. GB_ASSERT(metrics.max_align > 1);
  429. bc->metrics = metrics;
  430. bc->ODIN_OS = target_os_names[metrics.os];
  431. bc->ODIN_ARCH = target_arch_names[metrics.arch];
  432. bc->ODIN_ENDIAN = target_endian_names[target_endians[metrics.arch]];
  433. bc->endian_kind = target_endians[metrics.arch];
  434. bc->word_size = metrics.word_size;
  435. bc->max_align = metrics.max_align;
  436. bc->link_flags = str_lit(" ");
  437. bc->opt_flags = str_lit(" ");
  438. gbString llc_flags = gb_string_make_reserve(heap_allocator(), 64);
  439. if (bc->ODIN_DEBUG) {
  440. // llc_flags = gb_string_appendc(llc_flags, "-debug-compile ");
  441. }
  442. // NOTE(zangent): The linker flags to set the build architecture are different
  443. // across OSs. It doesn't make sense to allocate extra data on the heap
  444. // here, so I just #defined the linker flags to keep things concise.
  445. if (bc->metrics.arch == TargetArch_amd64) {
  446. llc_flags = gb_string_appendc(llc_flags, "-march=x86-64 ");
  447. switch (bc->metrics.os) {
  448. case TargetOs_windows:
  449. bc->link_flags = str_lit("/machine:x64 ");
  450. break;
  451. case TargetOs_osx:
  452. break;
  453. case TargetOs_linux:
  454. bc->link_flags = str_lit("-arch x86-64 ");
  455. break;
  456. }
  457. } else if (bc->metrics.arch == TargetArch_386) {
  458. llc_flags = gb_string_appendc(llc_flags, "-march=x86 ");
  459. switch (bc->metrics.os) {
  460. case TargetOs_windows:
  461. bc->link_flags = str_lit("/machine:x86 ");
  462. break;
  463. case TargetOs_osx:
  464. gb_printf_err("Unsupported architecture\n");
  465. gb_exit(1);
  466. break;
  467. case TargetOs_linux:
  468. bc->link_flags = str_lit("-arch x86 ");
  469. break;
  470. }
  471. } else {
  472. gb_printf_err("Unsupported architecture\n");;
  473. gb_exit(1);
  474. }
  475. bc->llc_flags = make_string_c(llc_flags);
  476. bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
  477. gbString opt_flags = gb_string_make_reserve(heap_allocator(), 64);
  478. if (bc->optimization_level != 0) {
  479. opt_flags = gb_string_append_fmt(opt_flags, "-O%d ", bc->optimization_level);
  480. // NOTE(lachsinc): The following options were previously passed during call
  481. // to opt in main.cpp:exec_llvm_opt().
  482. // -die: Dead instruction elimination
  483. // -memcpyopt: MemCpy optimization
  484. }
  485. if (bc->ODIN_DEBUG == false) {
  486. opt_flags = gb_string_appendc(opt_flags, "-memcpyopt -die ");
  487. }
  488. // NOTE(lachsinc): This optimization option was previously required to get
  489. // around an issue in fmt.odin. Thank bp for tracking it down! Leaving for now until the issue
  490. // is resolved and confirmed by Bill. Maybe it should be readded in non-debug builds.
  491. // if (bc->ODIN_DEBUG == false) {
  492. // opt_flags = gb_string_appendc(opt_flags, "-mem2reg ");
  493. // }
  494. bc->opt_flags = make_string_c(opt_flags);
  495. #undef LINK_FLAG_X64
  496. #undef LINK_FLAG_386
  497. }