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