build.c (90294B)
1 /* See LICENSE for license details. */ 2 /* NOTE: inspired by nob: https://github.com/tsoding/nob.h */ 3 4 /* TODO(rnp): 5 * [ ]: refactor: merge pack_table and bake_parameters 6 * [ ]: refactor: allow @Expand to come before the table definition 7 * [ ]: cross compile/override baked compiler 8 * [ ]: msvc build doesn't detect out of date files correctly 9 * [ ]: seperate dwarf debug info 10 */ 11 #include <stdarg.h> 12 #include <setjmp.h> 13 #include <stdio.h> 14 15 #include "util.h" 16 17 #define BeamformerShaderKind_ComputeCount (1) 18 #include "beamformer_parameters.h" 19 20 global char *g_argv0; 21 22 #define OUTDIR "out" 23 #define OUTPUT(s) OUTDIR OS_PATH_SEPARATOR s 24 25 #if COMPILER_MSVC 26 #define COMMON_FLAGS "-nologo", "-std:c11", "-Fo:" OUTDIR "\\", "-Z7", "-Zo" 27 #define DEBUG_FLAGS "-Od", "-D_DEBUG" 28 #define OPTIMIZED_FLAGS "-O2" 29 #define EXTRA_FLAGS "" 30 #else 31 #define COMMON_FLAGS "-std=c11", "-pipe", "-Wall" 32 #define DEBUG_FLAGS "-O0", "-D_DEBUG", "-Wno-unused-function" 33 #define OPTIMIZED_FLAGS "-O3" 34 #define EXTRA_FLAGS_BASE "-Werror", "-Wextra", "-Wshadow", "-Wconversion", "-Wno-unused-parameter", \ 35 "-Wno-error=unused-function", "-funsafe-math-optimizations", "-fno-math-errno" 36 #if COMPILER_GCC 37 #define EXTRA_FLAGS EXTRA_FLAGS_BASE, "-Wno-unused-variable" 38 #else 39 #define EXTRA_FLAGS EXTRA_FLAGS_BASE 40 #endif 41 #endif 42 43 #define is_aarch64 ARCH_ARM64 44 #define is_amd64 ARCH_X64 45 #define is_unix OS_LINUX 46 #define is_w32 OS_WINDOWS 47 #define is_clang COMPILER_CLANG 48 #define is_gcc COMPILER_GCC 49 #define is_msvc COMPILER_MSVC 50 51 #if OS_LINUX 52 53 #include <errno.h> 54 #include <string.h> 55 #include <sys/select.h> 56 #include <sys/wait.h> 57 58 #include "os_linux.c" 59 60 #define W32_DECL(x) 61 62 #define OS_SHARED_LINK_LIB(s) "lib" s ".so" 63 #define OS_SHARED_LIB(s) s ".so" 64 #define OS_STATIC_LIB(s) s ".a" 65 #define OS_MAIN "main_linux.c" 66 67 #elif OS_WINDOWS 68 69 #include "os_win32.c" 70 71 #define W32_DECL(x) x 72 73 #define OS_SHARED_LINK_LIB(s) s ".dll" 74 #define OS_SHARED_LIB(s) s ".dll" 75 #define OS_STATIC_LIB(s) s ".lib" 76 #define OS_MAIN "main_w32.c" 77 78 #else 79 #error Unsupported Platform 80 #endif 81 82 #if COMPILER_CLANG 83 #define COMPILER "clang" 84 #elif COMPILER_MSVC 85 #define COMPILER "cl" 86 #else 87 #define COMPILER "cc" 88 #endif 89 90 #if COMPILER_MSVC 91 #define LINK_LIB(name) name ".lib" 92 #define OBJECT(name) name ".obj" 93 #define OUTPUT_DLL(name) "/LD", "/Fe:", name 94 #define OUTPUT_LIB(name) "/out:" OUTPUT(name) 95 #define OUTPUT_EXE(name) "/Fe:", name 96 #define STATIC_LIBRARY_BEGIN(name) "lib", "/nologo", name 97 #else 98 #define LINK_LIB(name) "-l" name 99 #define OBJECT(name) name ".o" 100 #define OUTPUT_DLL(name) "-fPIC", "-shared", "-o", name 101 #define OUTPUT_LIB(name) OUTPUT(name) 102 #define OUTPUT_EXE(name) "-o", name 103 #define STATIC_LIBRARY_BEGIN(name) "ar", "rc", name 104 #endif 105 106 #define shift(list, count) ((count)--, *(list)++) 107 108 #define cmd_append_count da_append_count 109 #define cmd_append(a, s, ...) da_append_count(a, s, ((char *[]){__VA_ARGS__}), \ 110 (iz)(sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) 111 112 DA_STRUCT(char *, Command); 113 114 typedef struct { 115 b32 bake_shaders; 116 b32 debug; 117 b32 generic; 118 b32 sanitize; 119 b32 tests; 120 b32 time; 121 } Options; 122 123 #define BUILD_LOG_KINDS \ 124 X(Error, "\x1B[31m[ERROR]\x1B[0m ") \ 125 X(Warning, "\x1B[33m[WARNING]\x1B[0m ") \ 126 X(Generate, "\x1B[32m[GENERATE]\x1B[0m ") \ 127 X(Info, "\x1B[33m[INFO]\x1B[0m ") \ 128 X(Command, "\x1B[36m[COMMAND]\x1B[0m ") 129 #define X(t, ...) BuildLogKind_##t, 130 typedef enum {BUILD_LOG_KINDS BuildLogKind_Count} BuildLogKind; 131 #undef X 132 133 function void 134 build_log_base(BuildLogKind kind, char *format, va_list args) 135 { 136 #define X(t, pre) pre, 137 read_only local_persist char *prefixes[BuildLogKind_Count + 1] = {BUILD_LOG_KINDS "[INVALID] "}; 138 #undef X 139 FILE *out = kind == BuildLogKind_Error? stderr : stdout; 140 fputs(prefixes[MIN(kind, BuildLogKind_Count)], out); 141 vfprintf(out, format, args); 142 fputc('\n', out); 143 } 144 145 #define build_log_failure(format, ...) build_log(BuildLogKind_Error, \ 146 "failed to build: " format, ##__VA_ARGS__) 147 #define build_log_error(...) build_log(BuildLogKind_Error, ##__VA_ARGS__) 148 #define build_log_generate(...) build_log(BuildLogKind_Generate, ##__VA_ARGS__) 149 #define build_log_info(...) build_log(BuildLogKind_Info, ##__VA_ARGS__) 150 #define build_log_command(...) build_log(BuildLogKind_Command, ##__VA_ARGS__) 151 #define build_log_warning(...) build_log(BuildLogKind_Warning, ##__VA_ARGS__) 152 function void 153 build_log(BuildLogKind kind, char *format, ...) 154 { 155 va_list ap; 156 va_start(ap, format); 157 build_log_base(kind, format, ap); 158 va_end(ap); 159 } 160 161 #define build_fatal(fmt, ...) build_fatal_("%s: " fmt, __FUNCTION__, ##__VA_ARGS__) 162 function no_return void 163 build_fatal_(char *format, ...) 164 { 165 va_list ap; 166 va_start(ap, format); 167 build_log_base(BuildLogKind_Error, format, ap); 168 va_end(ap); 169 os_exit(1); 170 } 171 172 function b32 173 s8_contains(s8 s, u8 byte) 174 { 175 b32 result = 0; 176 for (iz i = 0 ; !result && i < s.len; i++) 177 result |= s.data[i] == byte; 178 return result; 179 } 180 181 function void 182 stream_push_command(Stream *s, CommandList *c) 183 { 184 if (!s->errors) { 185 for (iz i = 0; i < c->count; i++) { 186 s8 item = c_str_to_s8(c->data[i]); 187 if (item.len) { 188 b32 escape = s8_contains(item, ' ') || s8_contains(item, '"'); 189 if (escape) stream_append_byte(s, '\''); 190 stream_append_s8(s, item); 191 if (escape) stream_append_byte(s, '\''); 192 if (i != c->count - 1) stream_append_byte(s, ' '); 193 } 194 } 195 } 196 } 197 198 #if OS_LINUX 199 200 function b32 201 os_rename_file(char *name, char *new) 202 { 203 b32 result = rename(name, new) != -1; 204 return result; 205 } 206 207 function b32 208 os_remove_file(char *name) 209 { 210 b32 result = remove(name) != -1; 211 return result; 212 } 213 214 function void 215 os_make_directory(char *name) 216 { 217 mkdir(name, 0770); 218 } 219 220 function u64 221 os_get_filetime(char *file) 222 { 223 struct stat sb; 224 u64 result = (u64)-1; 225 if (stat(file, &sb) != -1) 226 result = (u64)sb.st_mtim.tv_sec; 227 return result; 228 } 229 230 function iptr 231 os_spawn_process(CommandList *cmd, Stream sb) 232 { 233 pid_t result = fork(); 234 switch (result) { 235 case -1: build_fatal("failed to fork command: %s: %s", cmd->data[0], strerror(errno)); break; 236 case 0: { 237 if (execvp(cmd->data[0], cmd->data) == -1) 238 build_fatal("failed to exec command: %s: %s", cmd->data[0], strerror(errno)); 239 unreachable(); 240 } break; 241 } 242 return (iptr)result; 243 } 244 245 function b32 246 os_wait_close_process(iptr handle) 247 { 248 b32 result = 0; 249 for (;;) { 250 i32 status; 251 iptr wait_pid = (iptr)waitpid((i32)handle, &status, 0); 252 if (wait_pid == -1) 253 build_fatal("failed to wait on child process: %s", strerror(errno)); 254 if (wait_pid == handle) { 255 if (WIFEXITED(status)) { 256 status = WEXITSTATUS(status); 257 /* TODO(rnp): logging */ 258 result = status == 0; 259 break; 260 } 261 if (WIFSIGNALED(status)) { 262 /* TODO(rnp): logging */ 263 result = 0; 264 break; 265 } 266 } else { 267 /* TODO(rnp): handle multiple children */ 268 InvalidCodePath; 269 } 270 } 271 return result; 272 } 273 274 #elif OS_WINDOWS 275 276 enum { 277 MOVEFILE_REPLACE_EXISTING = 0x01, 278 }; 279 280 W32(b32) CreateDirectoryA(c8 *, void *); 281 W32(b32) CreateProcessA(u8 *, u8 *, iptr, iptr, b32, u32, iptr, u8 *, iptr, iptr); 282 W32(b32) GetExitCodeProcess(iptr handle, u32 *); 283 W32(b32) GetFileTime(iptr, iptr, iptr, iptr); 284 W32(b32) MoveFileExA(c8 *, c8 *, u32); 285 286 function void 287 os_make_directory(char *name) 288 { 289 CreateDirectoryA(name, 0); 290 } 291 292 function b32 293 os_rename_file(char *name, char *new) 294 { 295 b32 result = MoveFileExA(name, new, MOVEFILE_REPLACE_EXISTING) != 0; 296 return result; 297 } 298 299 function b32 300 os_remove_file(char *name) 301 { 302 b32 result = DeleteFileA(name); 303 return result; 304 } 305 306 function u64 307 os_get_filetime(char *file) 308 { 309 u64 result = (u64)-1; 310 iptr h = CreateFileA(file, 0, 0, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); 311 if (h != INVALID_FILE) { 312 struct { u32 low, high; } w32_filetime; 313 GetFileTime(h, 0, 0, (iptr)&w32_filetime); 314 result = (u64)w32_filetime.high << 32ULL | w32_filetime.low; 315 CloseHandle(h); 316 } 317 return result; 318 } 319 320 function iptr 321 os_spawn_process(CommandList *cmd, Stream sb) 322 { 323 struct { 324 u32 cb; 325 u8 *reserved, *desktop, *title; 326 u32 x, y, x_size, y_size, x_count_chars, y_count_chars; 327 u32 fill_attr, flags; 328 u16 show_window, reserved_2; 329 u8 *reserved_3; 330 iptr std_input, std_output, std_error; 331 } w32_startup_info = { 332 .cb = sizeof(w32_startup_info), 333 .flags = 0x100, 334 .std_input = GetStdHandle(STD_INPUT_HANDLE), 335 .std_output = GetStdHandle(STD_OUTPUT_HANDLE), 336 .std_error = GetStdHandle(STD_ERROR_HANDLE), 337 }; 338 339 struct { 340 iptr phandle, thandle; 341 u32 pid, tid; 342 } w32_process_info = {0}; 343 344 /* TODO(rnp): warn if we need to clamp last string */ 345 sb.widx = MIN(sb.widx, (i32)(KB(32) - 1)); 346 if (sb.widx < sb.cap) sb.data[sb.widx] = 0; 347 else sb.data[sb.widx - 1] = 0; 348 349 iptr result = INVALID_FILE; 350 if (CreateProcessA(0, sb.data, 0, 0, 1, 0, 0, 0, (iptr)&w32_startup_info, 351 (iptr)&w32_process_info)) 352 { 353 CloseHandle(w32_process_info.thandle); 354 result = w32_process_info.phandle; 355 } 356 return result; 357 } 358 359 function b32 360 os_wait_close_process(iptr handle) 361 { 362 b32 result = WaitForSingleObject(handle, (u32)-1) != 0xFFFFFFFFUL; 363 if (result) { 364 u32 status; 365 GetExitCodeProcess(handle, &status); 366 result = status == 0; 367 } 368 CloseHandle(handle); 369 return result; 370 } 371 372 #endif 373 374 #define needs_rebuild(b, ...) needs_rebuild_(b, ((char *[]){__VA_ARGS__}), \ 375 (sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) 376 function b32 377 needs_rebuild_(char *binary, char *deps[], iz deps_count) 378 { 379 u64 binary_filetime = os_get_filetime(binary); 380 u64 argv0_filetime = os_get_filetime(g_argv0); 381 b32 result = (binary_filetime == (u64)-1) | (argv0_filetime > binary_filetime); 382 for (iz i = 0; i < deps_count; i++) { 383 u64 filetime = os_get_filetime(deps[i]); 384 result |= (filetime == (u64)-1) | (filetime > binary_filetime); 385 } 386 return result; 387 } 388 389 function b32 390 run_synchronous(Arena a, CommandList *command) 391 { 392 Stream sb = arena_stream(a); 393 stream_push_command(&sb, command); 394 build_log_command("%.*s", (i32)sb.widx, sb.data); 395 return os_wait_close_process(os_spawn_process(command, sb)); 396 } 397 398 function CommandList 399 cmd_base(Arena *a, Options *o) 400 { 401 CommandList result = {0}; 402 cmd_append(a, &result, COMPILER); 403 404 if (!is_msvc) { 405 /* TODO(rnp): support cross compiling with clang */ 406 if (!o->generic) cmd_append(a, &result, "-march=native"); 407 else if (is_amd64) cmd_append(a, &result, "-march=x86-64-v3"); 408 else if (is_aarch64) cmd_append(a, &result, "-march=armv8"); 409 } 410 411 cmd_append(a, &result, COMMON_FLAGS, "-Iexternal/include"); 412 if (o->debug) cmd_append(a, &result, DEBUG_FLAGS); 413 else cmd_append(a, &result, OPTIMIZED_FLAGS); 414 415 /* NOTE: ancient gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80454 */ 416 if (is_gcc) cmd_append(a, &result, "-Wno-missing-braces"); 417 418 if (is_w32 && is_clang) cmd_append(a, &result, "-fms-extensions"); 419 420 if (o->debug && is_unix) cmd_append(a, &result, "-gdwarf-4"); 421 422 /* NOTE(rnp): need to avoid w32-gcc for ci */ 423 b32 sanitize = o->sanitize && !is_msvc && !(is_w32 && is_gcc); 424 if (sanitize) { 425 cmd_append(a, &result, "-fsanitize=address,undefined"); 426 /* NOTE(rnp): impossible to autodetect on GCC versions < 14 (ci has 13) */ 427 cmd_append(a, &result, "-DASAN_ACTIVE=1"); 428 } else { 429 cmd_append(a, &result, "-DASAN_ACTIVE=0"); 430 } 431 if (!sanitize && o->sanitize) build_log_warning("santizers not supported with this compiler"); 432 433 return result; 434 } 435 436 function void 437 check_rebuild_self(Arena arena, i32 argc, char *argv[]) 438 { 439 char *binary = shift(argv, argc); 440 if (needs_rebuild(binary, __FILE__, "os_win32.c", "os_linux.c", "util.c", "util.h", "beamformer_parameters.h")) { 441 Stream name_buffer = arena_stream(arena); 442 stream_append_s8s(&name_buffer, c_str_to_s8(binary), s8(".old")); 443 char *old_name = (char *)arena_stream_commit_zero(&arena, &name_buffer).data; 444 445 if (!os_rename_file(binary, old_name)) 446 build_fatal("failed to move: %s -> %s", binary, old_name); 447 448 Options options = {0}; 449 CommandList c = cmd_base(&arena, &options); 450 cmd_append(&arena, &c, EXTRA_FLAGS); 451 if (!is_msvc) cmd_append(&arena, &c, "-Wno-unused-function"); 452 cmd_append(&arena, &c, __FILE__, OUTPUT_EXE(binary)); 453 if (is_msvc) cmd_append(&arena, &c, "/link", "-incremental:no", "-opt:ref"); 454 cmd_append(&arena, &c, (void *)0); 455 if (!run_synchronous(arena, &c)) { 456 os_rename_file(old_name, binary); 457 build_fatal("failed to rebuild self"); 458 } 459 os_remove_file(old_name); 460 461 c.count = 0; 462 cmd_append(&arena, &c, binary); 463 cmd_append_count(&arena, &c, argv, argc); 464 cmd_append(&arena, &c, (void *)0); 465 if (!run_synchronous(arena, &c)) 466 os_exit(1); 467 468 os_exit(0); 469 } 470 } 471 472 function b32 473 s8_equal(s8 a, s8 b) 474 { 475 b32 result = a.len == b.len; 476 for (iz i = 0; result && i < a.len; i++) 477 result = a.data[i] == b.data[i]; 478 return result; 479 } 480 481 function void 482 usage(char *argv0) 483 { 484 printf("%s [--bake-shaders] [--debug] [--sanitize] [--time]\n" 485 " --debug: dynamically link and build with debug symbols\n" 486 " --generic: compile for a generic target (x86-64-v3 or armv8 with NEON)\n" 487 " --sanitize: build with ASAN and UBSAN\n" 488 " --tests: also build programs in tests/\n" 489 " --time: print build time\n" 490 , argv0); 491 os_exit(0); 492 } 493 494 function Options 495 parse_options(i32 argc, char *argv[]) 496 { 497 Options result = {0}; 498 499 char *argv0 = shift(argv, argc); 500 while (argc > 0) { 501 char *arg = shift(argv, argc); 502 s8 str = c_str_to_s8(arg); 503 if (s8_equal(str, s8("--bake-shaders"))) { 504 result.bake_shaders = 1; 505 } else if (s8_equal(str, s8("--debug"))) { 506 result.debug = 1; 507 } else if (s8_equal(str, s8("--generic"))) { 508 result.generic = 1; 509 } else if (s8_equal(str, s8("--sanitize"))) { 510 result.sanitize = 1; 511 } else if (s8_equal(str, s8("--tests"))) { 512 result.tests = 1; 513 } else if (s8_equal(str, s8("--time"))) { 514 result.time = 1; 515 } else { 516 usage(argv0); 517 } 518 } 519 520 return result; 521 } 522 523 /* NOTE(rnp): produce pdbs on w32 */ 524 function void 525 cmd_pdb(Arena *a, CommandList *cmd, char *name) 526 { 527 if (is_w32 && is_clang) { 528 cmd_append(a, cmd, "-fuse-ld=lld", "-g", "-gcodeview", "-Wl,--pdb="); 529 } else if (is_msvc) { 530 Stream sb = arena_stream(*a); 531 stream_append_s8s(&sb, s8("-PDB:"), c_str_to_s8(name), s8(".pdb")); 532 char *pdb = (char *)arena_stream_commit_zero(a, &sb).data; 533 cmd_append(a, cmd, "/link", "-incremental:no", "-opt:ref", "-DEBUG", pdb); 534 } 535 } 536 537 function void 538 git_submodule_update(Arena a, char *name) 539 { 540 Stream sb = arena_stream(a); 541 stream_append_s8s(&sb, c_str_to_s8(name), s8(OS_PATH_SEPARATOR), s8(".git")); 542 arena_stream_commit_zero(&a, &sb); 543 544 CommandList git = {0}; 545 /* NOTE(rnp): cryptic bs needed to get a simple exit code if name is dirty */ 546 cmd_append(&a, &git, "git", "diff-index", "--quiet", "HEAD", "--", name, (void *)0); 547 if (!os_file_exists((c8 *)sb.data) || !run_synchronous(a, &git)) { 548 git.count = 1; 549 cmd_append(&a, &git, "submodule", "update", "--init", "--depth=1", name, (void *)0); 550 if (!run_synchronous(a, &git)) 551 build_fatal("failed to clone required module: %s", name); 552 } 553 } 554 555 function b32 556 build_shared_library(Arena a, CommandList cc, char *name, char *output, char **libs, iz libs_count, char **srcs, iz srcs_count) 557 { 558 cmd_append_count(&a, &cc, srcs, srcs_count); 559 cmd_append(&a, &cc, OUTPUT_DLL(output)); 560 cmd_pdb(&a, &cc, name); 561 cmd_append_count(&a, &cc, libs, libs_count); 562 cmd_append(&a, &cc, (void *)0); 563 b32 result = run_synchronous(a, &cc); 564 if (!result) build_log_failure("%s", output); 565 return result; 566 } 567 568 function b32 569 cc_single_file(Arena a, CommandList cc, char *exe, char *src, char *dest, char **tail, iz tail_count) 570 { 571 char *executable[] = {src, is_msvc? "/Fe:" : "-o", dest}; 572 char *object[] = {is_msvc? "/c" : "-c", src, is_msvc? "/Fo:" : "-o", dest}; 573 574 575 cmd_append_count(&a, &cc, exe? executable : object, 576 exe? countof(executable) : countof(object)); 577 if (exe) cmd_pdb(&a, &cc, exe); 578 cmd_append_count(&a, &cc, tail, tail_count); 579 cmd_append(&a, &cc, (void *)0); 580 b32 result = run_synchronous(a, &cc); 581 if (!result) build_log_failure("%s", dest); 582 return result; 583 } 584 585 function b32 586 build_static_library_from_objects(Arena a, char *name, char **flags, iz flags_count, char **objects, iz count) 587 { 588 CommandList ar = {0}; 589 cmd_append(&a, &ar, STATIC_LIBRARY_BEGIN(name)); 590 cmd_append_count(&a, &ar, flags, flags_count); 591 cmd_append_count(&a, &ar, objects, count); 592 cmd_append(&a, &ar, (void *)0); 593 b32 result = run_synchronous(a, &ar); 594 if (!result) build_log_failure("%s", name); 595 return result; 596 } 597 598 function b32 599 build_static_library(Arena a, CommandList cc, char *name, char **deps, char **outputs, iz count) 600 { 601 /* TODO(rnp): refactor to not need outputs */ 602 b32 result = 1; 603 for (iz i = 0; i < count; i++) 604 result &= cc_single_file(a, cc, 0, deps[i], outputs[i], 0, 0); 605 if (result) result = build_static_library_from_objects(a, name, 0, 0, outputs, count); 606 return result; 607 } 608 609 function b32 610 check_build_raylib(Arena a, CommandList cc, b32 shared) 611 { 612 b32 result = 1; 613 char *libraylib = shared ? OS_SHARED_LINK_LIB("raylib") : OUTPUT_LIB(OS_STATIC_LIB("raylib")); 614 if (needs_rebuild(libraylib, __FILE__, "external/include/rlgl.h", "external/raylib")) { 615 git_submodule_update(a, "external/raylib"); 616 os_copy_file("external/raylib/src/rlgl.h", "external/include/rlgl.h"); 617 618 if (is_unix) cmd_append(&a, &cc, "-D_GLFW_X11"); 619 cmd_append(&a, &cc, "-DPLATFORM_DESKTOP_GLFW"); 620 if (!is_msvc) cmd_append(&a, &cc, "-Wno-unused-but-set-variable"); 621 cmd_append(&a, &cc, "-Iexternal/raylib/src", "-Iexternal/raylib/src/external/glfw/include"); 622 #define RAYLIB_SOURCES \ 623 X(rglfw) \ 624 X(rshapes) \ 625 X(rtext) \ 626 X(rtextures) \ 627 X(utils) 628 #define X(name) "external/raylib/src/" #name ".c", 629 char *srcs[] = {"external/rcore_extended.c", RAYLIB_SOURCES}; 630 #undef X 631 #define X(name) OUTPUT(OBJECT(#name)), 632 char *outs[] = {OUTPUT(OBJECT("rcore_extended")), RAYLIB_SOURCES}; 633 #undef X 634 635 if (shared) { 636 char *libs[] = {LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), LINK_LIB("winmm")}; 637 iz libs_count = is_w32 ? countof(libs) : 0; 638 cmd_append(&a, &cc, "-DBUILD_LIBTYPE_SHARED", "-D_GLFW_BUILD_DLL"); 639 result = build_shared_library(a, cc, "raylib", libraylib, libs, libs_count, srcs, countof(srcs)); 640 } else { 641 result = build_static_library(a, cc, libraylib, srcs, outs, countof(srcs)); 642 } 643 } 644 return result; 645 } 646 647 function b32 648 build_helper_library(Arena arena, CommandList cc) 649 { 650 ///////////// 651 // library 652 char *library = OUTPUT(OS_SHARED_LIB("ogl_beamformer_lib")); 653 char *libs[] = {LINK_LIB("Synchronization")}; 654 iz libs_count = is_w32 ? countof(libs) : 0; 655 656 if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function"); 657 b32 result = build_shared_library(arena, cc, "ogl_beamformer_lib", library, 658 libs, libs_count, 659 arg_list(char *, "helpers/ogl_beamformer_lib.c")); 660 return result; 661 } 662 663 function b32 664 build_beamformer_as_library(Arena arena, CommandList cc) 665 { 666 char *library = OS_SHARED_LIB("beamformer"); 667 char *libs[] = {!is_msvc? "-L." : "", LINK_LIB("raylib"), LINK_LIB("gdi32"), 668 LINK_LIB("shell32"), LINK_LIB("user32"), LINK_LIB("opengl32"), 669 LINK_LIB("winmm"), LINK_LIB("Synchronization"), OUTPUT("main.lib")}; 670 iz libs_count = is_w32 ? countof(libs) : 0; 671 cmd_append(&arena, &cc, "-D_BEAMFORMER_DLL"); 672 b32 result = build_shared_library(arena, cc, "beamformer", library, 673 libs, libs_count, arg_list(char *, "beamformer.c")); 674 return result; 675 } 676 677 function b32 678 build_tests(Arena arena, CommandList cc) 679 { 680 #define TEST_PROGRAMS \ 681 X("throughput", LINK_LIB("zstd"), W32_DECL(LINK_LIB("Synchronization"))) 682 683 os_make_directory(OUTPUT("tests")); 684 if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function"); 685 cmd_append(&arena, &cc, "-I.", "-Ihelpers"); 686 687 b32 result = 1; 688 iz cc_count = cc.count; 689 #define X(prog, ...) \ 690 result &= cc_single_file(arena, cc, prog, "tests/" prog ".c", \ 691 OUTPUT("tests/" prog), \ 692 arg_list(char *, ##__VA_ARGS__)); \ 693 cc.count = cc_count; 694 TEST_PROGRAMS 695 #undef X 696 return result; 697 } 698 699 typedef struct { 700 s8 *data; 701 iz count; 702 iz capacity; 703 } s8_list; 704 705 function s8 706 s8_chop(s8 *in, iz count) 707 { 708 count = CLAMP(count, 0, in->len); 709 s8 result = {.data = in->data, .len = count}; 710 in->data += count; 711 in->len -= count; 712 return result; 713 } 714 715 function void 716 s8_split(s8 str, s8 *left, s8 *right, u8 byte) 717 { 718 iz i; 719 for (i = 0; i < str.len; i++) if (str.data[i] == byte) break; 720 721 if (left) *left = (s8){.data = str.data, .len = i}; 722 if (right) { 723 right->data = str.data + i + 1; 724 right->len = MAX(0, str.len - (i + 1)); 725 } 726 } 727 728 function s8 729 s8_trim(s8 in) 730 { 731 s8 result = in; 732 for (iz i = 0; i < in.len && *result.data == ' '; i++) result.data++; 733 result.len -= result.data - in.data; 734 for (; result.len > 0 && result.data[result.len - 1] == ' '; result.len--); 735 return result; 736 } 737 738 function void 739 s8_list_from_s8(s8_list *list, Arena *arena, s8 str) 740 { 741 s8 right = str, left; 742 while (right.len > 0) { 743 s8_split(right, &left, &right, ' '); 744 left = s8_trim(left); 745 if (left.len > 0) { *da_push(arena, list) = left; } 746 } 747 } 748 749 typedef struct { 750 Stream stream; 751 Arena scratch; 752 i32 indentation_level; 753 } MetaprogramContext; 754 755 function b32 756 meta_write_and_reset(MetaprogramContext *m, char *file) 757 { 758 b32 result = os_write_new_file(file, stream_to_s8(&m->stream)); 759 if (!result) build_log_failure("%s", file); 760 m->stream.widx = 0; 761 m->indentation_level = 0; 762 return result; 763 } 764 765 #define meta_push(m, ...) meta_push_(m, arg_list(s8, __VA_ARGS__)) 766 function void 767 meta_push_(MetaprogramContext *m, s8 *items, iz count) 768 { 769 stream_append_s8s_(&m->stream, items, count); 770 } 771 772 #define meta_pad(m, b, n) stream_pad(&(m)->stream, (b), (n)) 773 #define meta_indent(m) meta_pad((m), '\t', (m)->indentation_level) 774 #define meta_begin_line(m, ...) do { meta_indent(m); meta_push(m, __VA_ARGS__); } while(0) 775 #define meta_end_line(m, ...) meta_push(m, ##__VA_ARGS__, s8("\n")) 776 #define meta_push_line(m, ...) do { meta_indent(m); meta_push(m, ##__VA_ARGS__, s8("\n")); } while(0) 777 #define meta_begin_scope(m, ...) do { meta_push_line(m, __VA_ARGS__); (m)->indentation_level++; } while(0) 778 #define meta_end_scope(m, ...) do { (m)->indentation_level--; meta_push_line(m, __VA_ARGS__); } while(0) 779 #define meta_push_u64(m, n) stream_append_u64(&(m)->stream, (n)) 780 #define meta_push_i64(m, n) stream_append_i64(&(m)->stream, (n)) 781 #define meta_push_u64_hex(m, n) stream_append_hex_u64(&(m)->stream, (n)) 782 783 #define meta_begin_matlab_class_cracker(_1, _2, FN, ...) FN 784 #define meta_begin_matlab_class_1(m, name) meta_begin_scope(m, s8("classdef " name)) 785 #define meta_begin_matlab_class_2(m, name, type) \ 786 meta_begin_scope(m, s8("classdef " name " < " type)) 787 788 #define meta_begin_matlab_class(m, ...) \ 789 meta_begin_matlab_class_cracker(__VA_ARGS__, \ 790 meta_begin_matlab_class_2, \ 791 meta_begin_matlab_class_1)(m, __VA_ARGS__) 792 793 function void 794 meta_push_matlab_property(MetaprogramContext *m, s8 name, u64 length, s8 kind) 795 { 796 meta_begin_line(m, name, s8("(1,")); 797 meta_push_u64(m, (u64)length); 798 meta_end_line(m, s8(")"), kind.len > 0 ? s8(" ") : s8(""), kind); 799 } 800 801 function void 802 meta_push_matlab_enum_with_value(MetaprogramContext *m, s8 name, i32 value) 803 { 804 meta_indent(m); 805 stream_append_s8s(&m->stream, name, s8(" (")); 806 stream_append_i64(&m->stream, value); 807 stream_append_s8(&m->stream, s8(")\n")); 808 } 809 810 function b32 811 meta_end_and_write_matlab(MetaprogramContext *m, char *path) 812 { 813 while (m->indentation_level > 0) meta_end_scope(m, s8("end")); 814 b32 result = meta_write_and_reset(m, path); 815 return result; 816 } 817 818 #define META_ENTRY_KIND_LIST \ 819 X(Invalid) \ 820 X(Array) \ 821 X(Bake) \ 822 X(BakeInt) \ 823 X(BakeFloat) \ 824 X(BeginScope) \ 825 X(Emit) \ 826 X(Embed) \ 827 X(EndScope) \ 828 X(Enumeration) \ 829 X(Expand) \ 830 X(Flags) \ 831 X(String) \ 832 X(Shader) \ 833 X(ShaderGroup) \ 834 X(SubShader) \ 835 X(Table) 836 837 typedef enum { 838 #define X(k, ...) MetaEntryKind_## k, 839 META_ENTRY_KIND_LIST 840 #undef X 841 MetaEntryKind_Count, 842 } MetaEntryKind; 843 844 #define X(k, ...) #k, 845 read_only global char *meta_entry_kind_strings[] = {META_ENTRY_KIND_LIST}; 846 #undef X 847 848 typedef struct { u32 line, column; } MetaLocation; 849 850 #define META_ENTRY_ARGUMENT_KIND_LIST \ 851 X(None) \ 852 X(String) \ 853 X(Array) 854 855 #define X(k, ...) MetaEntryArgumentKind_## k, 856 typedef enum {META_ENTRY_ARGUMENT_KIND_LIST} MetaEntryArgumentKind; 857 #undef X 858 859 typedef struct { 860 MetaEntryArgumentKind kind; 861 MetaLocation location; 862 union { 863 s8 string; 864 struct { 865 s8 *strings; 866 u64 count; 867 }; 868 }; 869 } MetaEntryArgument; 870 871 typedef struct { 872 MetaEntryKind kind; 873 u32 argument_count; 874 MetaEntryArgument *arguments; 875 s8 name; 876 MetaLocation location; 877 } MetaEntry; 878 879 typedef struct { 880 MetaEntry *data; 881 iz count; 882 iz capacity; 883 s8 raw; 884 } MetaEntryStack; 885 886 #define META_PARSE_TOKEN_LIST \ 887 X('@', Entry) \ 888 X('`', RawString) \ 889 X('(', BeginArgs) \ 890 X(')', EndArgs) \ 891 X('[', BeginArray) \ 892 X(']', EndArray) \ 893 X('{', BeginScope) \ 894 X('}', EndScope) 895 896 typedef enum { 897 MetaParseToken_EOF, 898 MetaParseToken_String, 899 #define X(__1, kind, ...) MetaParseToken_## kind, 900 META_PARSE_TOKEN_LIST 901 #undef X 902 MetaParseToken_Count, 903 } MetaParseToken; 904 905 typedef union { 906 MetaEntryKind kind; 907 s8 string; 908 } MetaParseUnion; 909 910 typedef struct { 911 s8 s; 912 MetaLocation location; 913 } MetaParsePoint; 914 915 typedef struct { 916 MetaParsePoint p; 917 MetaParseUnion u; 918 MetaParsePoint save_point; 919 } MetaParser; 920 921 global char *compiler_file; 922 global jmp_buf compiler_jmp_buf; 923 924 #define meta_parser_save(v) (v)->save_point = (v)->p 925 #define meta_parser_restore(v) swap((v)->p, (v)->save_point) 926 #define meta_parser_commit(v) meta_parser_restore(v) 927 928 #define meta_compiler_error_message(loc, format, ...) \ 929 fprintf(stderr, "%s:%u:%u: error: "format, compiler_file, \ 930 loc.line + 1, loc.column + 1, ##__VA_ARGS__) 931 932 #define meta_compiler_error(loc, format, ...) do { \ 933 meta_compiler_error_message(loc, format, ##__VA_ARGS__); \ 934 meta_error(); \ 935 } while (0) 936 937 #define meta_entry_error(e, ...) meta_entry_error_column((e), (i32)(e)->location.column, __VA_ARGS__) 938 #define meta_entry_error_column(e, column, ...) do { \ 939 meta_compiler_error_message((e)->location, __VA_ARGS__); \ 940 meta_entry_print((e), 2 * (column), 0); \ 941 meta_error(); \ 942 } while(0) 943 944 #define meta_entry_pair_error(e, prefix, base_kind) \ 945 meta_entry_error(e, prefix"@%s() in @%s()\n", \ 946 meta_entry_kind_strings[(e)->kind], \ 947 meta_entry_kind_strings[(base_kind)]) 948 949 #define meta_entry_nesting_error(e, base_kind) meta_entry_pair_error(e, "invalid nesting: ", base_kind) 950 951 #define meta_entry_error_location(e, loc, ...) do { \ 952 meta_compiler_error_message((loc), __VA_ARGS__); \ 953 meta_entry_print((e), 1, (i32)(loc).column); \ 954 meta_error(); \ 955 } while (0) 956 957 function no_return void 958 meta_error(void) 959 { 960 assert(0); 961 longjmp(compiler_jmp_buf, 1); 962 } 963 964 function void 965 meta_entry_print(MetaEntry *e, i32 indent, i32 caret) 966 { 967 char *kind = meta_entry_kind_strings[e->kind]; 968 if (e->kind == MetaEntryKind_BeginScope) kind = "{"; 969 if (e->kind == MetaEntryKind_EndScope) kind = "}"; 970 971 fprintf(stderr, "%*s@%s", indent, "", kind); 972 973 if (e->argument_count) { 974 fprintf(stderr, "("); 975 for (u32 i = 0; i < e->argument_count; i++) { 976 MetaEntryArgument *a = e->arguments + i; 977 if (i != 0) fprintf(stderr, " "); 978 if (a->kind == MetaEntryArgumentKind_Array) { 979 fprintf(stderr, "["); 980 for (u64 j = 0; j < a->count; j++) { 981 if (j != 0) fprintf(stderr, " "); 982 fprintf(stderr, "%.*s", (i32)a->strings[j].len, a->strings[j].data); 983 } 984 fprintf(stderr, "]"); 985 } else { 986 fprintf(stderr, "%.*s", (i32)a->string.len, a->string.data); 987 } 988 } 989 fprintf(stderr, ")"); 990 } 991 if (e->name.len) fprintf(stderr, " %.*s", (i32)e->name.len, e->name.data); 992 993 if (caret >= 0) fprintf(stderr, "\n%*s^", indent + caret, ""); 994 995 fprintf(stderr, "\n"); 996 } 997 998 function MetaEntryKind 999 meta_entry_kind_from_string(s8 s) 1000 { 1001 #define X(k, ...) s8_comp(#k), 1002 read_only local_persist s8 kinds[] = {META_ENTRY_KIND_LIST}; 1003 #undef X 1004 MetaEntryKind result = MetaEntryKind_Invalid; 1005 for EachNonZeroEnumValue(MetaEntryKind, kind) { 1006 if (s8_equal(kinds[kind], s)) { 1007 result = kind; 1008 break; 1009 } 1010 } 1011 return result; 1012 } 1013 1014 function void 1015 meta_parser_trim(MetaParser *p) 1016 { 1017 u8 *s, *end = p->p.s.data + p->p.s.len; 1018 b32 done = 0; 1019 b32 comment = 0; 1020 for (s = p->p.s.data; !done && s != end;) { 1021 switch (*s) { 1022 case '\r': case '\t': case ' ': 1023 { 1024 p->p.location.column++; 1025 }break; 1026 case '\n':{ p->p.location.line++; p->p.location.column = 0; comment = 0; }break; 1027 case '/':{ 1028 comment = ((s + 1) != end && s[1] == '/'); 1029 } /* FALLTHROUGH */ 1030 default:{done = !comment;}break; 1031 } 1032 if (!done) s++; 1033 } 1034 p->p.s.data = s; 1035 p->p.s.len = end - s; 1036 } 1037 1038 function s8 1039 meta_parser_extract_raw_string(MetaParser *p) 1040 { 1041 s8 result = {.data = p->p.s.data}; 1042 for (; result.len < p->p.s.len; result.len++) { 1043 u8 byte = p->p.s.data[result.len]; 1044 p->p.location.column++; 1045 if (byte == '`') { 1046 break; 1047 } else if (byte == '\n') { 1048 p->p.location.column = 0; 1049 p->p.location.line++; 1050 } 1051 } 1052 p->p.s.data += (result.len + 1); 1053 p->p.s.len -= (result.len + 1); 1054 return result; 1055 } 1056 1057 function s8 1058 meta_parser_extract_string(MetaParser *p) 1059 { 1060 s8 result = {.data = p->p.s.data}; 1061 for (; result.len < p->p.s.len; result.len++) { 1062 b32 done = 0; 1063 switch (p->p.s.data[result.len]) { 1064 #define X(t, ...) case t: 1065 META_PARSE_TOKEN_LIST 1066 #undef X 1067 case ' ': case '\n': case '\r': case '\t': 1068 {done = 1;}break; 1069 case '/':{ 1070 done = (result.len + 1 < p->p.s.len) && (p->p.s.data[result.len + 1] == '/'); 1071 }break; 1072 default:{}break; 1073 } 1074 if (done) break; 1075 } 1076 p->p.location.column += (u32)result.len; 1077 p->p.s.data += result.len; 1078 p->p.s.len -= result.len; 1079 return result; 1080 } 1081 1082 function s8 1083 meta_parser_token_name(MetaParser *p, MetaParseToken t) 1084 { 1085 s8 result = s8("\"invalid\""); 1086 read_only local_persist s8 names[MetaParseToken_Count] = { 1087 [MetaParseToken_EOF] = s8_comp("\"EOF\""), 1088 #define X(k, v, ...) [MetaParseToken_## v] = s8_comp(#k), 1089 META_PARSE_TOKEN_LIST 1090 #undef X 1091 }; 1092 if (t >= 0 && t < countof(names)) result = names[t]; 1093 if (t == MetaParseToken_String) result = p->u.string; 1094 if (t == MetaParseToken_RawString) result = (s8){.data = p->u.string.data - 1, .len = p->u.string.len + 1}; 1095 return result; 1096 } 1097 1098 function MetaParseToken 1099 meta_parser_token(MetaParser *p) 1100 { 1101 MetaParseToken result = MetaParseToken_EOF; 1102 meta_parser_save(p); 1103 if (p->p.s.len > 0) { 1104 b32 chop = 1; 1105 switch (p->p.s.data[0]) { 1106 #define X(t, kind, ...) case t:{ result = MetaParseToken_## kind; }break; 1107 META_PARSE_TOKEN_LIST 1108 #undef X 1109 default:{ result = MetaParseToken_String; chop = 0; }break; 1110 } 1111 if (chop) { s8_chop(&p->p.s, 1); p->p.location.column++; } 1112 1113 if (result != MetaParseToken_RawString) meta_parser_trim(p); 1114 switch (result) { 1115 case MetaParseToken_RawString:{ p->u.string = meta_parser_extract_raw_string(p); }break; 1116 case MetaParseToken_String:{ p->u.string = meta_parser_extract_string(p); }break; 1117 1118 /* NOTE(rnp): '{' and '}' are shorthand for @BeginScope and @EndScope */ 1119 case MetaParseToken_BeginScope:{ p->u.kind = MetaEntryKind_BeginScope; }break; 1120 case MetaParseToken_EndScope:{ p->u.kind = MetaEntryKind_EndScope; }break; 1121 1122 /* NOTE(rnp): loose '[' implies implicit @Array() */ 1123 case MetaParseToken_BeginArray:{ p->u.kind = MetaEntryKind_Array; }break; 1124 1125 case MetaParseToken_Entry:{ 1126 s8 kind = meta_parser_extract_string(p); 1127 p->u.kind = meta_entry_kind_from_string(kind); 1128 if (p->u.kind == MetaEntryKind_Invalid) { 1129 meta_compiler_error(p->p.location, "invalid keyword: @%.*s\n", (i32)kind.len, kind.data); 1130 } 1131 }break; 1132 default:{}break; 1133 } 1134 meta_parser_trim(p); 1135 } 1136 1137 return result; 1138 } 1139 1140 function MetaParseToken 1141 meta_parser_peek_token(MetaParser *p) 1142 { 1143 MetaParseToken result = meta_parser_token(p); 1144 meta_parser_restore(p); 1145 return result; 1146 } 1147 1148 function void 1149 meta_parser_unexpected_token(MetaParser *p, MetaParseToken t) 1150 { 1151 meta_parser_restore(p); 1152 s8 token_name = meta_parser_token_name(p, t); 1153 meta_compiler_error(p->p.location, "unexpected token: %.*s\n", (i32)token_name.len, token_name.data); 1154 } 1155 1156 function void 1157 meta_parser_fill_argument_array(MetaParser *p, MetaEntryArgument *array, Arena *arena) 1158 { 1159 array->kind = MetaEntryArgumentKind_Array; 1160 array->strings = arena_aligned_start(*arena, alignof(s8)); 1161 array->location = p->p.location; 1162 for (MetaParseToken token = meta_parser_token(p); 1163 token != MetaParseToken_EndArray; 1164 token = meta_parser_token(p)) 1165 { 1166 switch (token) { 1167 case MetaParseToken_RawString: 1168 case MetaParseToken_String: 1169 { 1170 assert((u8 *)(array->strings + array->count) == arena->beg); 1171 *push_struct(arena, s8) = p->u.string; 1172 array->count++; 1173 }break; 1174 default:{ meta_parser_unexpected_token(p, token); }break; 1175 } 1176 } 1177 } 1178 1179 function void 1180 meta_parser_arguments(MetaParser *p, MetaEntry *e, Arena *arena) 1181 { 1182 if (meta_parser_peek_token(p) == MetaParseToken_BeginArgs) { 1183 meta_parser_commit(p); 1184 1185 e->arguments = arena_aligned_start(*arena, alignof(MetaEntryArgument)); 1186 for (MetaParseToken token = meta_parser_token(p); 1187 token != MetaParseToken_EndArgs; 1188 token = meta_parser_token(p)) 1189 { 1190 e->argument_count++; 1191 MetaEntryArgument *arg = push_struct(arena, MetaEntryArgument); 1192 switch (token) { 1193 case MetaParseToken_RawString: 1194 case MetaParseToken_String: 1195 { 1196 arg->kind = MetaEntryArgumentKind_String; 1197 arg->string = p->u.string; 1198 arg->location = p->p.location; 1199 }break; 1200 case MetaParseToken_BeginArray:{ 1201 meta_parser_fill_argument_array(p, arg, arena); 1202 }break; 1203 default:{ meta_parser_unexpected_token(p, token); }break; 1204 } 1205 } 1206 } 1207 } 1208 1209 typedef struct { 1210 MetaEntry *start; 1211 MetaEntry *one_past_last; 1212 iz consumed; 1213 } MetaEntryScope; 1214 1215 function MetaEntryScope 1216 meta_entry_extract_scope(MetaEntry *base, iz entry_count) 1217 { 1218 assert(base->kind != MetaEntryKind_BeginScope || base->kind != MetaEntryKind_EndScope); 1219 assert(entry_count > 0); 1220 1221 MetaEntryScope result = {.start = base + 1, .consumed = 1}; 1222 iz sub_scope = 0; 1223 for (MetaEntry *e = result.start; result.consumed < entry_count; result.consumed++, e++) { 1224 switch (e->kind) { 1225 case MetaEntryKind_BeginScope:{ sub_scope++; }break; 1226 case MetaEntryKind_EndScope:{ sub_scope--; }break; 1227 default:{}break; 1228 } 1229 if (sub_scope == 0) break; 1230 } 1231 1232 if (sub_scope != 0) 1233 meta_entry_error(base, "unclosed scope for entry\n"); 1234 1235 result.one_past_last = base + result.consumed; 1236 if (result.start->kind == MetaEntryKind_BeginScope) result.start++; 1237 if (result.one_past_last == result.start) result.one_past_last++; 1238 1239 return result; 1240 } 1241 1242 function MetaEntryStack 1243 meta_entry_stack_from_file(Arena *arena, Arena scratch, char *file) 1244 { 1245 MetaParser parser = {.p.s = os_read_whole_file(arena, file)}; 1246 MetaEntryStack result = {.raw = parser.p.s}; 1247 1248 compiler_file = file; 1249 1250 meta_parser_trim(&parser); 1251 1252 for (MetaParseToken token = meta_parser_token(&parser); 1253 token != MetaParseToken_EOF; 1254 token = meta_parser_token(&parser)) 1255 { 1256 MetaEntry *e = da_push(arena, &result); 1257 switch (token) { 1258 case MetaParseToken_RawString:{ 1259 e->kind = MetaEntryKind_String; 1260 e->location = parser.save_point.location; 1261 e->name = parser.u.string; 1262 }break; 1263 case MetaParseToken_BeginArray: 1264 case MetaParseToken_BeginScope: 1265 case MetaParseToken_EndScope: 1266 case MetaParseToken_Entry: 1267 { 1268 e->kind = parser.u.kind; 1269 e->location = parser.save_point.location; 1270 1271 if (token == MetaParseToken_Entry) 1272 meta_parser_arguments(&parser, e, arena); 1273 1274 if (token == MetaParseToken_BeginArray) { 1275 MetaEntryArgument *a = e->arguments = push_struct(arena, MetaEntryArgument); 1276 e->argument_count = 1; 1277 meta_parser_fill_argument_array(&parser, a, arena); 1278 } 1279 1280 if (meta_parser_peek_token(&parser) == MetaParseToken_String) { 1281 meta_parser_commit(&parser); 1282 e->name = parser.u.string; 1283 } 1284 }break; 1285 1286 default:{ meta_parser_unexpected_token(&parser, token); }break; 1287 } 1288 } 1289 1290 return result; 1291 } 1292 1293 #define meta_entry_argument_expected(e, ...) \ 1294 meta_entry_argument_expected_((e), arg_list(s8, __VA_ARGS__)) 1295 function void 1296 meta_entry_argument_expected_(MetaEntry *e, s8 *args, uz count) 1297 { 1298 if (e->argument_count != count) { 1299 meta_compiler_error_message(e->location, "incorrect argument count for entry %s() got: %u expected: %u\n", 1300 meta_entry_kind_strings[e->kind], e->argument_count, (u32)count); 1301 fprintf(stderr, " format: @%s(", meta_entry_kind_strings[e->kind]); 1302 for (uz i = 0; i < count; i++) { 1303 if (i != 0) fprintf(stderr, ", "); 1304 fprintf(stderr, "%.*s", (i32)args[i].len, args[i].data); 1305 } 1306 fprintf(stderr, ")\n"); 1307 meta_error(); 1308 } 1309 } 1310 1311 function MetaEntryArgument 1312 meta_entry_argument_expect(MetaEntry *e, u32 index, MetaEntryArgumentKind kind) 1313 { 1314 #define X(k, ...) #k, 1315 read_only local_persist char *kinds[] = {META_ENTRY_ARGUMENT_KIND_LIST}; 1316 #undef X 1317 1318 assert(e->argument_count > index); 1319 MetaEntryArgument result = e->arguments[index]; 1320 1321 if (result.kind != kind) { 1322 meta_entry_error_location(e, result.location, "unexpected argument kind: expected %s but got: %s\n", 1323 kinds[kind], kinds[result.kind]); 1324 } 1325 1326 if (kind == MetaEntryArgumentKind_Array && result.count == 0) 1327 meta_entry_error_location(e, result.location, "array arguments must have at least 1 element\n"); 1328 1329 return result; 1330 } 1331 1332 typedef struct { 1333 s8_list *data; 1334 iz count; 1335 iz capacity; 1336 } s8_list_table; 1337 1338 typedef struct { 1339 s8 *names_upper; 1340 s8 *names_lower; 1341 u8 *floating_point; 1342 u32 entry_count; 1343 u32 shader_id; 1344 } MetaShaderBakeParameters; 1345 DA_STRUCT(MetaShaderBakeParameters, MetaShaderBakeParameters); 1346 1347 typedef struct { 1348 iz kind; 1349 iz variation; 1350 } MetaEnumeration; 1351 1352 typedef struct { 1353 u32 *data; 1354 iz count; 1355 iz capacity; 1356 } MetaIDList; 1357 1358 typedef struct { 1359 MetaIDList global_flag_ids; 1360 MetaIDList global_enumeration_ids; 1361 MetaShaderBakeParameters *bake_parameters; 1362 u32 name_id; 1363 u32 flag_list_id; 1364 i32 base_shader_id; 1365 } MetaShader; 1366 DA_STRUCT(MetaShader, MetaShader); 1367 1368 typedef struct { 1369 MetaShader *shader; 1370 MetaIDList sub_shaders; 1371 s8 file; 1372 } MetaBaseShader; 1373 DA_STRUCT(MetaBaseShader, MetaBaseShader); 1374 1375 typedef struct { 1376 s8 name; 1377 MetaIDList shaders; 1378 } MetaShaderGroup; 1379 DA_STRUCT(MetaShaderGroup, MetaShaderGroup); 1380 1381 typedef struct { 1382 s8 *fields; 1383 s8 **entries; 1384 u32 field_count; 1385 u32 entry_count; 1386 u32 table_name_id; 1387 } MetaTable; 1388 DA_STRUCT(MetaTable, MetaTable); 1389 1390 typedef enum { 1391 MetaExpansionPartKind_Reference, 1392 MetaExpansionPartKind_String, 1393 } MetaExpansionPartKind; 1394 1395 typedef struct { 1396 s8 *strings; 1397 MetaExpansionPartKind kind; 1398 } MetaExpansionPart; 1399 DA_STRUCT(MetaExpansionPart, MetaExpansionPart); 1400 1401 typedef enum { 1402 MetaEmitOperationKind_Expand, 1403 MetaEmitOperationKind_FileBytes, 1404 MetaEmitOperationKind_String, 1405 } MetaEmitOperationKind; 1406 1407 typedef struct { 1408 MetaExpansionPart *parts; 1409 u32 part_count; 1410 u32 table_id; 1411 } MetaEmitOperationExpansion; 1412 1413 typedef struct { 1414 union { 1415 s8 string; 1416 MetaEmitOperationExpansion expansion_operation; 1417 }; 1418 MetaEmitOperationKind kind; 1419 } MetaEmitOperation; 1420 DA_STRUCT(MetaEmitOperation, MetaEmitOperation); 1421 1422 typedef struct { 1423 MetaEmitOperationList *data; 1424 iz count; 1425 iz capacity; 1426 } MetaEmitOperationListSet; 1427 1428 typedef struct { 1429 Arena *arena, scratch; 1430 1431 s8 filename; 1432 s8 directory; 1433 1434 s8_list enumeration_kinds; 1435 s8_list_table enumeration_members; 1436 1437 s8_list_table flags_for_shader; 1438 1439 s8_list table_names; 1440 MetaTableList tables; 1441 1442 MetaEmitOperationListSet emit_sets; 1443 1444 MetaShaderBakeParametersList shader_bake_parameters; 1445 MetaShaderGroupList shader_groups; 1446 MetaShaderList shaders; 1447 MetaBaseShaderList base_shaders; 1448 s8_list shader_names; 1449 } MetaContext; 1450 1451 function iz 1452 meta_lookup_string_slow(s8_list *sv, s8 s) 1453 { 1454 // TODO(rnp): obviously this is slow 1455 iz result = -1; 1456 for (iz i = 0; i < sv->count; i++) { 1457 if (s8_equal(s, sv->data[i])) { 1458 result = i; 1459 break; 1460 } 1461 } 1462 return result; 1463 } 1464 1465 function iz 1466 meta_lookup_id_slow(MetaIDList *v, u32 id) 1467 { 1468 // TODO(rnp): obviously this is slow 1469 iz result = -1; 1470 for (iz i = 0; i < v->count; i++) { 1471 if (id == v->data[i]) { 1472 result = i; 1473 break; 1474 } 1475 } 1476 return result; 1477 } 1478 1479 function iz 1480 meta_intern_string(MetaContext *ctx, s8_list *sv, s8 s) 1481 { 1482 iz result = meta_lookup_string_slow(sv, s); 1483 if (result < 0) { 1484 *da_push(ctx->arena, sv) = s; 1485 result = sv->count - 1; 1486 } 1487 return result; 1488 } 1489 1490 function iz 1491 meta_intern_id(MetaContext *ctx, MetaIDList *v, u32 id) 1492 { 1493 iz result = meta_lookup_id_slow(v, id); 1494 if (result < 0) { 1495 *da_push(ctx->arena, v) = id; 1496 result = v->count - 1; 1497 } 1498 return result; 1499 } 1500 1501 function iz 1502 meta_pack_shader_bake_parameters(MetaContext *ctx, MetaEntry *e, iz entry_count, u32 shader_id, u32 *table_id) 1503 { 1504 assert(e->kind == MetaEntryKind_Bake); 1505 1506 MetaShaderBakeParameters *bp = da_push(ctx->arena, &ctx->shader_bake_parameters); 1507 bp->shader_id = shader_id; 1508 if (table_id) *table_id = (u32)da_index(bp, &ctx->shader_bake_parameters); 1509 1510 if (e->argument_count) meta_entry_argument_expected(e); 1511 1512 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 1513 if (scope.consumed > 1) { 1514 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 1515 if (row->kind != MetaEntryKind_BakeInt && row->kind != MetaEntryKind_BakeFloat) 1516 meta_entry_nesting_error(row, MetaEntryKind_Bake); 1517 meta_entry_argument_expected(row, s8("name"), s8("name_lower")); 1518 bp->entry_count++; 1519 } 1520 1521 bp->names_upper = push_array(ctx->arena, s8, bp->entry_count); 1522 bp->names_lower = push_array(ctx->arena, s8, bp->entry_count); 1523 bp->floating_point = push_array(ctx->arena, u8, bp->entry_count); 1524 1525 u32 row_index = 0; 1526 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++, row_index++) { 1527 bp->names_upper[row_index] = row->arguments[0].string; 1528 bp->names_lower[row_index] = row->arguments[1].string; 1529 bp->floating_point[row_index] = row->kind == MetaEntryKind_BakeFloat; 1530 } 1531 } 1532 1533 return scope.consumed; 1534 } 1535 1536 function iz 1537 meta_enumeration_id(MetaContext *ctx, s8 kind) 1538 { 1539 iz result = meta_intern_string(ctx, &ctx->enumeration_kinds, kind); 1540 if (ctx->enumeration_kinds.count != ctx->enumeration_members.count) { 1541 da_push(ctx->arena, &ctx->enumeration_members); 1542 assert(result == (ctx->enumeration_members.count - 1)); 1543 } 1544 return result; 1545 } 1546 1547 function void 1548 meta_extend_enumeration(MetaContext *ctx, s8 kind, s8 *variations, uz count) 1549 { 1550 iz kidx = meta_enumeration_id(ctx, kind); 1551 /* NOTE(rnp): may overcommit if duplicates exist in variations */ 1552 da_reserve(ctx->arena, ctx->enumeration_members.data + kidx, (iz)count); 1553 for (uz i = 0; i < count; i++) 1554 meta_intern_string(ctx, ctx->enumeration_members.data + kidx, variations[i]); 1555 } 1556 1557 function MetaEnumeration 1558 meta_commit_enumeration(MetaContext *ctx, s8 kind, s8 variation) 1559 { 1560 iz kidx = meta_enumeration_id(ctx, kind); 1561 iz vidx = meta_intern_string(ctx, ctx->enumeration_members.data + kidx, variation); 1562 MetaEnumeration result = {.kind = kidx, .variation = vidx}; 1563 return result; 1564 } 1565 1566 function u16 1567 meta_pack_shader_name(MetaContext *ctx, s8 base_name, MetaLocation loc) 1568 { 1569 iz result = meta_intern_string(ctx, &ctx->shader_names, base_name); 1570 if (result > (iz)U16_MAX) 1571 meta_compiler_error(loc, "maximum base shaders exceeded: limit: %lu\n", U16_MAX); 1572 return (u16)result; 1573 } 1574 1575 function u8 1576 meta_commit_shader_flag(MetaContext *ctx, u32 flag_list_id, s8 flag, MetaEntry *e) 1577 { 1578 assert(flag_list_id < ctx->flags_for_shader.count); 1579 iz index = meta_intern_string(ctx, ctx->flags_for_shader.data + flag_list_id, flag); 1580 if (index > 31) meta_entry_error(e, "maximum shader local flags exceeded: limit: 32\n"); 1581 u8 result = (u8)index; 1582 return result; 1583 } 1584 1585 function iz 1586 meta_pack_shader(MetaContext *ctx, MetaShaderGroup *sg, Arena scratch, MetaEntry *entries, iz entry_count) 1587 { 1588 assert(entries[0].kind == MetaEntryKind_Shader); 1589 1590 MetaShader *s = da_push(ctx->arena, &ctx->shaders); 1591 *da_push(ctx->arena, &sg->shaders) = (u32)da_index(s, &ctx->shaders); 1592 { 1593 s8_list *flag_list = da_push(ctx->arena, &ctx->flags_for_shader); 1594 s->flag_list_id = (u32)da_index(flag_list, &ctx->flags_for_shader); 1595 } 1596 s->name_id = meta_pack_shader_name(ctx, entries->name, entries->location); 1597 s->base_shader_id = -1; 1598 1599 MetaBaseShader *base_shader = 0; 1600 if (entries->argument_count > 1) { 1601 meta_entry_argument_expected(entries, s8("[file_name]")); 1602 } else if (entries->argument_count == 1) { 1603 base_shader = da_push(ctx->arena, &ctx->base_shaders); 1604 base_shader->file = meta_entry_argument_expect(entries, 0, MetaEntryArgumentKind_String).string; 1605 base_shader->shader = s; 1606 s->base_shader_id = (i32)da_index(base_shader, &ctx->base_shaders); 1607 } 1608 1609 i32 stack_items[32]; 1610 struct { i32 *data; iz capacity; iz count; } stack = {stack_items, countof(stack_items), 0}; 1611 1612 iz result; 1613 b32 in_sub_shader = 0; 1614 for (result = 0; result < entry_count; result++) { 1615 MetaEntry *e = entries + result; 1616 switch (e->kind) { 1617 case MetaEntryKind_BeginScope:{}break; 1618 case MetaEntryKind_SubShader:{ 1619 if (in_sub_shader) goto error; 1620 in_sub_shader = 1; 1621 } /* FALLTHROUGH */ 1622 case MetaEntryKind_Shader: 1623 { 1624 *da_push(&scratch, &stack) = (i32)result; 1625 if ((result + 1 < entry_count) && entries[result + 1].kind == MetaEntryKind_BeginScope) 1626 break; 1627 } /* FALLTHROUGH */ 1628 case MetaEntryKind_EndScope:{ 1629 i32 index = stack.data[--stack.count]; 1630 MetaEntry *ended = entries + index; 1631 if (index != 0) { 1632 if (stack.count > 0 && entries[stack.data[stack.count - 1]].kind == MetaEntryKind_Shader) { 1633 if (ended->kind == MetaEntryKind_SubShader) { 1634 if (!base_shader) { 1635 meta_entry_error(ended, "invalid nesting: @%s in @%s\n" 1636 "@%s only allowed in base shaders (shaders with a backing file)\n", 1637 meta_entry_kind_strings[ended->kind], 1638 meta_entry_kind_strings[MetaEntryKind_Shader], 1639 meta_entry_kind_strings[ended->kind]); 1640 } 1641 MetaShader *ss = da_push(ctx->arena, &ctx->shaders); 1642 u32 sid = (u32)da_index(ss, &ctx->shaders); 1643 *da_push(ctx->arena, &sg->shaders) = sid; 1644 *da_push(ctx->arena, &base_shader->sub_shaders) = sid; 1645 1646 ss->flag_list_id = s->flag_list_id; 1647 ss->base_shader_id = s->base_shader_id; 1648 ss->name_id = meta_pack_shader_name(ctx, ended->name, ended->location); 1649 meta_commit_shader_flag(ctx, s->flag_list_id, ended->name, ended); 1650 in_sub_shader = 0; 1651 } 1652 } 1653 } 1654 }break; 1655 case MetaEntryKind_Enumeration:{ 1656 meta_entry_argument_expected(e, s8("kind")); 1657 s8 kind = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 1658 iz kid = meta_enumeration_id(ctx, kind); 1659 meta_intern_id(ctx, &s->global_enumeration_ids, (u32)kid); 1660 }break; 1661 case MetaEntryKind_Flags:{ 1662 meta_entry_argument_expected(e, s8("[flag ...]")); 1663 MetaEntryArgument flags = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_Array); 1664 for (u32 index = 0; index < flags.count; index++) 1665 meta_commit_shader_flag(ctx, s->flag_list_id, flags.strings[index], e); 1666 }break; 1667 case MetaEntryKind_Bake:{ 1668 if (s->bake_parameters) { 1669 meta_entry_error(e, "invalid @%s in @%s: only one @%s allowed per @%s\n", 1670 meta_entry_kind_strings[e->kind], meta_entry_kind_strings[MetaEntryKind_Shader], 1671 meta_entry_kind_strings[e->kind], meta_entry_kind_strings[MetaEntryKind_Shader]); 1672 } 1673 u32 table_id; 1674 result += meta_pack_shader_bake_parameters(ctx, e, entry_count - result, (u32)da_index(s, &ctx->shaders), &table_id); 1675 s->bake_parameters = ctx->shader_bake_parameters.data + table_id; 1676 }break; 1677 1678 default: 1679 error: 1680 { 1681 meta_entry_nesting_error(e, MetaEntryKind_Shader); 1682 }break; 1683 } 1684 if (stack.count == 0) 1685 break; 1686 } 1687 1688 return result; 1689 } 1690 1691 function void 1692 meta_expansion_string_split(s8 string, s8 *left, s8 *inner, s8 *remainder, MetaLocation loc) 1693 { 1694 b32 found = 0; 1695 for (u8 *s = string.data, *e = s + string.len; (s + 1) != e; s++) { 1696 u32 val = (u32)'$' << 8u | (u32)'('; 1697 u32 test = (u32)s[0] << 8u | s[1]; 1698 if (test == val) { 1699 if (left) { 1700 left->data = string.data; 1701 left->len = s - string.data; 1702 } 1703 1704 u8 *start = s + 2; 1705 while (s != e && *s != ')') s++; 1706 if (s == e) { 1707 meta_compiler_error_message(loc, "unterminated expansion in raw string:\n %.*s\n", 1708 (i32)string.len, string.data); 1709 fprintf(stderr, " %.*s^\n", (i32)(start - string.data), ""); 1710 meta_error(); 1711 } 1712 1713 if (inner) { 1714 inner->data = start; 1715 inner->len = s - start; 1716 } 1717 1718 if (remainder) { 1719 remainder->data = s + 1; 1720 remainder->len = string.len - (remainder->data - string.data); 1721 } 1722 found = 1; 1723 break; 1724 } 1725 } 1726 if (!found) { 1727 if (left) *left = string; 1728 if (inner) *inner = (s8){0}; 1729 if (remainder) *remainder = (s8){0}; 1730 } 1731 } 1732 1733 function MetaExpansionPart * 1734 meta_push_expansion_part(MetaContext *ctx, Arena *arena, MetaExpansionPartList *parts, 1735 MetaExpansionPartKind kind, s8 string, MetaTable *t, MetaLocation loc) 1736 { 1737 MetaExpansionPart *result = da_push(arena, parts); 1738 result->kind = kind; 1739 if (kind == MetaExpansionPartKind_String) { 1740 result->strings = push_struct(arena, s8); 1741 result->strings[0] = string; 1742 } else { 1743 iz index = -1; 1744 for (u32 field = 0; field < t->field_count; field++) { 1745 if (s8_equal(string, t->fields[field])) { 1746 index = (iz)field; 1747 break; 1748 } 1749 } 1750 result->strings = t->entries[index]; 1751 if (index < 0) { 1752 /* TODO(rnp): fix this location to point directly at the field in the string */ 1753 s8 table_name = ctx->table_names.data[t->table_name_id]; 1754 meta_compiler_error(loc, "table \"%.*s\" does not contain member: %.*s\n", 1755 (i32)table_name.len, table_name.data, (i32)string.len, string.data); 1756 } 1757 } 1758 return result; 1759 } 1760 1761 function MetaExpansionPartList 1762 meta_generate_expansion_set(MetaContext *ctx, Arena *arena, s8 expansion_string, MetaTable *t, MetaLocation loc) 1763 { 1764 MetaExpansionPartList result = {0}; 1765 s8 left = {0}, inner, remainder = expansion_string; 1766 do { 1767 meta_expansion_string_split(remainder, &left, &inner, &remainder, loc); 1768 if (left.len) meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_String, left, t, loc); 1769 if (inner.len) meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Reference, inner, t, loc); 1770 } while (remainder.len); 1771 return result; 1772 } 1773 1774 function iz 1775 meta_expand(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count, MetaEmitOperationList *ops) 1776 { 1777 assert(e->kind == MetaEntryKind_Expand); 1778 1779 /* TODO(rnp): for now this requires that the @Table came first */ 1780 meta_entry_argument_expected(e, s8("table_name")); 1781 s8 table_name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 1782 1783 MetaTable *t = ctx->tables.data + meta_lookup_string_slow(&ctx->table_names, table_name); 1784 if (t < ctx->tables.data) 1785 meta_entry_error(e, "undefined table %.*s\n", (i32)table_name.len, table_name.data); 1786 1787 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 1788 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 1789 switch (row->kind) { 1790 case MetaEntryKind_String:{ 1791 if (!ops) goto error; 1792 1793 MetaExpansionPartList parts = meta_generate_expansion_set(ctx, ctx->arena, row->name, t, row->location); 1794 1795 MetaEmitOperation *op = da_push(ctx->arena, ops); 1796 op->kind = MetaEmitOperationKind_Expand; 1797 op->expansion_operation.parts = parts.data; 1798 op->expansion_operation.part_count = (u32)parts.count; 1799 op->expansion_operation.table_id = (u32)da_index(t, &ctx->tables); 1800 }break; 1801 case MetaEntryKind_Enumeration:{ 1802 if (ops) meta_entry_nesting_error(row, MetaEntryKind_Emit); 1803 1804 meta_entry_argument_expected(row, s8("kind"), s8("`raw_string`")); 1805 s8 kind = meta_entry_argument_expect(row, 0, MetaEntryArgumentKind_String).string; 1806 s8 expand = meta_entry_argument_expect(row, 1, MetaEntryArgumentKind_String).string; 1807 1808 MetaExpansionPartList parts = meta_generate_expansion_set(ctx, &scratch, expand, t, row->location); 1809 s8 *variations = push_array(&scratch, s8, t->entry_count); 1810 for (u32 expansion = 0; expansion < t->entry_count; expansion++) { 1811 Stream sb = arena_stream(*ctx->arena); 1812 for (iz part = 0; part < parts.count; part++) { 1813 MetaExpansionPart *p = parts.data + part; 1814 u32 index = 0; 1815 if (p->kind == MetaExpansionPartKind_Reference) index = expansion; 1816 stream_append_s8(&sb, p->strings[index]); 1817 } 1818 variations[expansion] = arena_stream_commit(ctx->arena, &sb); 1819 } 1820 meta_extend_enumeration(ctx, kind, variations, t->entry_count); 1821 }break; 1822 error: 1823 default: 1824 { 1825 meta_entry_nesting_error(row, MetaEntryKind_Expand); 1826 }break; 1827 } 1828 } 1829 return scope.consumed; 1830 } 1831 1832 function void 1833 meta_embed(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count) 1834 { 1835 assert(e->kind == MetaEntryKind_Embed); 1836 1837 meta_entry_argument_expected(e, s8("filename")); 1838 s8 filename = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 1839 1840 MetaEmitOperationList *ops = da_push(ctx->arena, &ctx->emit_sets); 1841 if (e->name.len == 0) meta_entry_error(e, "name must be provided for output array"); 1842 1843 MetaEmitOperation *op; 1844 op = da_push(ctx->arena, ops); 1845 op->kind = MetaEmitOperationKind_String; 1846 op->string = push_s8_from_parts(ctx->arena, s8(""), s8("read_only global u8 "), e->name, s8("[] = {")); 1847 1848 op = da_push(ctx->arena, ops); 1849 op->kind = MetaEmitOperationKind_FileBytes; 1850 op->string = filename; 1851 1852 op = da_push(ctx->arena, ops); 1853 op->kind = MetaEmitOperationKind_String; 1854 op->string = s8("};"); 1855 } 1856 1857 function iz 1858 meta_pack_emit(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count) 1859 { 1860 assert(e->kind == MetaEntryKind_Emit); 1861 1862 MetaEmitOperationList *ops = da_push(ctx->arena, &ctx->emit_sets); 1863 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 1864 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 1865 switch (row->kind) { 1866 case MetaEntryKind_String:{ 1867 MetaEmitOperation *op = da_push(ctx->arena, ops); 1868 op->kind = MetaEmitOperationKind_String; 1869 op->string = row->name; 1870 }break; 1871 case MetaEntryKind_Expand:{ 1872 row += meta_expand(ctx, scratch, row, entry_count - (row - e), ops); 1873 }break; 1874 default:{ meta_entry_nesting_error(row, MetaEntryKind_Emit); }break; 1875 } 1876 } 1877 return scope.consumed; 1878 } 1879 1880 function iz 1881 meta_pack_table(MetaContext *ctx, MetaEntry *e, iz entry_count) 1882 { 1883 assert(e->kind == MetaEntryKind_Table); 1884 1885 MetaTable *t = da_push(ctx->arena, &ctx->tables); 1886 iz table_name_id = meta_lookup_string_slow(&ctx->table_names, e->name); 1887 if (table_name_id >= 0) meta_entry_error(e, "table redifined\n"); 1888 1889 s8 *t_name = da_push(ctx->arena, &ctx->table_names); 1890 t->table_name_id = (u32)da_index(t_name, &ctx->table_names); 1891 *t_name = e->name; 1892 1893 meta_entry_argument_expected(e, s8("[field ...]")); 1894 MetaEntryArgument fields = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_Array); 1895 t->fields = fields.strings; 1896 t->field_count = (u32)fields.count; 1897 1898 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 1899 if (scope.consumed > 1) { 1900 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 1901 if (row->kind != MetaEntryKind_Array) 1902 meta_entry_nesting_error(row, MetaEntryKind_Table); 1903 1904 MetaEntryArgument entries = meta_entry_argument_expect(row, 0, MetaEntryArgumentKind_Array); 1905 if (entries.count != t->field_count) { 1906 meta_compiler_error_message(row->location, "incorrect field count for @%s entry got: %zu expected: %u\n", 1907 meta_entry_kind_strings[MetaEntryKind_Table], 1908 entries.count, t->field_count); 1909 fprintf(stderr, " fields: ["); 1910 for (uz i = 0; i < t->field_count; i++) { 1911 if (i != 0) fprintf(stderr, ", "); 1912 fprintf(stderr, "%.*s", (i32)t->fields[i].len, t->fields[i].data); 1913 } 1914 fprintf(stderr, "]\n"); 1915 meta_error(); 1916 } 1917 t->entry_count++; 1918 } 1919 1920 t->entries = push_array(ctx->arena, s8 *, t->field_count); 1921 for (u32 field = 0; field < t->field_count; field++) 1922 t->entries[field] = push_array(ctx->arena, s8, t->entry_count); 1923 1924 u32 row_index = 0; 1925 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++, row_index++) { 1926 s8 *fs = row->arguments->strings; 1927 for (u32 field = 0; field < t->field_count; field++) 1928 t->entries[field][row_index] = fs[field]; 1929 } 1930 } 1931 1932 return scope.consumed; 1933 } 1934 1935 function CommandList 1936 meta_extract_emit_file_dependencies(MetaContext *ctx, Arena *arena) 1937 { 1938 CommandList result = {0}; 1939 for (iz set = 0; set < ctx->emit_sets.count; set++) { 1940 MetaEmitOperationList *ops = ctx->emit_sets.data + set; 1941 for (iz opcode = 0; opcode < ops->count; opcode++) { 1942 MetaEmitOperation *op = ops->data + opcode; 1943 switch (op->kind) { 1944 case MetaEmitOperationKind_FileBytes:{ 1945 s8 filename = push_s8_from_parts(arena, s8(OS_PATH_SEPARATOR), ctx->directory, op->string); 1946 *da_push(arena, &result) = (c8 *)filename.data; 1947 }break; 1948 default:{}break; 1949 } 1950 } 1951 } 1952 return result; 1953 } 1954 1955 function void 1956 metagen_push_byte_array(MetaprogramContext *m, s8 bytes) 1957 { 1958 for (iz i = 0; i < bytes.len; i++) { 1959 b32 end_line = (i != 0) && (i % 16) == 0; 1960 if (i != 0) meta_push(m, end_line ? s8(",") : s8(", ")); 1961 if (end_line) meta_end_line(m); 1962 if ((i % 16) == 0) meta_indent(m); 1963 meta_push(m, s8("0x")); 1964 meta_push_u64_hex(m, bytes.data[i]); 1965 } 1966 meta_end_line(m); 1967 } 1968 1969 function void 1970 metagen_run_emit(MetaprogramContext *m, MetaContext *ctx) 1971 { 1972 for (iz set = 0; set < ctx->emit_sets.count; set++) { 1973 MetaEmitOperationList *ops = ctx->emit_sets.data + set; 1974 for (iz opcode = 0; opcode < ops->count; opcode++) { 1975 MetaEmitOperation *op = ops->data + opcode; 1976 switch (op->kind) { 1977 case MetaEmitOperationKind_String:{ meta_push_line(m, op->string); }break; 1978 case MetaEmitOperationKind_FileBytes:{ 1979 Arena scratch = m->scratch; 1980 s8 filename = push_s8_from_parts(&scratch, s8(OS_PATH_SEPARATOR), ctx->directory, op->string); 1981 s8 file = os_read_whole_file(&scratch, (c8 *)filename.data); 1982 m->indentation_level++; 1983 metagen_push_byte_array(m, file); 1984 m->indentation_level--; 1985 }break; 1986 case MetaEmitOperationKind_Expand:{ 1987 MetaEmitOperationExpansion *eop = &op->expansion_operation; 1988 MetaTable *t = ctx->tables.data + eop->table_id; 1989 for (u32 entry = 0; entry < t->entry_count; entry++) { 1990 meta_indent(m); 1991 for (u32 part = 0; part < eop->part_count; part++) { 1992 MetaExpansionPart *p = eop->parts + part; 1993 u32 index = p->kind == MetaExpansionPartKind_Reference ? entry : 0; 1994 meta_push(m, p->strings[index]); 1995 } 1996 meta_end_line(m); 1997 } 1998 }break; 1999 InvalidDefaultCase; 2000 } 2001 } 2002 meta_end_line(m); 2003 } 2004 } 2005 2006 function void 2007 metagen_push_table(MetaprogramContext *m, Arena scratch, s8 row_start, s8 row_end, 2008 s8 **column_strings, uz rows, uz columns) 2009 { 2010 u32 *column_widths = 0; 2011 if (columns > 1) { 2012 column_widths = push_array(&scratch, u32, (iz)columns - 1); 2013 for (uz column = 0; column < columns - 1; column++) { 2014 s8 *strings = column_strings[column]; 2015 for (uz row = 0; row < rows; row++) 2016 column_widths[column] = MAX(column_widths[column], (u32)strings[row].len); 2017 } 2018 } 2019 2020 for (uz row = 0; row < rows; row++) { 2021 meta_begin_line(m, row_start); 2022 for (uz column = 0; column < columns; column++) { 2023 s8 text = column_strings[column][row]; 2024 meta_push(m, text); 2025 i32 pad = columns > 1 ? 1 : 0; 2026 if (column_widths && column < columns - 1) 2027 pad += (i32)column_widths[column] - (i32)text.len; 2028 if (column < columns - 1) meta_pad(m, ' ', pad); 2029 } 2030 meta_end_line(m, row_end); 2031 } 2032 } 2033 2034 function void 2035 metagen_push_counted_enum_body(MetaprogramContext *m, s8 kind, s8 prefix, s8 mid, s8 suffix, s8 *ids, iz ids_count) 2036 { 2037 iz max_id_length = 0; 2038 for (iz id = 0; id < ids_count; id++) 2039 max_id_length = MAX(max_id_length, ids[id].len); 2040 2041 for (iz id = 0; id < ids_count; id++) { 2042 meta_begin_line(m, prefix, kind, ids[id]); 2043 meta_pad(m, ' ', 1 + (i32)(max_id_length - ids[id].len)); 2044 meta_push(m, mid); 2045 meta_push_u64(m, (u64)id); 2046 meta_end_line(m, suffix); 2047 } 2048 } 2049 2050 function void 2051 metagen_push_c_enum(MetaprogramContext *m, Arena scratch, s8 kind, s8 *ids, iz ids_count) 2052 { 2053 s8 kind_full = push_s8_from_parts(&scratch, s8(""), kind, s8("_")); 2054 meta_begin_scope(m, s8("typedef enum {")); 2055 metagen_push_counted_enum_body(m, kind_full, s8(""), s8("= "), s8(","), ids, ids_count); 2056 meta_push_line(m, kind_full, s8("Count,")); 2057 meta_end_scope(m, s8("} "), kind, s8(";\n")); 2058 } 2059 2060 function void 2061 metagen_push_c_flag_enum(MetaprogramContext *m, Arena scratch, s8 kind, s8 *ids, iz ids_count) 2062 { 2063 s8 kind_full = push_s8_from_parts(&scratch, s8(""), kind, s8("_")); 2064 meta_begin_scope(m, s8("typedef enum {")); 2065 metagen_push_counted_enum_body(m, kind_full, s8(""), s8("= (1 << "), s8("),"), ids, ids_count); 2066 meta_end_scope(m, s8("} "), kind, s8(";\n")); 2067 } 2068 2069 function void 2070 meta_push_shader_reload_info(MetaprogramContext *m, MetaContext *ctx) 2071 { 2072 /////////////////////////////// 2073 // NOTE(rnp): reloadable infos 2074 meta_begin_scope(m, s8("read_only global BeamformerShaderKind beamformer_reloadable_shader_kinds[] = {")); 2075 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2076 MetaShader *s = ctx->base_shaders.data[shader].shader; 2077 meta_push_line(m, s8("BeamformerShaderKind_"), ctx->shader_names.data[s->name_id], s8(",")); 2078 } 2079 meta_end_scope(m, s8("};\n")); 2080 2081 meta_begin_scope(m, s8("read_only global s8 beamformer_reloadable_shader_files[] = {")); 2082 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) 2083 meta_push_line(m, s8("s8_comp(\""), ctx->base_shaders.data[shader].file, s8("\"),")); 2084 meta_end_scope(m, s8("};\n")); 2085 2086 { 2087 meta_begin_scope(m, s8("read_only global i32 beamformer_shader_reloadable_index_by_shader[] = {")); 2088 for (iz shader = 0; shader < ctx->shaders.count; shader++) { 2089 meta_indent(m); 2090 meta_push_i64(m, ctx->shaders.data[shader].base_shader_id); 2091 meta_end_line(m, s8(",")); 2092 } 2093 meta_end_scope(m, s8("};\n")); 2094 } 2095 2096 { 2097 u32 info_index = 0; 2098 for (iz group = 0; group < ctx->shader_groups.count; group++) { 2099 MetaShaderGroup *sg = ctx->shader_groups.data + group; 2100 meta_begin_line(m, s8("read_only global i32 beamformer_reloadable_")); 2101 for (iz i = 0; i < sg->name.len; i++) 2102 stream_append_byte(&m->stream, TOLOWER(sg->name.data[i])); 2103 meta_begin_scope(m, s8("_shader_info_indices[] = {")); 2104 2105 for (iz shader = 0; shader < sg->shaders.count; shader++) { 2106 MetaShader *s = ctx->shaders.data + sg->shaders.data[shader]; 2107 /* TODO(rnp): store base shader list in a better format */ 2108 for (iz base_shader = 0; base_shader < ctx->base_shaders.count; base_shader++) { 2109 MetaBaseShader *bs = ctx->base_shaders.data + base_shader; 2110 if (bs->shader == s) { 2111 meta_indent(m); 2112 meta_push_u64(m, info_index++); 2113 meta_end_line(m, s8(",")); 2114 break; 2115 } 2116 } 2117 } 2118 meta_end_scope(m, s8("};\n")); 2119 } 2120 } 2121 2122 //////////////////////////////////// 2123 // NOTE(rnp): shader header strings 2124 meta_begin_scope(m, s8("read_only global s8 beamformer_shader_global_header_strings[] = {")); 2125 for (iz kind = 0; kind < ctx->enumeration_kinds.count; kind++) { 2126 s8_list *sub_list = ctx->enumeration_members.data + kind; 2127 s8 kind_name = push_s8_from_parts(&m->scratch, s8(""), ctx->enumeration_kinds.data[kind], s8("_")); 2128 meta_push_line(m, s8("s8_comp(\"\"")); 2129 metagen_push_counted_enum_body(m, kind_name, s8("\"#define "), s8(""), s8("\\n\""), 2130 sub_list->data, sub_list->count); 2131 meta_push_line(m, s8("\"\\n\"),")); 2132 m->scratch = ctx->scratch; 2133 } 2134 meta_end_scope(m, s8("};\n")); 2135 2136 meta_begin_scope(m, s8("read_only global s8 *beamformer_shader_flag_strings[] = {")); 2137 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2138 MetaShader *s = ctx->base_shaders.data[shader].shader; 2139 s8_list *flag_list = ctx->flags_for_shader.data + s->flag_list_id; 2140 2141 if (flag_list->count) { 2142 meta_begin_scope(m, s8("(s8 []){")); 2143 for (iz flag = 0; flag < flag_list->count; flag++) 2144 meta_push_line(m, s8("s8_comp(\""), flag_list->data[flag], s8("\"),")); 2145 meta_end_scope(m, s8("},")); 2146 } else { 2147 meta_push_line(m, s8("0,")); 2148 } 2149 } 2150 meta_end_scope(m, s8("};\n")); 2151 2152 meta_begin_scope(m, s8("read_only global u8 beamformer_shader_flag_strings_count[] = {")); 2153 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2154 MetaShader *s = ctx->base_shaders.data[shader].shader; 2155 s8_list *flag_list = ctx->flags_for_shader.data + s->flag_list_id; 2156 2157 meta_indent(m); 2158 meta_push_u64(m, (u64)flag_list->count); 2159 meta_end_line(m, s8(",")); 2160 } 2161 meta_end_scope(m, s8("};\n")); 2162 } 2163 2164 function void 2165 meta_push_shader_bake(MetaprogramContext *m, MetaContext *ctx) 2166 { 2167 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2168 MetaShader *s = ctx->base_shaders.data[shader].shader; 2169 s8 shader_name = ctx->shader_names.data[s->name_id]; 2170 meta_begin_line(m, s8("read_only global u8 beamformer_shader_")); 2171 for (iz i = 0; i < shader_name.len; i++) 2172 stream_append_byte(&m->stream, TOLOWER(shader_name.data[i])); 2173 meta_begin_scope(m, s8("_bytes[] = {")); { 2174 Arena scratch = m->scratch; 2175 s8 filename = push_s8_from_parts(&scratch, s8(OS_PATH_SEPARATOR), s8("shaders"), 2176 ctx->base_shaders.data[shader].file); 2177 s8 file = os_read_whole_file(&scratch, (c8 *)filename.data); 2178 metagen_push_byte_array(m, file); 2179 } meta_end_scope(m, s8("};\n")); 2180 } 2181 2182 meta_begin_scope(m, s8("read_only global s8 beamformer_shader_data[] = {")); { 2183 Arena scratch = m->scratch; 2184 s8 *columns[2]; 2185 columns[0] = push_array(&m->scratch, s8, ctx->base_shaders.count); 2186 columns[1] = push_array(&m->scratch, s8, ctx->base_shaders.count); 2187 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2188 MetaShader *s = ctx->base_shaders.data[shader].shader; 2189 s8 shader_name = ctx->shader_names.data[s->name_id]; 2190 2191 Stream sb = arena_stream(m->scratch); 2192 for (iz i = 0; i < shader_name.len; i++) 2193 stream_append_byte(&sb, TOLOWER(shader_name.data[i])); 2194 stream_append_s8(&sb, s8("_bytes,")); 2195 columns[0][shader] = arena_stream_commit_and_reset(&m->scratch, &sb); 2196 2197 stream_append_s8(&sb, s8(".len = countof(beamformer_shader_")); 2198 for (iz i = 0; i < shader_name.len; i++) 2199 stream_append_byte(&sb, TOLOWER(shader_name.data[i])); 2200 columns[1][shader] = arena_stream_commit(&m->scratch, &sb); 2201 } 2202 metagen_push_table(m, m->scratch, s8("{.data = beamformer_shader_"), s8("_bytes)},"), columns, 2203 (uz)ctx->base_shaders.count, 2); 2204 m->scratch = scratch; 2205 } meta_end_scope(m, s8("};\n")); 2206 } 2207 2208 read_only global s8 c_file_header = s8("" 2209 "/* See LICENSE for license details. */\n\n" 2210 "// GENERATED CODE\n\n" 2211 ); 2212 2213 function b32 2214 metagen_emit_c_code(MetaContext *ctx, Arena arena) 2215 { 2216 b32 result = 1; 2217 2218 os_make_directory("generated"); 2219 char *out_meta = "generated" OS_PATH_SEPARATOR "beamformer.meta.c"; 2220 char *out_shaders = "generated" OS_PATH_SEPARATOR "beamformer_shaders.c"; 2221 2222 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 2223 2224 //////////////////////////// 2225 // NOTE(rnp): shader baking 2226 { 2227 char **deps = push_array(&m->scratch, char *, ctx->base_shaders.count); 2228 for (iz i = 0; i < ctx->base_shaders.count; i++) { 2229 MetaBaseShader *b = ctx->base_shaders.data + i; 2230 deps[i] = (c8 *)push_s8_from_parts(&m->scratch, s8(OS_PATH_SEPARATOR), s8("shaders"), b->file).data; 2231 } 2232 if (needs_rebuild_(out_shaders, deps, ctx->base_shaders.count)) { 2233 build_log_generate("Bake Shaders"); 2234 meta_push(m, c_file_header); 2235 meta_push_shader_bake(m, ctx); 2236 result &= meta_write_and_reset(m, out_shaders); 2237 } 2238 m->scratch = ctx->scratch; 2239 } 2240 2241 if (!needs_rebuild(out_meta, "beamformer.meta")) 2242 return result; 2243 2244 build_log_generate("Core C Code"); 2245 2246 meta_push(m, c_file_header); 2247 2248 ///////////////////////// 2249 // NOTE(rnp): enumarents 2250 for (iz kind = 0; kind < ctx->enumeration_kinds.count; kind++) { 2251 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8("Beamformer"), ctx->enumeration_kinds.data[kind]); 2252 metagen_push_c_enum(m, m->scratch, enum_name, ctx->enumeration_members.data[kind].data, 2253 ctx->enumeration_members.data[kind].count); 2254 m->scratch = ctx->scratch; 2255 } 2256 2257 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2258 MetaShader *s = ctx->base_shaders.data[shader].shader; 2259 s8_list flag_list = ctx->flags_for_shader.data[s->flag_list_id]; 2260 if (flag_list.count) { 2261 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8("BeamformerShader"), 2262 ctx->shader_names.data[s->name_id], s8("Flags")); 2263 metagen_push_c_flag_enum(m, m->scratch, enum_name, flag_list.data, flag_list.count); 2264 m->scratch = ctx->scratch; 2265 } 2266 } 2267 2268 { 2269 s8 kind = s8("BeamformerShaderKind"); 2270 s8 kind_full = s8("BeamformerShaderKind_"); 2271 meta_begin_scope(m, s8("typedef enum {")); 2272 metagen_push_counted_enum_body(m, kind_full, s8(""), s8("= "), s8(","), 2273 ctx->shader_names.data, ctx->shader_names.count); 2274 meta_push_line(m, kind_full, s8("Count,\n")); 2275 2276 s8 *columns[2]; 2277 columns[0] = push_array(&m->scratch, s8, ctx->shader_groups.count * 3); 2278 columns[1] = push_array(&m->scratch, s8, ctx->shader_groups.count * 3); 2279 2280 for (iz group = 0; group < ctx->shader_groups.count; group++) { 2281 MetaShaderGroup *sg = ctx->shader_groups.data + group; 2282 2283 s8 first_name = ctx->shader_names.data[ctx->shaders.data[sg->shaders.data[0]].name_id]; 2284 s8 last_name = ctx->shader_names.data[ctx->shaders.data[sg->shaders.data[sg->shaders.count - 1]].name_id]; 2285 2286 columns[0][3 * group + 0] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), sg->name, s8("First")); 2287 columns[1][3 * group + 0] = push_s8_from_parts(&m->scratch, s8(""), s8("= "), kind, s8("_"), first_name); 2288 2289 columns[0][3 * group + 1] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), sg->name, s8("Last")); 2290 columns[1][3 * group + 1] = push_s8_from_parts(&m->scratch, s8(""),s8("= "), kind, s8("_"), last_name); 2291 2292 columns[0][3 * group + 2] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), sg->name, s8("Count")); 2293 Stream sb = arena_stream(m->scratch); 2294 stream_append_s8(&sb, s8("= ")); 2295 stream_append_u64(&sb, (u64)sg->shaders.count); 2296 columns[1][3 * group + 2] = arena_stream_commit(&m->scratch, &sb); 2297 } 2298 metagen_push_table(m, m->scratch, s8(""), s8(","), columns, (uz)ctx->shader_groups.count * 3, 2); 2299 2300 meta_end_scope(m, s8("} "), kind, s8(";\n")); 2301 m->scratch = ctx->scratch; 2302 } 2303 2304 ////////////////////// 2305 // NOTE(rnp): structs 2306 for (u32 bake = 0; bake < ctx->shader_bake_parameters.count; bake++) { 2307 Arena tmp = m->scratch; 2308 MetaShaderBakeParameters *b = ctx->shader_bake_parameters.data + bake; 2309 MetaShader *s = ctx->shaders.data + b->shader_id; 2310 s8 name = push_s8_from_parts(&m->scratch, s8(""), s8("BeamformerShader"), 2311 ctx->shader_names.data[s->name_id], s8("BakeParameters")); 2312 meta_begin_scope(m, s8("typedef struct {")); 2313 for (u32 entry = 0; entry < b->entry_count; entry++) { 2314 s8 kind = b->floating_point[entry] ? s8("f32 ") : s8("u32 "); 2315 meta_push_line(m, kind, b->names_lower[entry], s8(";")); 2316 } 2317 meta_end_scope(m, s8("} "), name, s8(";\n")); 2318 m->scratch = tmp; 2319 } 2320 2321 // shader bake parameter struct 2322 meta_begin_scope(m, s8("typedef struct {")); 2323 { 2324 meta_begin_scope(m, s8("union {")); 2325 { 2326 Arena tmp = m->scratch; 2327 s8 *columns[2]; 2328 columns[0] = push_array(&m->scratch, s8, ctx->shader_bake_parameters.count); 2329 columns[1] = push_array(&m->scratch, s8, ctx->shader_bake_parameters.count); 2330 for (u32 bake = 0; bake < ctx->shader_bake_parameters.count; bake++) { 2331 MetaShaderBakeParameters *b = ctx->shader_bake_parameters.data + bake; 2332 MetaShader *s = ctx->shaders.data + b->shader_id; 2333 columns[0][bake] = push_s8_from_parts(&m->scratch, s8(""), s8("BeamformerShader"), 2334 ctx->shader_names.data[s->name_id], s8("BakeParameters")); 2335 columns[1][bake] = ctx->shader_names.data[s->name_id]; 2336 } 2337 metagen_push_table(m, m->scratch, s8(""), s8(";"), columns, 2338 (uz)ctx->shader_bake_parameters.count, 2); 2339 m->scratch = tmp; 2340 } meta_end_scope(m, s8("};")); 2341 s8 names[] = {s8("data_kind"), s8("flags")}; 2342 s8 types[] = {s8("u32"), s8("u32")}; 2343 metagen_push_table(m, m->scratch, s8(""), s8(";"), (s8 *[]){types, names}, countof(names), 2); 2344 } meta_end_scope(m, s8("} BeamformerShaderBakeParameters;\n")); 2345 2346 ///////////////////////////////// 2347 // NOTE(rnp): shader info tables 2348 meta_begin_scope(m, s8("read_only global s8 beamformer_shader_names[] = {")); 2349 metagen_push_table(m, m->scratch, s8("s8_comp(\""), s8("\"),"), &ctx->shader_names.data, 2350 (uz)ctx->shader_names.count, 1); 2351 meta_end_scope(m, s8("};\n")); 2352 2353 meta_push_shader_reload_info(m, ctx); 2354 2355 meta_begin_scope(m, s8("read_only global i32 *beamformer_shader_header_vectors[] = {")); 2356 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2357 MetaShader *s = ctx->base_shaders.data[shader].shader; 2358 if (s->global_flag_ids.count || s->global_enumeration_ids.count) { 2359 meta_begin_line(m, s8("(i32 []){")); 2360 for (iz id = 0; id < s->global_flag_ids.count; id++) { 2361 if (id != 0) meta_push(m, s8(", ")); 2362 meta_push_u64(m, s->global_flag_ids.data[id]); 2363 } 2364 for (iz id = 0; id < s->global_enumeration_ids.count; id++) { 2365 if (id != 0 || s->global_flag_ids.count) meta_push(m, s8(", ")); 2366 meta_push_u64(m, s->global_enumeration_ids.data[id]); 2367 } 2368 meta_end_line(m, s8("},")); 2369 } else { 2370 meta_push_line(m, s8("0,")); 2371 } 2372 } 2373 meta_end_scope(m, s8("};\n")); 2374 2375 meta_begin_scope(m, s8("read_only global i32 beamformer_shader_header_vector_lengths[] = {")); 2376 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2377 MetaShader *s = ctx->base_shaders.data[shader].shader; 2378 meta_indent(m); 2379 meta_push_u64(m, (u64)s->global_enumeration_ids.count); 2380 meta_end_line(m, s8(",")); 2381 } 2382 meta_end_scope(m, s8("};\n")); 2383 2384 meta_begin_scope(m, s8("read_only global s8 *beamformer_shader_bake_parameter_names[] = {")); 2385 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2386 MetaShader *s = ctx->base_shaders.data[shader].shader; 2387 if (s->bake_parameters) { 2388 meta_begin_scope(m, s8("(s8 []){")); 2389 for (u32 index = 0; index < s->bake_parameters->entry_count; index++) 2390 meta_push_line(m, s8("s8_comp(\""), s->bake_parameters->names_upper[index], s8("\"),")); 2391 meta_end_scope(m, s8("},")); 2392 } else { 2393 meta_push_line(m, s8("0,")); 2394 } 2395 } 2396 meta_end_scope(m, s8("};\n")); 2397 2398 meta_begin_scope(m, s8("read_only global u8 *beamformer_shader_bake_parameter_is_float[] = {")); 2399 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2400 MetaShader *s = ctx->base_shaders.data[shader].shader; 2401 if (s->bake_parameters) { 2402 meta_begin_line(m, s8("(u8 []){")); 2403 for (u32 index = 0; index < s->bake_parameters->entry_count; index++) { 2404 if (index != 0) meta_push(m, s8(", ")); 2405 meta_push(m, s->bake_parameters->floating_point[index] ? s8("1") : s8("0")); 2406 } 2407 meta_end_line(m, s8("},")); 2408 } else { 2409 meta_push_line(m, s8("0,")); 2410 } 2411 } 2412 meta_end_scope(m, s8("};\n")); 2413 2414 meta_begin_scope(m, s8("read_only global i32 beamformer_shader_bake_parameter_counts[] = {")); 2415 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2416 MetaShader *s = ctx->base_shaders.data[shader].shader; 2417 if (s->bake_parameters) { 2418 meta_indent(m); 2419 meta_push_u64(m, s->bake_parameters->entry_count); 2420 meta_end_line(m, s8(",")); 2421 } else { 2422 meta_push_line(m, s8("0,")); 2423 } 2424 } 2425 meta_end_scope(m, s8("};\n")); 2426 2427 metagen_run_emit(m, ctx); 2428 2429 //fprintf(stderr, "%.*s\n", (i32)m.stream.widx, m.stream.data); 2430 2431 result = meta_write_and_reset(m, out_meta); 2432 2433 return result; 2434 } 2435 2436 function b32 2437 metagen_emit_matlab_code(MetaContext *ctx, Arena arena) 2438 { 2439 b32 result = 1; 2440 if (!needs_rebuild(OUTPUT("matlab/OGLBeamformerFilterKind.m"), "beamformer_parameters.h")) 2441 return result; 2442 2443 build_log_generate("MATLAB Bindings"); 2444 /* TODO(rnp): recreate/clear directory incase these file names change */ 2445 os_make_directory(OUTPUT("matlab")); 2446 2447 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 2448 2449 #define X(name, flag, ...) meta_push_line(m, s8(#name " (" str(flag) ")")); 2450 meta_begin_matlab_class(m, "OGLBeamformerLiveFeedbackFlags", "int32"); 2451 meta_begin_scope(m, s8("enumeration")); 2452 BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST 2453 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerLiveFeedbackFlags.m")); 2454 #undef X 2455 2456 #define X(kind, ...) meta_push_matlab_enum_with_value(m, s8(#kind), BeamformerFilterKind_## kind); 2457 meta_begin_matlab_class(m, "OGLBeamformerFilterKind", "int32"); 2458 meta_begin_scope(m, s8("enumeration")); 2459 BEAMFORMER_FILTER_KIND_LIST(,) 2460 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerFilterKind.m")); 2461 #undef X 2462 2463 os_make_directory(OUTPUT("matlab/+OGLBeamformerFilter")); 2464 #define X(kind, ...) {OUTPUT("matlab/+OGLBeamformerFilter/" #kind ".m"), s8_comp(#kind), s8_comp(#__VA_ARGS__)}, 2465 read_only local_persist struct {char *out; s8 class, args;} filter_table[] = { 2466 BEAMFORMER_FILTER_KIND_LIST(,) 2467 }; 2468 #undef X 2469 2470 s8_list members = {0}; 2471 for EachNonZeroEnumValue(BeamformerFilterKind, filter) { 2472 typeof(*filter_table) *f = filter_table + filter; 2473 members.count = 0; 2474 s8_list_from_s8(&members, &m->scratch, f->args); 2475 meta_begin_scope(m, s8("classdef "), f->class, s8(" < OGLBeamformerFilter.BaseFilter")); 2476 2477 meta_begin_scope(m, s8("properties")); 2478 for (iz it = 0; it < members.count; it++) 2479 meta_push_matlab_property(m, members.data[it], 1, s8("single")); 2480 meta_end_scope(m, s8("end")); 2481 2482 meta_begin_scope(m, s8("methods")); 2483 meta_begin_line(m, s8("function obj = "), f->class, s8("(")); 2484 for (iz it = 0; it < members.count; it++) 2485 meta_push(m, it > 0 ? s8(", ") : s8(""), members.data[it]); 2486 meta_end_line(m, s8(")")); 2487 2488 m->indentation_level++; 2489 for (iz it = 0; it < members.count; it++) 2490 meta_push_line(m, s8("obj."), members.data[it], s8(" = "), members.data[it], s8(";")); 2491 result &= meta_end_and_write_matlab(m, f->out); 2492 } 2493 m->scratch = ctx->scratch; 2494 2495 meta_begin_matlab_class(m, "BaseFilter"); 2496 meta_begin_scope(m, s8("methods")); 2497 meta_begin_scope(m, s8("function out = Flatten(obj)")); 2498 meta_push_line(m, s8("fields = struct2cell(struct(obj));")); 2499 meta_push_line(m, s8("out = zeros(1, numel(fields));")); 2500 meta_begin_scope(m, s8("for i = 1:numel(fields)")); 2501 meta_push_line(m, s8("out(i) = fields{i};")); 2502 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/+OGLBeamformerFilter/BaseFilter.m")); 2503 2504 #define X(name, __t, __s, kind, elements, ...) meta_push_matlab_property(m, s8(#name), (u64)elements, s8(#kind)); 2505 meta_begin_matlab_class(m, "OGLBeamformerParameters"); 2506 meta_begin_scope(m, s8("properties")); 2507 BEAMFORMER_PARAMS_HEAD 2508 BEAMFORMER_UI_PARAMS 2509 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerParameters.m")); 2510 2511 meta_begin_matlab_class(m, "OGLBeamformerParametersHead"); 2512 meta_begin_scope(m, s8("properties")); 2513 BEAMFORMER_PARAMS_HEAD 2514 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerParametersHead.m")); 2515 2516 meta_begin_matlab_class(m, "OGLBeamformerParametersUI"); 2517 meta_begin_scope(m, s8("properties")); 2518 BEAMFORMER_UI_PARAMS 2519 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerParametersUI.m")); 2520 2521 meta_begin_matlab_class(m, "OGLBeamformerSimpleParameters"); 2522 meta_begin_scope(m, s8("properties")); 2523 BEAMFORMER_PARAMS_HEAD 2524 BEAMFORMER_UI_PARAMS 2525 BEAMFORMER_SIMPLE_PARAMS 2526 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerSimpleParameters.m")); 2527 #undef X 2528 2529 #define X(name, __t, __s, elements, ...) meta_push_matlab_property(m, s8(#name), elements, s8("")); 2530 meta_begin_matlab_class(m, "OGLBeamformerLiveImagingParameters"); 2531 meta_begin_scope(m, s8("properties")); 2532 BEAMFORMER_LIVE_IMAGING_PARAMETERS_LIST 2533 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerLiveImagingParameters.m")); 2534 #undef X 2535 2536 meta_begin_matlab_class(m, "OGLBeamformerShaderStage", "int32"); 2537 meta_begin_scope(m, s8("enumeration")); 2538 { 2539 iz index = -1; 2540 for (iz group = 0; group < ctx->shader_groups.count; group++) { 2541 if (s8_equal(ctx->shader_groups.data[group].name, s8("Compute"))) { 2542 index = group; 2543 break; 2544 } 2545 } 2546 if (index != -1) { 2547 MetaShaderGroup *sg = ctx->shader_groups.data + index; 2548 /* TODO(rnp): this assumes that the shaders are sequential */ 2549 s8 *names = ctx->shader_names.data + ctx->shaders.data[0].name_id; 2550 metagen_push_counted_enum_body(m, s8(""), s8(""), s8("("), s8(")"), names, sg->shaders.count); 2551 } else { 2552 build_log_failure("failed to find Compute shader group in meta info\n"); 2553 } 2554 result &= index != -1; 2555 } 2556 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerShaderStage.m")); 2557 2558 for (iz kind = 0; kind < ctx->enumeration_kinds.count; kind++) { 2559 Arena scratch = ctx->scratch; 2560 s8 name = ctx->enumeration_kinds.data[kind]; 2561 s8 output = push_s8_from_parts(&scratch, s8(""), s8(OUTPUT("matlab/OGLBeamformer")), name, s8(".m")); 2562 s8_list *kinds = ctx->enumeration_members.data + kind; 2563 meta_begin_scope(m, s8("classdef OGLBeamformer"), name, s8(" < int32")); 2564 meta_begin_scope(m, s8("enumeration")); 2565 s8 prefix = s8(""); 2566 if (kinds->count > 0 && ISDIGIT(kinds->data[0].data[0])) prefix = s8("m"); 2567 metagen_push_counted_enum_body(m, s8(""), prefix, s8("("), s8(")"), kinds->data, kinds->count); 2568 result &= meta_end_and_write_matlab(m, (c8 *)output.data); 2569 } 2570 2571 return result; 2572 } 2573 2574 function b32 2575 metagen_emit_helper_library_header(MetaContext *ctx, Arena arena) 2576 { 2577 b32 result = 1; 2578 char *out = OUTPUT("ogl_beamformer_lib.h"); 2579 if (!needs_rebuild(out, "helpers/ogl_beamformer_lib_base.h", "beamformer.meta")) 2580 return result; 2581 2582 build_log_generate("Helper Library Header"); 2583 2584 s8 parameters_header = os_read_whole_file(&arena, "beamformer_parameters.h"); 2585 s8 base_header = os_read_whole_file(&arena, "helpers/ogl_beamformer_lib_base.h"); 2586 2587 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 2588 2589 meta_push_line(m, s8("/* See LICENSE for license details. */\n")); 2590 meta_push_line(m, s8("// GENERATED CODE\n")); 2591 2592 { 2593 iz index = meta_lookup_string_slow(&ctx->enumeration_kinds, s8("DataKind")); 2594 if (index != -1) { 2595 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8("Beamformer"), ctx->enumeration_kinds.data[index]); 2596 metagen_push_c_enum(m, m->scratch, enum_name, ctx->enumeration_members.data[index].data, 2597 ctx->enumeration_members.data[index].count); 2598 m->scratch = ctx->scratch; 2599 } else { 2600 build_log_failure("failed to find DataKind in meta info\n"); 2601 } 2602 } 2603 2604 { 2605 iz index = -1; 2606 for (iz group = 0; group < ctx->shader_groups.count; group++) { 2607 if (s8_equal(ctx->shader_groups.data[group].name, s8("Compute"))) { 2608 index = group; 2609 break; 2610 } 2611 } 2612 if (index != -1) { 2613 MetaShaderGroup *sg = ctx->shader_groups.data + index; 2614 meta_begin_line(m, s8("#define BeamformerShaderKind_ComputeCount (")); 2615 meta_push_u64(m, (u64)sg->shaders.count); 2616 meta_end_line(m, s8(")\n")); 2617 } else { 2618 build_log_failure("failed to find Compute shader group in meta info\n"); 2619 } 2620 } 2621 2622 meta_push(m, parameters_header, base_header); 2623 result &= meta_write_and_reset(m, out); 2624 2625 return result; 2626 } 2627 2628 function MetaContext * 2629 metagen_load_context(Arena *arena, char *filename) 2630 { 2631 if (setjmp(compiler_jmp_buf)) { 2632 /* NOTE(rnp): compiler error */ 2633 return 0; 2634 } 2635 2636 MetaContext *ctx = push_struct(arena, MetaContext); 2637 ctx->scratch = sub_arena(arena, MB(1), 16); 2638 ctx->arena = arena; 2639 2640 MetaContext *result = ctx; 2641 2642 ctx->filename = c_str_to_s8(filename); 2643 ctx->directory = s8_chop(&ctx->filename, s8_scan_backwards(ctx->filename, OS_PATH_SEPARATOR_CHAR)); 2644 s8_chop(&ctx->filename, 1); 2645 if (ctx->directory.len <= 0) ctx->directory = s8("."); 2646 2647 Arena scratch = ctx->scratch; 2648 MetaEntryStack entries = meta_entry_stack_from_file(ctx->arena, scratch, filename); 2649 2650 i32 stack_items[32]; 2651 struct { i32 *data; iz capacity; iz count; } stack = {stack_items, countof(stack_items), 0}; 2652 2653 MetaShaderGroup *current_shader_group = 0; 2654 for (iz i = 0; i < entries.count; i++) { 2655 MetaEntry *e = entries.data + i; 2656 //if (e->kind == MetaEntryKind_EndScope) depth--; 2657 //meta_entry_print(e, depth, -1); 2658 //if (e->kind == MetaEntryKind_BeginScope) depth++; 2659 //continue; 2660 2661 switch (e->kind) { 2662 case MetaEntryKind_BeginScope:{ *da_push(&scratch, &stack) = (i32)(i - 1); }break; 2663 case MetaEntryKind_EndScope:{ 2664 i32 index = stack.data[--stack.count]; 2665 MetaEntry *ended = entries.data + index; 2666 switch (ended->kind) { 2667 case MetaEntryKind_ShaderGroup:{ current_shader_group = 0; }break; 2668 default:{}break; 2669 } 2670 }break; 2671 case MetaEntryKind_Emit:{ 2672 i += meta_pack_emit(ctx, scratch, e, entries.count - i); 2673 }break; 2674 case MetaEntryKind_Enumeration:{ 2675 meta_entry_argument_expected(e, s8("kind"), s8("[id ...]")); 2676 s8 kind = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2677 MetaEntryArgument ids = meta_entry_argument_expect(e, 1, MetaEntryArgumentKind_Array); 2678 for (u32 id = 0; id < ids.count; id++) 2679 meta_commit_enumeration(ctx, kind, ids.strings[id]); 2680 }break; 2681 case MetaEntryKind_Embed:{ 2682 meta_embed(ctx, scratch, e, entries.count - i); 2683 }break; 2684 case MetaEntryKind_Expand:{ 2685 i += meta_expand(ctx, scratch, e, entries.count - i, 0); 2686 }break; 2687 case MetaEntryKind_ShaderGroup:{ 2688 MetaShaderGroup *sg = da_push(ctx->arena, &ctx->shader_groups); 2689 sg->name = e->name; 2690 current_shader_group = sg; 2691 }break; 2692 case MetaEntryKind_Shader:{ 2693 if (!current_shader_group) goto error; 2694 i += meta_pack_shader(ctx, current_shader_group, scratch, e, entries.count - i); 2695 }break; 2696 case MetaEntryKind_Table:{ 2697 i += meta_pack_table(ctx, e, entries.count - i); 2698 }break; 2699 2700 error: 2701 default: 2702 { 2703 meta_entry_error(e, "invalid @%s() in global scope\n", meta_entry_kind_strings[e->kind]); 2704 }break; 2705 } 2706 } 2707 2708 result->arena = 0; 2709 return result; 2710 } 2711 2712 function b32 2713 metagen_file_direct(Arena arena, char *filename) 2714 { 2715 MetaContext *ctx = metagen_load_context(&arena, filename); 2716 if (!ctx) return 0; 2717 if (ctx->shaders.count || ctx->base_shaders.count || ctx->shader_groups.count) { 2718 build_log_error("shaders not supported in file: %s\n", filename); 2719 return 0; 2720 } 2721 2722 b32 result = 1; 2723 char *out; 2724 { 2725 s8 basename; 2726 s8_split(ctx->filename, &basename, 0, '.'); 2727 2728 Stream sb = arena_stream(arena); 2729 stream_append_s8s(&sb, ctx->directory, s8(OS_PATH_SEPARATOR), s8("generated")); 2730 stream_append_byte(&sb, 0); 2731 os_make_directory((c8 *)sb.data); 2732 stream_reset(&sb, sb.widx - 1); 2733 2734 stream_append_s8s(&sb, s8(OS_PATH_SEPARATOR), basename, s8(".c")); 2735 stream_append_byte(&sb, 0); 2736 2737 out = (c8 *)arena_stream_commit(&arena, &sb).data; 2738 } 2739 2740 CommandList deps = meta_extract_emit_file_dependencies(ctx, &arena); 2741 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 2742 if (needs_rebuild_(out, deps.data, deps.count)) { 2743 build_log_generate(out); 2744 meta_push(m, c_file_header); 2745 metagen_run_emit(m, ctx); 2746 result &= meta_write_and_reset(m, out); 2747 } 2748 2749 return result; 2750 } 2751 2752 i32 2753 main(i32 argc, char *argv[]) 2754 { 2755 u64 start_time = os_get_timer_counter(); 2756 g_argv0 = argv[0]; 2757 2758 b32 result = 1; 2759 Arena arena = os_alloc_arena(MB(8)); 2760 check_rebuild_self(arena, argc, argv); 2761 2762 os_make_directory(OUTDIR); 2763 2764 result &= metagen_file_direct(arena, "assets" OS_PATH_SEPARATOR "assets.meta"); 2765 2766 MetaContext *meta = metagen_load_context(&arena, "beamformer.meta"); 2767 if (!meta) return 1; 2768 2769 result &= metagen_emit_c_code(meta, arena); 2770 result &= metagen_emit_helper_library_header(meta, arena); 2771 result &= metagen_emit_matlab_code(meta, arena); 2772 2773 Options options = parse_options(argc, argv); 2774 2775 CommandList c = cmd_base(&arena, &options); 2776 if (!check_build_raylib(arena, c, options.debug)) return 1; 2777 2778 ///////////////////////////////////// 2779 // extra flags (unusable for raylib) 2780 cmd_append(&arena, &c, EXTRA_FLAGS); 2781 2782 ///////////////// 2783 // helpers/tests 2784 result &= build_helper_library(arena, c); 2785 if (options.tests) result &= build_tests(arena, c); 2786 2787 ////////////////// 2788 // static portion 2789 cmd_append(&arena, &c, options.bake_shaders? "-DBakeShaders=1" : "-DBakeShaders=0"); 2790 iz c_count = c.count; 2791 cmd_append(&arena, &c, OS_MAIN, OUTPUT_EXE("ogl")); 2792 cmd_pdb(&arena, &c, "ogl"); 2793 if (options.debug) { 2794 if (!is_w32) cmd_append(&arena, &c, "-Wl,--export-dynamic", "-Wl,-rpath,."); 2795 if (!is_msvc) cmd_append(&arena, &c, "-L."); 2796 cmd_append(&arena, &c, LINK_LIB("raylib")); 2797 } else { 2798 cmd_append(&arena, &c, OUTPUT(OS_STATIC_LIB("raylib"))); 2799 } 2800 if (!is_msvc) cmd_append(&arena, &c, "-lm"); 2801 if (is_unix) cmd_append(&arena, &c, "-lGL"); 2802 if (is_w32) { 2803 cmd_append(&arena, &c, LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), 2804 LINK_LIB("opengl32"), LINK_LIB("winmm"), LINK_LIB("Synchronization")); 2805 if (!is_msvc) cmd_append(&arena, &c, "-Wl,--out-implib," OUTPUT(OS_STATIC_LIB("main"))); 2806 } 2807 cmd_append(&arena, &c, (void *)0); 2808 2809 result &= run_synchronous(arena, &c); 2810 c.count = c_count; 2811 2812 ///////////////////////// 2813 // hot reloadable portion 2814 // 2815 // NOTE: this is built after main because on w32 we need to export 2816 // gl function pointers for the reloadable portion to import 2817 if (options.debug) { 2818 if (is_msvc) { 2819 build_static_library_from_objects(arena, OUTPUT_LIB(OS_STATIC_LIB("main")), 2820 arg_list(char *, "/def", "/name:ogl.exe"), 2821 arg_list(char *, OUTPUT(OBJECT("main_w32")))); 2822 } 2823 result &= build_beamformer_as_library(arena, c); 2824 } 2825 2826 if (options.time) { 2827 f64 seconds = (f64)(os_get_timer_counter() - start_time) / (f64)os_get_timer_frequency(); 2828 build_log_info("took %0.03f [s]", seconds); 2829 } 2830 2831 return result != 1; 2832 }