build.c (24420B)
1 /* See LICENSE for license details. */ 2 /* NOTE: inspired by nob: https://github.com/tsoding/nob.h */ 3 4 /* TODO(rnp): 5 * [ ]: bake shaders and font data into binary 6 * - for shaders there is a way of making a separate data section and referring 7 * to it with extern from the C source (bake both data and size) 8 * - use objcopy, maybe need linker script maybe command line flags for ld will work 9 * [ ]: cross compile/override baked compiler 10 * [ ]: msvc build doesn't detect out of date files correctly 11 * [ ]: seperate dwarf debug info 12 */ 13 #include <stdarg.h> 14 #include <stdio.h> 15 16 #include "util.h" 17 18 #include "beamformer_parameters.h" 19 20 #define OUTDIR "out" 21 #define OUTPUT(s) OUTDIR "/" s 22 23 #if COMPILER_MSVC 24 #define COMMON_FLAGS "-nologo", "-std:c11", "-Fo:" OUTDIR "\\", "-Z7", "-Zo" 25 #define DEBUG_FLAGS "-Od", "-D_DEBUG" 26 #define OPTIMIZED_FLAGS "-O2" 27 #define EXTRA_FLAGS "" 28 #else 29 #define COMMON_FLAGS "-std=c11", "-pipe", "-Wall" 30 #define DEBUG_FLAGS "-O0", "-D_DEBUG", "-Wno-unused-function" 31 #define OPTIMIZED_FLAGS "-O3" 32 #define EXTRA_FLAGS "-Werror", "-Wextra", "-Wshadow", "-Wconversion", "-Wno-unused-parameter", \ 33 "-Wno-error=unused-function", "-ffast-math" 34 #endif 35 36 #define is_aarch64 ARCH_ARM64 37 #define is_amd64 ARCH_X64 38 #define is_unix OS_LINUX 39 #define is_w32 OS_WINDOWS 40 #define is_clang COMPILER_CLANG 41 #define is_gcc COMPILER_GCC 42 #define is_msvc COMPILER_MSVC 43 44 #if OS_LINUX 45 46 #include <errno.h> 47 #include <string.h> 48 #include <sys/select.h> 49 #include <sys/wait.h> 50 51 #include "os_linux.c" 52 53 #define W32_DECL(x) 54 55 #define OS_SHARED_LINK_LIB(s) "lib" s ".so" 56 #define OS_SHARED_LIB(s) s ".so" 57 #define OS_STATIC_LIB(s) s ".a" 58 #define OS_MAIN "main_linux.c" 59 60 #elif OS_WINDOWS 61 62 #include "os_win32.c" 63 64 #define W32_DECL(x) x 65 66 #define OS_SHARED_LINK_LIB(s) s ".dll" 67 #define OS_SHARED_LIB(s) s ".dll" 68 #define OS_STATIC_LIB(s) s ".lib" 69 #define OS_MAIN "main_w32.c" 70 71 #else 72 #error Unsupported Platform 73 #endif 74 75 #if COMPILER_CLANG 76 #define COMPILER "clang" 77 #elif COMPILER_MSVC 78 #define COMPILER "cl" 79 #else 80 #define COMPILER "cc" 81 #endif 82 83 #if COMPILER_MSVC 84 #define LINK_LIB(name) name ".lib" 85 #define OBJECT(name) name ".obj" 86 #define OUTPUT_DLL(name) "/LD", "/Fe:", name 87 #define OUTPUT_LIB(name) "/out:" OUTPUT(name) 88 #define OUTPUT_EXE(name) "/Fe:", name 89 #define STATIC_LIBRARY_BEGIN(name) "lib", "/nologo", name 90 #else 91 #define LINK_LIB(name) "-l" name 92 #define OBJECT(name) name ".o" 93 #define OUTPUT_DLL(name) "-fPIC", "-shared", "-o", name 94 #define OUTPUT_LIB(name) OUTPUT(name) 95 #define OUTPUT_EXE(name) "-o", name 96 #define STATIC_LIBRARY_BEGIN(name) "ar", "rc", name 97 #endif 98 99 #define shift(list, count) ((count)--, *(list)++) 100 101 #define da_append_count(a, s, items, item_count) do { \ 102 da_reserve((a), (s), (s)->count + (item_count)); \ 103 mem_copy((s)->data + (s)->count, (items), sizeof(*(items)) * (uz)(item_count)); \ 104 (s)->count += (item_count); \ 105 } while (0) 106 107 #define cmd_append_count da_append_count 108 #define cmd_append(a, s, ...) da_append_count(a, s, ((char *[]){__VA_ARGS__}), \ 109 (iz)(sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) 110 111 typedef struct { 112 char **data; 113 iz count; 114 iz capacity; 115 } CommandList; 116 117 typedef struct { 118 b32 debug; 119 b32 generic; 120 b32 sanitize; 121 b32 tests; 122 b32 time; 123 } Options; 124 125 #define BUILD_LOG_KINDS \ 126 X(Error, "\x1B[31m[ERROR]\x1B[0m ") \ 127 X(Warning, "\x1B[33m[WARNING]\x1B[0m ") \ 128 X(Info, "\x1B[32m[INFO]\x1B[0m ") \ 129 X(Command, "\x1B[36m[COMMAND]\x1B[0m ") 130 #define X(t, ...) BuildLogKind_##t, 131 typedef enum {BUILD_LOG_KINDS BuildLogKind_Count} BuildLogKind; 132 #undef X 133 134 function void 135 build_log_base(BuildLogKind kind, char *format, va_list args) 136 { 137 #define X(t, pre) pre, 138 read_only local_persist char *prefixes[BuildLogKind_Count + 1] = {BUILD_LOG_KINDS "[INVALID] "}; 139 #undef X 140 FILE *out = kind == BuildLogKind_Error? stderr : stdout; 141 fputs(prefixes[MIN(kind, BuildLogKind_Count)], out); 142 vfprintf(out, format, args); 143 fputc('\n', out); 144 } 145 146 #define build_log_failure(format, ...) build_log(BuildLogKind_Error, \ 147 "failed to build: " format, ##__VA_ARGS__) 148 #define build_log_info(...) build_log(BuildLogKind_Info, ##__VA_ARGS__) 149 #define build_log_command(...) build_log(BuildLogKind_Command, ##__VA_ARGS__) 150 #define build_log_warning(...) build_log(BuildLogKind_Warning, ##__VA_ARGS__) 151 function void 152 build_log(BuildLogKind kind, char *format, ...) 153 { 154 va_list ap; 155 va_start(ap, format); 156 build_log_base(kind, format, ap); 157 va_end(ap); 158 } 159 160 #define build_fatal(fmt, ...) build_fatal_("%s: " fmt, __FUNCTION__, ##__VA_ARGS__) 161 function no_return void 162 build_fatal_(char *format, ...) 163 { 164 va_list ap; 165 va_start(ap, format); 166 build_log_base(BuildLogKind_Error, format, ap); 167 va_end(ap); 168 os_exit(1); 169 } 170 171 function b32 172 s8_contains(s8 s, u8 byte) 173 { 174 b32 result = 0; 175 for (iz i = 0 ; !result && i < s.len; i++) 176 result |= s.data[i] == byte; 177 return result; 178 } 179 180 function void 181 stream_push_command(Stream *s, CommandList *c) 182 { 183 if (!s->errors) { 184 for (iz i = 0; i < c->count; i++) { 185 s8 item = c_str_to_s8(c->data[i]); 186 if (item.len) { 187 b32 escape = s8_contains(item, ' ') || s8_contains(item, '"'); 188 if (escape) stream_append_byte(s, '\''); 189 stream_append_s8(s, item); 190 if (escape) stream_append_byte(s, '\''); 191 if (i != c->count - 1) stream_append_byte(s, ' '); 192 } 193 } 194 } 195 } 196 197 #if OS_LINUX 198 199 function b32 200 os_rename_file(char *name, char *new) 201 { 202 b32 result = rename(name, new) != -1; 203 return result; 204 } 205 206 function b32 207 os_remove_file(char *name) 208 { 209 b32 result = remove(name) != -1; 210 return result; 211 } 212 213 function void 214 os_make_directory(char *name) 215 { 216 mkdir(name, 0770); 217 } 218 219 function u64 220 os_get_filetime(char *file) 221 { 222 struct stat sb; 223 u64 result = (u64)-1; 224 if (stat(file, &sb) != -1) 225 result = (u64)sb.st_mtim.tv_sec; 226 return result; 227 } 228 229 function iptr 230 os_spawn_process(CommandList *cmd, Stream sb) 231 { 232 pid_t result = fork(); 233 switch (result) { 234 case -1: build_fatal("failed to fork command: %s: %s", cmd->data[0], strerror(errno)); break; 235 case 0: { 236 if (execvp(cmd->data[0], cmd->data) == -1) 237 build_fatal("failed to exec command: %s: %s", cmd->data[0], strerror(errno)); 238 unreachable(); 239 } break; 240 } 241 return (iptr)result; 242 } 243 244 function b32 245 os_wait_close_process(iptr handle) 246 { 247 b32 result = 0; 248 for (;;) { 249 i32 status; 250 iptr wait_pid = (iptr)waitpid((i32)handle, &status, 0); 251 if (wait_pid == -1) 252 build_fatal("failed to wait on child process: %s", strerror(errno)); 253 if (wait_pid == handle) { 254 if (WIFEXITED(status)) { 255 status = WEXITSTATUS(status); 256 /* TODO(rnp): logging */ 257 result = status == 0; 258 break; 259 } 260 if (WIFSIGNALED(status)) { 261 /* TODO(rnp): logging */ 262 result = 0; 263 break; 264 } 265 } else { 266 /* TODO(rnp): handle multiple children */ 267 INVALID_CODE_PATH; 268 } 269 } 270 return result; 271 } 272 273 #elif OS_WINDOWS 274 275 enum { 276 MOVEFILE_REPLACE_EXISTING = 0x01, 277 }; 278 279 W32(b32) CreateDirectoryA(c8 *, void *); 280 W32(b32) CreateProcessA(u8 *, u8 *, iptr, iptr, b32, u32, iptr, u8 *, iptr, iptr); 281 W32(b32) GetExitCodeProcess(iptr handle, u32 *); 282 W32(b32) GetFileTime(iptr, iptr, iptr, iptr); 283 W32(b32) MoveFileExA(c8 *, c8 *, u32); 284 285 function void 286 os_make_directory(char *name) 287 { 288 CreateDirectoryA(name, 0); 289 } 290 291 function b32 292 os_rename_file(char *name, char *new) 293 { 294 b32 result = MoveFileExA(name, new, MOVEFILE_REPLACE_EXISTING) != 0; 295 return result; 296 } 297 298 function b32 299 os_remove_file(char *name) 300 { 301 b32 result = DeleteFileA(name); 302 return result; 303 } 304 305 function u64 306 os_get_filetime(char *file) 307 { 308 u64 result = (u64)-1; 309 iptr h = CreateFileA(file, 0, 0, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); 310 if (h != INVALID_FILE) { 311 struct { u32 low, high; } w32_filetime; 312 GetFileTime(h, 0, 0, (iptr)&w32_filetime); 313 result = (u64)w32_filetime.high << 32ULL | w32_filetime.low; 314 CloseHandle(h); 315 } 316 return result; 317 } 318 319 function iptr 320 os_spawn_process(CommandList *cmd, Stream sb) 321 { 322 struct { 323 u32 cb; 324 u8 *reserved, *desktop, *title; 325 u32 x, y, x_size, y_size, x_count_chars, y_count_chars; 326 u32 fill_attr, flags; 327 u16 show_window, reserved_2; 328 u8 *reserved_3; 329 iptr std_input, std_output, std_error; 330 } w32_startup_info = { 331 .cb = sizeof(w32_startup_info), 332 .flags = 0x100, 333 .std_input = GetStdHandle(STD_INPUT_HANDLE), 334 .std_output = GetStdHandle(STD_OUTPUT_HANDLE), 335 .std_error = GetStdHandle(STD_ERROR_HANDLE), 336 }; 337 338 struct { 339 iptr phandle, thandle; 340 u32 pid, tid; 341 } w32_process_info = {0}; 342 343 /* TODO(rnp): warn if we need to clamp last string */ 344 sb.widx = MIN(sb.widx, KB(32) - 1); 345 if (sb.widx < sb.cap) sb.data[sb.widx] = 0; 346 else sb.data[sb.widx - 1] = 0; 347 348 iptr result = INVALID_FILE; 349 if (CreateProcessA(0, sb.data, 0, 0, 1, 0, 0, 0, (iptr)&w32_startup_info, 350 (iptr)&w32_process_info)) 351 { 352 CloseHandle(w32_process_info.thandle); 353 result = w32_process_info.phandle; 354 } 355 return result; 356 } 357 358 function b32 359 os_wait_close_process(iptr handle) 360 { 361 b32 result = WaitForSingleObject(handle, -1) != 0xFFFFFFFFUL; 362 if (result) { 363 u32 status; 364 GetExitCodeProcess(handle, &status); 365 result = status == 0; 366 } 367 CloseHandle(handle); 368 return result; 369 } 370 371 #endif 372 373 #define needs_rebuild(b, ...) needs_rebuild_(b, ((char *[]){__VA_ARGS__}), \ 374 (sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) 375 function b32 376 needs_rebuild_(char *binary, char *deps[], iz deps_count) 377 { 378 u64 binary_filetime = os_get_filetime(binary); 379 b32 result = binary_filetime == (u64)-1; 380 for (iz i = 0; i < deps_count; i++) { 381 u64 filetime = os_get_filetime(deps[i]); 382 result |= (filetime == (u64)-1) | (filetime > binary_filetime); 383 } 384 return result; 385 } 386 387 function b32 388 run_synchronous(Arena a, CommandList *command) 389 { 390 Stream sb = arena_stream(a); 391 stream_push_command(&sb, command); 392 build_log_command("%.*s", (i32)sb.widx, sb.data); 393 return os_wait_close_process(os_spawn_process(command, sb)); 394 } 395 396 function CommandList 397 cmd_base(Arena *a, Options *o) 398 { 399 CommandList result = {0}; 400 cmd_append(a, &result, COMPILER); 401 402 if (!is_msvc) { 403 /* TODO(rnp): support cross compiling with clang */ 404 if (!o->generic) cmd_append(a, &result, "-march=native"); 405 else if (is_amd64) cmd_append(a, &result, "-march=x86-64-v3"); 406 else if (is_aarch64) cmd_append(a, &result, "-march=armv8"); 407 } 408 409 cmd_append(a, &result, COMMON_FLAGS, "-Iexternal/include"); 410 if (o->debug) cmd_append(a, &result, DEBUG_FLAGS); 411 else cmd_append(a, &result, OPTIMIZED_FLAGS); 412 413 /* NOTE: ancient gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80454 */ 414 if (is_gcc) cmd_append(a, &result, "-Wno-missing-braces"); 415 416 if (is_w32 && is_clang) cmd_append(a, &result, "-fms-extensions"); 417 418 if (o->debug && is_unix) cmd_append(a, &result, "-ggdb"); 419 420 /* NOTE(rnp): need to avoid w32-gcc for ci */ 421 b32 sanitize = !is_msvc && (o->debug || o->sanitize) && !(is_w32 && is_gcc); 422 if (sanitize) { 423 cmd_append(a, &result, "-fsanitize=address,undefined"); 424 /* NOTE(rnp): impossible to autodetect on GCC versions < 14 (ci has 13) */ 425 cmd_append(a, &result, "-DASAN_ACTIVE=1"); 426 } else { 427 cmd_append(a, &result, "-DASAN_ACTIVE=0"); 428 } 429 if (!sanitize && o->sanitize) build_log_warning("santizers not supported with this compiler"); 430 431 return result; 432 } 433 434 function void 435 check_rebuild_self(Arena arena, i32 argc, char *argv[]) 436 { 437 char *binary = shift(argv, argc); 438 if (needs_rebuild(binary, __FILE__, "os_win32.c", "os_linux.c", "util.c", "util.h", "beamformer_parameters.h")) { 439 Stream name_buffer = arena_stream(arena); 440 stream_append_s8s(&name_buffer, c_str_to_s8(binary), s8(".old")); 441 char *old_name = (char *)arena_stream_commit_zero(&arena, &name_buffer).data; 442 443 if (!os_rename_file(binary, old_name)) 444 build_fatal("failed to move: %s -> %s", binary, old_name); 445 446 Options options = {0}; 447 CommandList c = cmd_base(&arena, &options); 448 cmd_append(&arena, &c, EXTRA_FLAGS); 449 if (!is_msvc) cmd_append(&arena, &c, "-Wno-unused-function"); 450 cmd_append(&arena, &c, __FILE__, OUTPUT_EXE(binary)); 451 if (is_msvc) cmd_append(&arena, &c, "/link", "-incremental:no", "-opt:ref"); 452 cmd_append(&arena, &c, (void *)0); 453 if (!run_synchronous(arena, &c)) { 454 os_rename_file(old_name, binary); 455 build_fatal("failed to rebuild self"); 456 } 457 os_remove_file(old_name); 458 459 c.count = 0; 460 cmd_append(&arena, &c, binary); 461 cmd_append_count(&arena, &c, argv, argc); 462 cmd_append(&arena, &c, (void *)0); 463 if (!run_synchronous(arena, &c)) 464 os_exit(1); 465 466 os_exit(0); 467 } 468 } 469 470 function b32 471 s8_equal(s8 a, s8 b) 472 { 473 b32 result = a.len == b.len; 474 for (iz i = 0; result && i < a.len; i++) 475 result = a.data[i] == b.data[i]; 476 return result; 477 } 478 479 function void 480 usage(char *argv0) 481 { 482 printf("%s [--debug] [--sanitize] [--time]\n" 483 " --debug: dynamically link and build with debug symbols\n" 484 " --generic: compile for a generic target (x86-64-v3 or armv8 with NEON)\n" 485 " --sanitize: build with ASAN and UBSAN\n" 486 " --tests: also build programs in tests/\n" 487 " --time: print build time\n" 488 , argv0); 489 os_exit(0); 490 } 491 492 function Options 493 parse_options(i32 argc, char *argv[]) 494 { 495 Options result = {0}; 496 497 char *argv0 = shift(argv, argc); 498 while (argc > 0) { 499 char *arg = shift(argv, argc); 500 s8 str = c_str_to_s8(arg); 501 if (s8_equal(str, s8("--debug"))) { 502 result.debug = 1; 503 } else if (s8_equal(str, s8("--generic"))) { 504 result.generic = 1; 505 } else if (s8_equal(str, s8("--sanitize"))) { 506 result.sanitize = 1; 507 } else if (s8_equal(str, s8("--tests"))) { 508 result.tests = 1; 509 } else if (s8_equal(str, s8("--time"))) { 510 result.time = 1; 511 } else { 512 usage(argv0); 513 } 514 } 515 516 return result; 517 } 518 519 /* NOTE(rnp): produce pdbs on w32 */ 520 function void 521 cmd_pdb(Arena *a, CommandList *cmd, char *name) 522 { 523 if (is_w32 && is_clang) { 524 cmd_append(a, cmd, "-fuse-ld=lld", "-g", "-gcodeview", "-Wl,--pdb="); 525 } else if (is_msvc) { 526 Stream sb = arena_stream(*a); 527 stream_append_s8s(&sb, s8("-PDB:"), c_str_to_s8(name), s8(".pdb")); 528 char *pdb = (char *)arena_stream_commit_zero(a, &sb).data; 529 cmd_append(a, cmd, "/link", "-incremental:no", "-opt:ref", "-DEBUG", pdb); 530 } 531 } 532 533 function void 534 git_submodule_update(Arena a, char *name) 535 { 536 Stream sb = arena_stream(a); 537 stream_append_s8s(&sb, c_str_to_s8(name), s8(OS_PATH_SEPARATOR), s8(".git")); 538 arena_stream_commit_zero(&a, &sb); 539 540 CommandList git = {0}; 541 /* NOTE(rnp): cryptic bs needed to get a simple exit code if name is dirty */ 542 cmd_append(&a, &git, "git", "diff-index", "--quiet", "HEAD", "--", name, (void *)0); 543 if (!os_file_exists((c8 *)sb.data) || !run_synchronous(a, &git)) { 544 git.count = 1; 545 cmd_append(&a, &git, "submodule", "update", "--init", "--depth=1", name, (void *)0); 546 if (!run_synchronous(a, &git)) 547 build_fatal("failed to clone required module: %s", name); 548 } 549 } 550 551 function b32 552 build_shared_library(Arena a, CommandList cc, char *name, char *output, char **libs, iz libs_count, char **srcs, iz srcs_count) 553 { 554 cmd_append_count(&a, &cc, srcs, srcs_count); 555 cmd_append(&a, &cc, OUTPUT_DLL(output)); 556 cmd_pdb(&a, &cc, name); 557 cmd_append_count(&a, &cc, libs, libs_count); 558 cmd_append(&a, &cc, (void *)0); 559 b32 result = run_synchronous(a, &cc); 560 if (!result) build_log_failure("%s", output); 561 return result; 562 } 563 564 function b32 565 cc_single_file(Arena a, CommandList cc, b32 exe, char *src, char *dest, char **tail, iz tail_count) 566 { 567 char *executable[] = {src, is_msvc? "/Fe:" : "-o", dest}; 568 char *object[] = {is_msvc? "/c" : "-c", src, is_msvc? "/Fo:" : "-o", dest}; 569 cmd_append_count(&a, &cc, exe? executable : object, 570 exe? countof(executable) : countof(object)); 571 cmd_append_count(&a, &cc, tail, tail_count); 572 cmd_append(&a, &cc, (void *)0); 573 b32 result = run_synchronous(a, &cc); 574 if (!result) build_log_failure("%s", dest); 575 return result; 576 } 577 578 function b32 579 build_static_library_from_objects(Arena a, char *name, char **flags, iz flags_count, char **objects, iz count) 580 { 581 CommandList ar = {0}; 582 cmd_append(&a, &ar, STATIC_LIBRARY_BEGIN(name)); 583 cmd_append_count(&a, &ar, flags, flags_count); 584 cmd_append_count(&a, &ar, objects, count); 585 cmd_append(&a, &ar, (void *)0); 586 b32 result = run_synchronous(a, &ar); 587 if (!result) build_log_failure("%s", name); 588 return result; 589 } 590 591 function b32 592 build_static_library(Arena a, CommandList cc, char *name, char **deps, char **outputs, iz count) 593 { 594 /* TODO(rnp): refactor to not need outputs */ 595 b32 result = 1; 596 for (iz i = 0; i < count; i++) 597 result &= cc_single_file(a, cc, 0, deps[i], outputs[i], 0, 0); 598 if (result) result = build_static_library_from_objects(a, name, 0, 0, outputs, count); 599 return result; 600 } 601 602 function b32 603 check_build_raylib(Arena a, CommandList cc, b32 shared) 604 { 605 b32 result = 1; 606 char *libraylib = shared ? OS_SHARED_LINK_LIB("raylib") : OUTPUT_LIB(OS_STATIC_LIB("raylib")); 607 if (needs_rebuild(libraylib, __FILE__, "external/include/rlgl.h", "external/raylib")) { 608 git_submodule_update(a, "external/raylib"); 609 os_copy_file("external/raylib/src/rlgl.h", "external/include/rlgl.h"); 610 611 if (is_unix) cmd_append(&a, &cc, "-D_GLFW_X11"); 612 cmd_append(&a, &cc, "-DPLATFORM_DESKTOP_GLFW"); 613 if (!is_msvc) cmd_append(&a, &cc, "-Wno-unused-but-set-variable"); 614 cmd_append(&a, &cc, "-Iexternal/raylib/src", "-Iexternal/raylib/src/external/glfw/include"); 615 #define RAYLIB_SOURCES \ 616 X(rglfw) \ 617 X(rshapes) \ 618 X(rtext) \ 619 X(rtextures) \ 620 X(utils) 621 #define X(name) "external/raylib/src/" #name ".c", 622 char *srcs[] = {"external/rcore_extended.c", RAYLIB_SOURCES}; 623 #undef X 624 #define X(name) OUTPUT(OBJECT(#name)), 625 char *outs[] = {OUTPUT(OBJECT("rcore_extended")), RAYLIB_SOURCES}; 626 #undef X 627 628 if (shared) { 629 char *libs[] = {LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), LINK_LIB("winmm")}; 630 iz libs_count = is_w32 ? countof(libs) : 0; 631 cmd_append(&a, &cc, "-DBUILD_LIBTYPE_SHARED", "-D_GLFW_BUILD_DLL"); 632 result = build_shared_library(a, cc, "raylib", libraylib, libs, libs_count, srcs, countof(srcs)); 633 } else { 634 result = build_static_library(a, cc, libraylib, srcs, outs, countof(srcs)); 635 } 636 } 637 return result; 638 } 639 640 function b32 641 build_helper_library(Arena arena, CommandList cc) 642 { 643 ///////////// 644 // library 645 char *library = OUTPUT(OS_SHARED_LIB("ogl_beamformer_lib")); 646 char *libs[] = {LINK_LIB("Synchronization")}; 647 iz libs_count = is_w32 ? countof(libs) : 0; 648 649 if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function"); 650 b32 result = build_shared_library(arena, cc, "ogl_beamformer_lib", library, 651 libs, libs_count, 652 arg_list(char *, "helpers/ogl_beamformer_lib.c")); 653 654 ///////////// 655 // header 656 char *lib_header_out = OUTPUT("ogl_beamformer_lib.h"); 657 if (needs_rebuild(lib_header_out, "beamformer_parameters.h", "helpers/ogl_beamformer_lib_base.h")) { 658 s8 parameters_header = os_read_whole_file(&arena, "beamformer_parameters.h"); 659 s8 base_header = os_read_whole_file(&arena, "helpers/ogl_beamformer_lib_base.h"); 660 result = parameters_header.len != 0 && base_header.len != 0 && 661 parameters_header.data + parameters_header.len == base_header.data; 662 if (result) { 663 s8 output_file = parameters_header; 664 output_file.len += base_header.len; 665 result &= os_write_new_file(lib_header_out, output_file); 666 } 667 if (!result) build_log_failure("%s", lib_header_out); 668 } 669 670 return result; 671 } 672 673 function b32 674 build_beamformer_as_library(Arena arena, CommandList cc) 675 { 676 char *library = OS_SHARED_LIB("beamformer"); 677 char *libs[] = {!is_msvc? "-L." : "", LINK_LIB("raylib"), LINK_LIB("gdi32"), 678 LINK_LIB("shell32"), LINK_LIB("user32"), LINK_LIB("opengl32"), 679 LINK_LIB("winmm"), LINK_LIB("Synchronization"), OUTPUT("main.lib")}; 680 iz libs_count = is_w32 ? countof(libs) : 0; 681 cmd_append(&arena, &cc, "-D_BEAMFORMER_DLL"); 682 b32 result = build_shared_library(arena, cc, "beamformer", library, 683 libs, libs_count, arg_list(char *, "beamformer.c")); 684 return result; 685 } 686 687 function b32 688 build_tests(Arena arena, CommandList cc) 689 { 690 #define TEST_PROGRAMS \ 691 X("throughput", LINK_LIB("zstd"), W32_DECL(LINK_LIB("Synchronization"))) 692 693 os_make_directory(OUTPUT("tests")); 694 if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function"); 695 cmd_append(&arena, &cc, "-I.", "-Ihelpers"); 696 697 b32 result = 1; 698 iz cc_count = cc.count; 699 #define X(prog, ...) \ 700 cmd_pdb(&arena, &cc, prog); \ 701 result &= cc_single_file(arena, cc, 1, "tests/" prog ".c", \ 702 OUTPUT("tests/" prog), \ 703 arg_list(char *, ##__VA_ARGS__)); \ 704 cc.count = cc_count; 705 TEST_PROGRAMS 706 #undef X 707 return result; 708 } 709 710 function void 711 stream_begin_matlab_enumeration(Stream *s, s8 name, s8 type) 712 { 713 stream_append_s8s(s, s8("classdef "), name, s8(" < "), type, s8("\n"), s8(" enumeration\n")); 714 } 715 716 function void 717 stream_append_matlab_enumeration_field(Stream *s, s8 field) 718 { 719 stream_append_s8s(s, s8(" "), field, s8(",\n")); 720 } 721 722 function void 723 stream_end_matlab_enumeration(Stream *s) 724 { 725 stream_append_s8(s, s8(" end\nend\n")); 726 } 727 728 function b32 729 build_matlab_bindings(Arena arena) 730 { 731 b32 result = 1; 732 os_make_directory(OUTPUT("matlab")); 733 734 char *feedback_out = OUTPUT("matlab/LiveFeedbackFlags.m"); 735 /* NOTE(rnp): if one file is outdated all files are outdated */ 736 if (needs_rebuild(feedback_out, "beamformer_parameters.h")) { 737 738 Stream sb = arena_stream(arena); 739 740 #define X(name, flag) stream_append_matlab_enumeration_field(&sb, s8(#name " (" str(flag) ")")); 741 742 stream_begin_matlab_enumeration(&sb, s8("LiveFeedbackFlags"), s8("int32")); 743 BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST 744 stream_end_matlab_enumeration(&sb); 745 result &= os_write_new_file(feedback_out, stream_to_s8(&sb)); 746 747 #undef X 748 749 if (!result) build_log_failure("%s", feedback_out); 750 } 751 752 return result; 753 } 754 755 i32 756 main(i32 argc, char *argv[]) 757 { 758 u64 start_time = os_get_timer_counter(); 759 760 b32 result = 1; 761 Arena arena = os_alloc_arena(MB(8)); 762 check_rebuild_self(arena, argc, argv); 763 764 Options options = parse_options(argc, argv); 765 766 os_make_directory(OUTDIR); 767 768 CommandList c = cmd_base(&arena, &options); 769 if (!check_build_raylib(arena, c, options.debug)) return 1; 770 771 ///////////////////////////////////// 772 // extra flags (unusable for raylib) 773 cmd_append(&arena, &c, EXTRA_FLAGS); 774 775 ///////////////// 776 // helpers/tests 777 result &= build_matlab_bindings(arena); 778 result &= build_helper_library(arena, c); 779 if (options.tests) result &= build_tests(arena, c); 780 781 ////////////////// 782 // static portion 783 iz c_count = c.count; 784 cmd_append(&arena, &c, OS_MAIN, OUTPUT_EXE("ogl")); 785 cmd_pdb(&arena, &c, "ogl"); 786 if (options.debug) { 787 /* NOTE(rnp): (gnu) ld doesn't properly export global symbols without this */ 788 if (is_gcc) cmd_append(&arena, &c, "-Wl,--export-dynamic"); 789 790 if (!is_w32) cmd_append(&arena, &c, "-Wl,-rpath,."); 791 if (!is_msvc) cmd_append(&arena, &c, "-L."); 792 cmd_append(&arena, &c, LINK_LIB("raylib")); 793 } else { 794 cmd_append(&arena, &c, OUTPUT(OS_STATIC_LIB("raylib"))); 795 } 796 if (!is_msvc) cmd_append(&arena, &c, "-lm"); 797 if (is_unix) cmd_append(&arena, &c, "-lGL"); 798 if (is_w32) { 799 cmd_append(&arena, &c, LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), 800 LINK_LIB("opengl32"), LINK_LIB("winmm"), LINK_LIB("Synchronization")); 801 if (!is_msvc) cmd_append(&arena, &c, "-Wl,--out-implib," OUTPUT(OS_STATIC_LIB("main"))); 802 } 803 cmd_append(&arena, &c, (void *)0); 804 805 result &= run_synchronous(arena, &c); 806 c.count = c_count; 807 808 ///////////////////////// 809 // hot reloadable portion 810 // 811 // NOTE: this is built after main because on w32 we need to export 812 // gl function pointers for the reloadable portion to import 813 if (options.debug) { 814 if (is_msvc) { 815 build_static_library_from_objects(arena, OUTPUT_LIB(OS_STATIC_LIB("main")), 816 arg_list(char *, "/def", "/name:ogl.exe"), 817 arg_list(char *, OUTPUT(OBJECT("main_w32")))); 818 } 819 result &= build_beamformer_as_library(arena, c); 820 } 821 822 if (options.time) { 823 f64 seconds = (f64)(os_get_timer_counter() - start_time) / (f64)os_get_timer_frequency(); 824 build_log_info("took %0.03f [s]", seconds); 825 } 826 827 return result != 1; 828 }