build.c (116594B)
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 12 #include "util.h" 13 14 #include <stdarg.h> 15 #include <setjmp.h> 16 #include <stdio.h> 17 18 #define BeamformerShaderKind_ComputeCount (1) 19 #include "beamformer_parameters.h" 20 21 global char *g_argv0; 22 23 #define OUTDIR "out" 24 #define OUTPUT(s) OUTDIR OS_PATH_SEPARATOR s 25 26 #if COMPILER_MSVC 27 #define COMMON_FLAGS "-nologo", "-std:c11", "-Fo:" OUTDIR "\\", "-Z7", "-Zo" 28 #define DEBUG_FLAGS "-Od", "-D_DEBUG" 29 #define OPTIMIZED_FLAGS "-O2" 30 #define EXTRA_FLAGS "" 31 #else 32 #define COMMON_FLAGS "-std=c11", "-pipe", "-Wall" 33 #define DEBUG_FLAGS "-O0", "-D_DEBUG", "-Wno-unused-function" 34 #define OPTIMIZED_FLAGS "-O3" 35 #define EXTRA_FLAGS_BASE "-Werror", "-Wextra", "-Wshadow", "-Wno-unused-parameter", \ 36 "-Wno-error=unused-function", "-fno-builtin" 37 #if COMPILER_GCC 38 #define EXTRA_FLAGS EXTRA_FLAGS_BASE, "-Wno-unused-variable" 39 #else 40 #define EXTRA_FLAGS EXTRA_FLAGS_BASE 41 #endif 42 #endif 43 44 #define is_aarch64 ARCH_ARM64 45 #define is_amd64 ARCH_X64 46 #define is_unix OS_LINUX 47 #define is_w32 OS_WINDOWS 48 #define is_clang COMPILER_CLANG 49 #define is_gcc COMPILER_GCC 50 #define is_msvc COMPILER_MSVC 51 52 #define BEAMFORMER_IMPORT function 53 54 #if OS_LINUX 55 56 #include <dirent.h> 57 #include <errno.h> 58 #include <string.h> 59 #include <sys/select.h> 60 #include <sys/wait.h> 61 62 #include "os_linux.c" 63 64 #define W32_DECL(x) 65 66 #define OS_SHARED_LINK_LIB(s) "lib" s ".so" 67 #define OS_SHARED_LIB(s) s ".so" 68 #define OS_STATIC_LIB(s) s ".a" 69 #define OS_MAIN "main_linux.c" 70 71 #elif OS_WINDOWS 72 73 #include <string.h> 74 75 #include "os_win32.c" 76 77 #define W32_DECL(x) x 78 79 #define OS_SHARED_LINK_LIB(s) s ".dll" 80 #define OS_SHARED_LIB(s) s ".dll" 81 #define OS_STATIC_LIB(s) s ".lib" 82 #define OS_MAIN "main_w32.c" 83 84 #else 85 #error Unsupported Platform 86 #endif 87 88 #if COMPILER_CLANG 89 #define COMPILER "clang" 90 #define PREPROCESSOR "clang", "-E", "-P" 91 #elif COMPILER_MSVC 92 #define COMPILER "cl" 93 #define PREPROCESSOR "cl", "/EP" 94 #else 95 #define COMPILER "cc" 96 #define PREPROCESSOR "cc", "-E", "-P" 97 #endif 98 99 #if COMPILER_MSVC 100 #define LINK_LIB(name) name ".lib" 101 #define OBJECT(name) name ".obj" 102 #define OUTPUT_DLL(name) "/LD", "/Fe:", name 103 #define OUTPUT_LIB(name) "/out:" OUTPUT(name) 104 #define OUTPUT_EXE(name) "/Fe:", name 105 #define COMPILER_OUTPUT "/Fo:" 106 #define STATIC_LIBRARY_BEGIN(name) "lib", "/nologo", name 107 #else 108 #define LINK_LIB(name) "-l" name 109 #define OBJECT(name) name ".o" 110 #define OUTPUT_DLL(name) "-fPIC", "-shared", "-o", name 111 #define OUTPUT_LIB(name) OUTPUT(name) 112 #define OUTPUT_EXE(name) "-o", name 113 #define COMPILER_OUTPUT "-o" 114 #define STATIC_LIBRARY_BEGIN(name) "ar", "rc", name 115 #endif 116 117 #define shift(list, count) ((count)--, *(list)++) 118 119 #define cmd_append_count da_append_count 120 #define cmd_append(a, s, ...) da_append_count(a, s, ((char *[]){__VA_ARGS__}), \ 121 (iz)(sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) 122 123 DA_STRUCT(char *, Command); 124 125 typedef struct { 126 b32 bake_shaders; 127 b32 debug; 128 b32 generic; 129 b32 sanitize; 130 b32 tests; 131 b32 time; 132 } Config; 133 global Config config; 134 135 read_only global s8 c_file_header = s8_comp("" 136 "/* See LICENSE for license details. */\n\n" 137 "// GENERATED CODE\n\n" 138 ); 139 140 #define BUILD_LOG_KINDS \ 141 X(Error, "\x1B[31m[ERROR]\x1B[0m ") \ 142 X(Warning, "\x1B[33m[WARNING]\x1B[0m ") \ 143 X(Generate, "\x1B[32m[GENERATE]\x1B[0m ") \ 144 X(Info, "\x1B[33m[INFO]\x1B[0m ") \ 145 X(Command, "\x1B[36m[COMMAND]\x1B[0m ") 146 #define X(t, ...) BuildLogKind_##t, 147 typedef enum {BUILD_LOG_KINDS BuildLogKind_Count} BuildLogKind; 148 #undef X 149 150 function void 151 build_log_base(BuildLogKind kind, char *format, va_list args) 152 { 153 #define X(t, pre) pre, 154 read_only local_persist char *prefixes[BuildLogKind_Count + 1] = {BUILD_LOG_KINDS "[INVALID] "}; 155 #undef X 156 FILE *out = kind == BuildLogKind_Error? stderr : stdout; 157 fputs(prefixes[MIN(kind, BuildLogKind_Count)], out); 158 vfprintf(out, format, args); 159 fputc('\n', out); 160 } 161 162 #define build_log_failure(format, ...) build_log(BuildLogKind_Error, \ 163 "failed to build: " format, ##__VA_ARGS__) 164 #define build_log_error(...) build_log(BuildLogKind_Error, ##__VA_ARGS__) 165 #define build_log_generate(...) build_log(BuildLogKind_Generate, ##__VA_ARGS__) 166 #define build_log_info(...) build_log(BuildLogKind_Info, ##__VA_ARGS__) 167 #define build_log_command(...) build_log(BuildLogKind_Command, ##__VA_ARGS__) 168 #define build_log_warning(...) build_log(BuildLogKind_Warning, ##__VA_ARGS__) 169 function void 170 build_log(BuildLogKind kind, char *format, ...) 171 { 172 va_list ap; 173 va_start(ap, format); 174 build_log_base(kind, format, ap); 175 va_end(ap); 176 } 177 178 #define build_fatal(fmt, ...) build_fatal_("%s: " fmt, __FUNCTION__, ##__VA_ARGS__) 179 function no_return void 180 build_fatal_(char *format, ...) 181 { 182 va_list ap; 183 va_start(ap, format); 184 build_log_base(BuildLogKind_Error, format, ap); 185 va_end(ap); 186 os_exit(1); 187 } 188 189 function s8 190 read_entire_file(const char *file, Arena *arena) 191 { 192 s8 result = {0}; 193 result.len = os_read_entire_file(file, arena->beg, arena_capacity(arena, u8)); 194 if (result.len) result.data = arena_commit(arena, result.len); 195 return result; 196 } 197 198 function b32 199 s8_equal(s8 a, s8 b) 200 { 201 b32 result = a.len == b.len; 202 for (iz i = 0; result && i < a.len; i++) 203 result = a.data[i] == b.data[i]; 204 return result; 205 } 206 207 function b32 208 s8_contains(s8 s, u8 byte) 209 { 210 b32 result = 0; 211 for (iz i = 0 ; !result && i < s.len; i++) 212 result |= s.data[i] == byte; 213 return result; 214 } 215 216 function void 217 stream_push_command(Stream *s, CommandList *c) 218 { 219 if (!s->errors) { 220 for (iz i = 0; i < c->count; i++) { 221 s8 item = c_str_to_s8(c->data[i]); 222 if (item.len) { 223 b32 escape = s8_contains(item, ' ') || s8_contains(item, '"'); 224 if (escape) stream_append_byte(s, '\''); 225 stream_append_s8(s, item); 226 if (escape) stream_append_byte(s, '\''); 227 if (i != c->count - 1) stream_append_byte(s, ' '); 228 } 229 } 230 } 231 } 232 233 function char * 234 temp_sprintf(char *format, ...) 235 { 236 local_persist char buffer[4096]; 237 va_list ap; 238 va_start(ap, format); 239 vsnprintf(buffer, countof(buffer), format, ap); 240 va_end(ap); 241 return buffer; 242 } 243 244 #if OS_LINUX 245 246 function b32 247 os_rename_file(char *name, char *new) 248 { 249 b32 result = rename(name, new) != -1; 250 return result; 251 } 252 253 function b32 254 os_remove_file(char *name) 255 { 256 b32 result = remove(name) != -1; 257 return result; 258 } 259 260 function void 261 os_make_directory(char *name) 262 { 263 mkdir(name, 0770); 264 } 265 266 #define os_remove_directory(f) os_remove_directory_(AT_FDCWD, (f)) 267 function b32 268 os_remove_directory_(i32 base_fd, char *name) 269 { 270 /* POSix sucks */ 271 #ifndef DT_DIR 272 enum {DT_DIR = 4, DT_REG = 8, DT_LNK = 10}; 273 #endif 274 275 i32 dir_fd = openat(base_fd, name, O_DIRECTORY); 276 b32 result = dir_fd != -1 || errno == ENOTDIR || errno == ENOENT; 277 DIR *dir; 278 if (dir_fd != -1 && (dir = fdopendir(dir_fd))) { 279 struct dirent *dp; 280 while ((dp = readdir(dir))) { 281 switch (dp->d_type) { 282 case DT_LNK: 283 case DT_REG: 284 { 285 unlinkat(dir_fd, dp->d_name, 0); 286 }break; 287 case DT_DIR:{ 288 s8 dir_name = c_str_to_s8(dp->d_name); 289 if (!s8_equal(s8("."), dir_name) && !s8_equal(s8(".."), dir_name)) 290 os_remove_directory_(dir_fd, dp->d_name); 291 }break; 292 default:{ 293 build_log_warning("\"%s\": unknown directory entry kind: %d", dp->d_name, dp->d_type); 294 }break; 295 } 296 } 297 298 closedir(dir); 299 result = unlinkat(base_fd, name, AT_REMOVEDIR) == 0; 300 } 301 return result; 302 } 303 304 function u64 305 os_get_filetime(char *file) 306 { 307 struct stat sb; 308 u64 result = (u64)-1; 309 if (stat(file, &sb) != -1) 310 result = (u64)sb.st_mtim.tv_sec; 311 return result; 312 } 313 314 function iptr 315 os_spawn_process(CommandList *cmd, Stream sb) 316 { 317 pid_t result = fork(); 318 switch (result) { 319 case -1: build_fatal("failed to fork command: %s: %s", cmd->data[0], strerror(errno)); break; 320 case 0: { 321 if (execvp(cmd->data[0], cmd->data) == -1) 322 build_fatal("failed to exec command: %s: %s", cmd->data[0], strerror(errno)); 323 unreachable(); 324 } break; 325 } 326 return (iptr)result; 327 } 328 329 function b32 330 os_wait_close_process(iptr handle) 331 { 332 b32 result = 0; 333 for (;;) { 334 i32 status; 335 iptr wait_pid = (iptr)waitpid((i32)handle, &status, 0); 336 if (wait_pid == -1) 337 build_fatal("failed to wait on child process: %s", strerror(errno)); 338 if (wait_pid == handle) { 339 if (WIFEXITED(status)) { 340 status = WEXITSTATUS(status); 341 /* TODO(rnp): logging */ 342 result = status == 0; 343 break; 344 } 345 if (WIFSIGNALED(status)) { 346 /* TODO(rnp): logging */ 347 result = 0; 348 break; 349 } 350 } else { 351 /* TODO(rnp): handle multiple children */ 352 InvalidCodePath; 353 } 354 } 355 return result; 356 } 357 358 #elif OS_WINDOWS 359 360 enum { 361 MOVEFILE_REPLACE_EXISTING = 0x01, 362 363 FILE_ATTRIBUTE_DIRECTORY = 0x10, 364 365 ERROR_FILE_NOT_FOUND = 0x02, 366 ERROR_PATH_NOT_FOUND = 0x03, 367 }; 368 369 #pragma pack(push, 1) 370 typedef struct { 371 u32 file_attributes; 372 u64 creation_time; 373 u64 last_access_time; 374 u64 last_write_time; 375 u64 file_size; 376 u64 reserved; 377 c8 file_name[260]; 378 c8 alternate_file_name[14]; 379 u32 file_type; 380 u32 creator_type; 381 u16 finder_flag; 382 } w32_find_data; 383 #pragma pack(pop) 384 385 W32(b32) CreateDirectoryA(c8 *, void *); 386 W32(b32) CreateProcessA(u8 *, u8 *, iptr, iptr, b32, u32, iptr, u8 *, iptr, iptr); 387 W32(b32) FindClose(iptr); 388 W32(iptr) FindFirstFileA(c8 *, w32_find_data *); 389 W32(b32) FindNextFileA(iptr, w32_find_data *); 390 W32(b32) GetExitCodeProcess(iptr, u32 *); 391 W32(b32) GetFileTime(iptr, iptr, iptr, iptr); 392 W32(b32) MoveFileExA(c8 *, c8 *, u32); 393 W32(b32) RemoveDirectoryA(c8 *); 394 395 function void 396 os_make_directory(char *name) 397 { 398 CreateDirectoryA(name, 0); 399 } 400 401 function b32 402 os_remove_directory(char *name) 403 { 404 w32_find_data find_data[1]; 405 char *search = temp_sprintf(".\\%s\\*", name); 406 iptr handle = FindFirstFileA(search, find_data); 407 b32 result = 1; 408 if (handle != INVALID_FILE) { 409 do { 410 s8 file_name = c_str_to_s8(find_data->file_name); 411 if (!s8_equal(s8("."), file_name) && !s8_equal(s8(".."), file_name)) { 412 char *full_path = temp_sprintf("%s" OS_PATH_SEPARATOR "%s", name, find_data->file_name); 413 if (find_data->file_attributes & FILE_ATTRIBUTE_DIRECTORY) { 414 char *wow_w32_is_even_worse_than_POSix = strdup(full_path); 415 os_remove_directory(wow_w32_is_even_worse_than_POSix); 416 free(wow_w32_is_even_worse_than_POSix); 417 } else { 418 DeleteFileA(full_path); 419 } 420 } 421 } while (FindNextFileA(handle, find_data)); 422 FindClose(handle); 423 } else { 424 i32 error = GetLastError(); 425 result = error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND; 426 } 427 RemoveDirectoryA(name); 428 return result; 429 } 430 431 function b32 432 os_rename_file(char *name, char *new) 433 { 434 b32 result = MoveFileExA(name, new, MOVEFILE_REPLACE_EXISTING) != 0; 435 return result; 436 } 437 438 function b32 439 os_remove_file(char *name) 440 { 441 b32 result = DeleteFileA(name); 442 return result; 443 } 444 445 function u64 446 os_get_filetime(char *file) 447 { 448 u64 result = (u64)-1; 449 iptr h = CreateFileA(file, 0, 0, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); 450 if (h != INVALID_FILE) { 451 union { struct { u32 low, high; }; u64 U64; } w32_filetime; 452 GetFileTime(h, 0, 0, (iptr)&w32_filetime); 453 result = w32_filetime.U64; 454 CloseHandle(h); 455 } 456 return result; 457 } 458 459 function iptr 460 os_spawn_process(CommandList *cmd, Stream sb) 461 { 462 struct { 463 u32 cb; 464 u8 *reserved, *desktop, *title; 465 u32 x, y, x_size, y_size, x_count_chars, y_count_chars; 466 u32 fill_attr, flags; 467 u16 show_window, reserved_2; 468 u8 *reserved_3; 469 iptr std_input, std_output, std_error; 470 } w32_startup_info = { 471 .cb = sizeof(w32_startup_info), 472 .flags = 0x100, 473 .std_input = GetStdHandle(STD_INPUT_HANDLE), 474 .std_output = GetStdHandle(STD_OUTPUT_HANDLE), 475 .std_error = GetStdHandle(STD_ERROR_HANDLE), 476 }; 477 478 struct { 479 iptr phandle, thandle; 480 u32 pid, tid; 481 } w32_process_info = {0}; 482 483 /* TODO(rnp): warn if we need to clamp last string */ 484 sb.widx = MIN(sb.widx, (i32)(KB(32) - 1)); 485 if (sb.widx < sb.cap) sb.data[sb.widx] = 0; 486 else sb.data[sb.widx - 1] = 0; 487 488 iptr result = INVALID_FILE; 489 if (CreateProcessA(0, sb.data, 0, 0, 1, 0, 0, 0, (iptr)&w32_startup_info, 490 (iptr)&w32_process_info)) 491 { 492 CloseHandle(w32_process_info.thandle); 493 result = w32_process_info.phandle; 494 } 495 return result; 496 } 497 498 function b32 499 os_wait_close_process(iptr handle) 500 { 501 b32 result = WaitForSingleObject(handle, (u32)-1) != 0xFFFFFFFFUL; 502 if (result) { 503 u32 status; 504 GetExitCodeProcess(handle, &status); 505 result = status == 0; 506 } 507 CloseHandle(handle); 508 return result; 509 } 510 511 #endif 512 513 #define needs_rebuild(b, ...) needs_rebuild_(b, ((char *[]){__VA_ARGS__}), \ 514 (sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) 515 function b32 516 needs_rebuild_(char *binary, char *deps[], iz deps_count) 517 { 518 u64 binary_filetime = os_get_filetime(binary); 519 u64 argv0_filetime = os_get_filetime(g_argv0); 520 b32 result = (binary_filetime == (u64)-1) | (argv0_filetime > binary_filetime); 521 for (iz i = 0; i < deps_count; i++) { 522 u64 filetime = os_get_filetime(deps[i]); 523 result |= (filetime == (u64)-1) | (filetime > binary_filetime); 524 } 525 return result; 526 } 527 528 function b32 529 run_synchronous(Arena a, CommandList *command) 530 { 531 Stream sb = arena_stream(a); 532 stream_push_command(&sb, command); 533 build_log_command("%.*s", (i32)sb.widx, sb.data); 534 return os_wait_close_process(os_spawn_process(command, sb)); 535 } 536 537 function b32 538 use_sanitization(void) 539 { 540 return config.sanitize && !is_msvc && !(is_w32 && is_gcc); 541 } 542 543 function void 544 cmd_base(Arena *a, CommandList *c) 545 { 546 Config *o = &config; 547 548 cmd_append(a, c, COMPILER); 549 550 if (!is_msvc) { 551 /* TODO(rnp): support cross compiling with clang */ 552 if (!o->generic) cmd_append(a, c, "-march=native"); 553 else if (is_amd64) cmd_append(a, c, "-march=x86-64-v3", "-msse4.1"); 554 else if (is_aarch64) cmd_append(a, c, "-march=armv8"); 555 } 556 557 cmd_append(a, c, COMMON_FLAGS); 558 if (o->debug) cmd_append(a, c, DEBUG_FLAGS); 559 else cmd_append(a, c, OPTIMIZED_FLAGS); 560 561 /* NOTE: ancient gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80454 */ 562 if (is_gcc) cmd_append(a, c, "-Wno-missing-braces"); 563 564 if (!is_msvc) cmd_append(a, c, "-fms-extensions"); 565 566 if (o->debug && is_unix) cmd_append(a, c, "-gdwarf-4"); 567 568 /* NOTE(rnp): need to avoid w32-gcc for ci */ 569 b32 sanitize = use_sanitization(); 570 if (sanitize) cmd_append(a, c, "-fsanitize=address,undefined"); 571 if (!sanitize && o->sanitize) build_log_warning("santizers not supported with this compiler"); 572 } 573 574 function void 575 check_rebuild_self(Arena arena, i32 argc, char *argv[]) 576 { 577 char *binary = shift(argv, argc); 578 if (needs_rebuild(binary, __FILE__, "os_win32.c", "os_linux.c", "util.c", "util.h", "beamformer_parameters.h")) { 579 Stream name_buffer = arena_stream(arena); 580 stream_append_s8s(&name_buffer, c_str_to_s8(binary), s8(".old")); 581 char *old_name = (char *)arena_stream_commit_zero(&arena, &name_buffer).data; 582 583 if (!os_rename_file(binary, old_name)) 584 build_fatal("failed to move: %s -> %s", binary, old_name); 585 586 CommandList c = {0}; 587 cmd_base(&arena, &c); 588 cmd_append(&arena, &c, EXTRA_FLAGS); 589 if (!is_msvc) cmd_append(&arena, &c, "-Wno-unused-function"); 590 cmd_append(&arena, &c, __FILE__, OUTPUT_EXE(binary)); 591 if (is_msvc) cmd_append(&arena, &c, "/link", "-incremental:no", "-opt:ref"); 592 cmd_append(&arena, &c, (void *)0); 593 if (!run_synchronous(arena, &c)) { 594 os_rename_file(old_name, binary); 595 build_fatal("failed to rebuild self"); 596 } 597 os_remove_file(old_name); 598 599 c.count = 0; 600 cmd_append(&arena, &c, binary); 601 cmd_append_count(&arena, &c, argv, argc); 602 cmd_append(&arena, &c, (void *)0); 603 if (!run_synchronous(arena, &c)) 604 os_exit(1); 605 606 os_exit(0); 607 } 608 } 609 610 function void 611 usage(char *argv0) 612 { 613 printf("%s [--bake-shaders] [--debug] [--sanitize] [--time]\n" 614 " --debug: dynamically link and build with debug symbols\n" 615 " --generic: compile for a generic target (x86-64-v3 or armv8 with NEON)\n" 616 " --sanitize: build with ASAN and UBSAN\n" 617 " --tests: also build programs in tests/\n" 618 " --time: print build time\n" 619 , argv0); 620 os_exit(0); 621 } 622 623 function void 624 parse_config(i32 argc, char *argv[]) 625 { 626 char *argv0 = shift(argv, argc); 627 while (argc > 0) { 628 char *arg = shift(argv, argc); 629 s8 str = c_str_to_s8(arg); 630 if (s8_equal(str, s8("--bake-shaders"))) { 631 config.bake_shaders = 1; 632 } else if (s8_equal(str, s8("--debug"))) { 633 config.debug = 1; 634 } else if (s8_equal(str, s8("--generic"))) { 635 config.generic = 1; 636 } else if (s8_equal(str, s8("--sanitize"))) { 637 config.sanitize = 1; 638 } else if (s8_equal(str, s8("--tests"))) { 639 config.tests = 1; 640 } else if (s8_equal(str, s8("--time"))) { 641 config.time = 1; 642 } else { 643 usage(argv0); 644 } 645 } 646 } 647 648 /* NOTE(rnp): produce pdbs on w32 */ 649 function void 650 cmd_pdb(Arena *a, CommandList *cmd, char *name) 651 { 652 if (is_w32 && is_clang) { 653 cmd_append(a, cmd, "-fuse-ld=lld", "-g", "-gcodeview", "-Wl,--pdb="); 654 } else if (is_msvc) { 655 Stream sb = arena_stream(*a); 656 stream_append_s8s(&sb, s8("-PDB:"), c_str_to_s8(name), s8(".pdb")); 657 char *pdb = (char *)arena_stream_commit_zero(a, &sb).data; 658 cmd_append(a, cmd, "/link", "-incremental:no", "-opt:ref", "-DEBUG", pdb); 659 } 660 } 661 662 function void 663 git_submodule_update(Arena a, char *name) 664 { 665 Stream sb = arena_stream(a); 666 stream_append_s8s(&sb, c_str_to_s8(name), s8(OS_PATH_SEPARATOR), s8(".git")); 667 arena_stream_commit_zero(&a, &sb); 668 669 CommandList git = {0}; 670 /* NOTE(rnp): cryptic bs needed to get a simple exit code if name is dirty */ 671 cmd_append(&a, &git, "git", "diff-index", "--quiet", "HEAD", "--", name, (void *)0); 672 if (!os_file_exists((c8 *)sb.data) || !run_synchronous(a, &git)) { 673 git.count = 1; 674 cmd_append(&a, &git, "submodule", "update", "--init", "--depth=1", name, (void *)0); 675 if (!run_synchronous(a, &git)) 676 build_fatal("failed to clone required module: %s", name); 677 } 678 } 679 680 function b32 681 build_shared_library(Arena a, CommandList cc, char *name, char *output, char **libs, iz libs_count, char **srcs, iz srcs_count) 682 { 683 cmd_append_count(&a, &cc, srcs, srcs_count); 684 cmd_append(&a, &cc, OUTPUT_DLL(output)); 685 cmd_pdb(&a, &cc, name); 686 cmd_append_count(&a, &cc, libs, libs_count); 687 cmd_append(&a, &cc, (void *)0); 688 b32 result = run_synchronous(a, &cc); 689 if (!result) build_log_failure("%s", output); 690 return result; 691 } 692 693 function b32 694 cc_single_file(Arena a, CommandList cc, char *exe, char *src, char *dest, char **tail, iz tail_count) 695 { 696 char *executable[] = {src, is_msvc? "/Fe:" : "-o", dest}; 697 char *object[] = {is_msvc? "/c" : "-c", src, is_msvc? "/Fo:" : "-o", dest}; 698 699 700 cmd_append_count(&a, &cc, exe? executable : object, 701 exe? countof(executable) : countof(object)); 702 if (exe) cmd_pdb(&a, &cc, exe); 703 cmd_append_count(&a, &cc, tail, tail_count); 704 cmd_append(&a, &cc, (void *)0); 705 b32 result = run_synchronous(a, &cc); 706 if (!result) build_log_failure("%s", dest); 707 return result; 708 } 709 710 function b32 711 build_static_library_from_objects(Arena a, char *name, char **flags, iz flags_count, char **objects, iz count) 712 { 713 CommandList ar = {0}; 714 cmd_append(&a, &ar, STATIC_LIBRARY_BEGIN(name)); 715 cmd_append_count(&a, &ar, flags, flags_count); 716 cmd_append_count(&a, &ar, objects, count); 717 cmd_append(&a, &ar, (void *)0); 718 b32 result = run_synchronous(a, &ar); 719 if (!result) build_log_failure("%s", name); 720 return result; 721 } 722 723 function b32 724 build_static_library(Arena a, CommandList cc, char *name, char **deps, char **outputs, iz count) 725 { 726 /* TODO(rnp): refactor to not need outputs */ 727 b32 result = 1; 728 for (iz i = 0; i < count; i++) 729 result &= cc_single_file(a, cc, 0, deps[i], outputs[i], 0, 0); 730 if (result) result = build_static_library_from_objects(a, name, 0, 0, outputs, count); 731 return result; 732 } 733 734 function b32 735 check_build_raylib(Arena a) 736 { 737 b32 result = 1, shared = config.debug; 738 char *libraylib = shared ? OS_SHARED_LINK_LIB("raylib") : OUTPUT_LIB(OS_STATIC_LIB("raylib")); 739 if (needs_rebuild(libraylib, "external/include/rlgl.h", "external/raylib")) { 740 git_submodule_update(a, "external/raylib"); 741 os_copy_file("external/raylib/src/rlgl.h", "external/include/rlgl.h"); 742 743 CommandList cc = {0}; 744 cmd_base(&a, &cc); 745 if (is_unix) cmd_append(&a, &cc, "-D_GLFW_X11"); 746 cmd_append(&a, &cc, "-DPLATFORM_DESKTOP_GLFW"); 747 if (!is_msvc) cmd_append(&a, &cc, "-Wno-unused-but-set-variable"); 748 cmd_append(&a, &cc, "-Iexternal/include", "-Iexternal/raylib/src", "-Iexternal/raylib/src/external/glfw/include"); 749 #define RAYLIB_SOURCES \ 750 X(rglfw) \ 751 X(rshapes) \ 752 X(rtext) \ 753 X(rtextures) \ 754 X(utils) 755 #define X(name) "external/raylib/src/" #name ".c", 756 char *srcs[] = {"external/rcore_extended.c", RAYLIB_SOURCES}; 757 #undef X 758 #define X(name) OUTPUT(OBJECT(#name)), 759 char *outs[] = {OUTPUT(OBJECT("rcore_extended")), RAYLIB_SOURCES}; 760 #undef X 761 762 if (shared) { 763 char *libs[] = {LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), LINK_LIB("winmm")}; 764 iz libs_count = is_w32 ? countof(libs) : 0; 765 cmd_append(&a, &cc, "-DBUILD_LIBTYPE_SHARED", "-D_GLFW_BUILD_DLL"); 766 result = build_shared_library(a, cc, "raylib", libraylib, libs, libs_count, srcs, countof(srcs)); 767 } else { 768 result = build_static_library(a, cc, libraylib, srcs, outs, countof(srcs)); 769 } 770 } 771 return result; 772 } 773 774 function b32 775 build_helper_library(Arena arena) 776 { 777 CommandList cc = {0}; 778 cmd_base(&arena, &cc); 779 cmd_append(&arena, &cc, EXTRA_FLAGS); 780 781 ///////////// 782 // library 783 char *library = OUTPUT(OS_SHARED_LIB("ogl_beamformer_lib")); 784 char *libs[] = {LINK_LIB("Synchronization")}; 785 iz libs_count = is_w32 ? countof(libs) : 0; 786 787 if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function"); 788 b32 result = build_shared_library(arena, cc, "ogl_beamformer_lib", library, 789 libs, libs_count, (char *[]){"lib/ogl_beamformer_lib.c"}, 1); 790 return result; 791 } 792 793 function void 794 cmd_beamformer_base(Arena *a, CommandList *c) 795 { 796 cmd_base(a, c); 797 cmd_append(a, c, "-Iexternal/include"); 798 cmd_append(a, c, EXTRA_FLAGS); 799 cmd_append(a, c, config.bake_shaders? "-DBakeShaders=1" : "-DBakeShaders=0"); 800 if (config.debug) cmd_append(a, c, "-DBEAMFORMER_DEBUG", "-DBEAMFORMER_RENDERDOC_HOOKS"); 801 802 /* NOTE(rnp): impossible to autodetect on GCC versions < 14 (ci has 13) */ 803 cmd_append(a, c, use_sanitization() ? "-DASAN_ACTIVE=1" : "-DASAN_ACTIVE=0"); 804 } 805 806 function b32 807 build_beamformer_main(Arena arena) 808 { 809 CommandList c = {0}; 810 cmd_beamformer_base(&arena, &c); 811 812 cmd_append(&arena, &c, OS_MAIN, OUTPUT_EXE("ogl")); 813 cmd_pdb(&arena, &c, "ogl"); 814 if (config.debug) { 815 if (!is_w32) cmd_append(&arena, &c, "-Wl,--export-dynamic", "-Wl,-rpath,."); 816 if (!is_msvc) cmd_append(&arena, &c, "-L."); 817 cmd_append(&arena, &c, LINK_LIB("raylib")); 818 } else { 819 cmd_append(&arena, &c, OUTPUT(OS_STATIC_LIB("raylib"))); 820 } 821 if (!is_msvc) cmd_append(&arena, &c, "-lm"); 822 if (is_unix) cmd_append(&arena, &c, "-lGL"); 823 if (is_w32) { 824 cmd_append(&arena, &c, LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), 825 LINK_LIB("opengl32"), LINK_LIB("winmm"), LINK_LIB("Synchronization")); 826 if (!is_msvc) cmd_append(&arena, &c, "-Wl,--out-implib," OUTPUT(OS_STATIC_LIB("main"))); 827 } 828 cmd_append(&arena, &c, (void *)0); 829 830 return run_synchronous(arena, &c); 831 } 832 833 function b32 834 build_beamformer_as_library(Arena arena) 835 { 836 CommandList cc = {0}; 837 cmd_beamformer_base(&arena, &cc); 838 839 if (is_msvc) { 840 build_static_library_from_objects(arena, OUTPUT_LIB(OS_STATIC_LIB("main")), 841 arg_list(char *, "/def", "/name:ogl.exe"), 842 arg_list(char *, OUTPUT(OBJECT("main_w32")))); 843 } 844 845 char *library = OS_SHARED_LIB("beamformer"); 846 char *libs[] = {!is_msvc? "-L." : "", LINK_LIB("raylib"), LINK_LIB("gdi32"), 847 LINK_LIB("shell32"), LINK_LIB("user32"), LINK_LIB("opengl32"), 848 LINK_LIB("winmm"), LINK_LIB("Synchronization"), OUTPUT("main.lib")}; 849 iz libs_count = is_w32 ? countof(libs) : 0; 850 cmd_append(&arena, &cc, "-D_BEAMFORMER_DLL"); 851 b32 result = build_shared_library(arena, cc, "beamformer", library, 852 libs, libs_count, arg_list(char *, "beamformer_core.c")); 853 return result; 854 } 855 856 function b32 857 build_tests(Arena arena) 858 { 859 CommandList cc = {0}; 860 cmd_base(&arena, &cc); 861 cmd_append(&arena, &cc, EXTRA_FLAGS); 862 863 #define TEST_PROGRAMS \ 864 X("throughput", LINK_LIB("zstd"), W32_DECL(LINK_LIB("Synchronization"))) 865 866 os_make_directory(OUTPUT("tests")); 867 if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function"); 868 cmd_append(&arena, &cc, "-I.", "-Ilib"); 869 870 b32 result = 1; 871 iz cc_count = cc.count; 872 #define X(prog, ...) \ 873 result &= cc_single_file(arena, cc, prog, "tests/" prog ".c", \ 874 OUTPUT("tests/" prog), \ 875 arg_list(char *, ##__VA_ARGS__)); \ 876 cc.count = cc_count; 877 TEST_PROGRAMS 878 #undef X 879 return result; 880 } 881 882 typedef struct { 883 s8 *data; 884 iz count; 885 iz capacity; 886 } s8_list; 887 888 function s8 889 s8_chop(s8 *in, iz count) 890 { 891 count = CLAMP(count, 0, in->len); 892 s8 result = {.data = in->data, .len = count}; 893 in->data += count; 894 in->len -= count; 895 return result; 896 } 897 898 function void 899 s8_split(s8 str, s8 *left, s8 *right, u8 byte) 900 { 901 iz i; 902 for (i = 0; i < str.len; i++) if (str.data[i] == byte) break; 903 904 if (left) *left = (s8){.data = str.data, .len = i}; 905 if (right) { 906 right->data = str.data + i + 1; 907 right->len = MAX(0, str.len - (i + 1)); 908 } 909 } 910 911 function s8 912 s8_trim(s8 in) 913 { 914 s8 result = in; 915 for (iz i = 0; i < in.len && *result.data == ' '; i++) result.data++; 916 result.len -= result.data - in.data; 917 for (; result.len > 0 && result.data[result.len - 1] == ' '; result.len--); 918 return result; 919 } 920 921 typedef struct { 922 Stream stream; 923 Arena scratch; 924 i32 indentation_level; 925 } MetaprogramContext; 926 927 function b32 928 meta_write_and_reset(MetaprogramContext *m, char *file) 929 { 930 b32 result = os_write_new_file(file, stream_to_s8(&m->stream)); 931 if (!result) build_log_failure("%s", file); 932 m->stream.widx = 0; 933 m->indentation_level = 0; 934 return result; 935 } 936 937 #define meta_push(m, ...) meta_push_(m, arg_list(s8, __VA_ARGS__)) 938 function void 939 meta_push_(MetaprogramContext *m, s8 *items, iz count) 940 { 941 stream_append_s8s_(&m->stream, items, count); 942 } 943 944 #define meta_pad(m, b, n) stream_pad(&(m)->stream, (b), (n)) 945 #define meta_indent(m) meta_pad((m), '\t', (m)->indentation_level) 946 #define meta_begin_line(m, ...) do { meta_indent(m); meta_push(m, __VA_ARGS__); } while(0) 947 #define meta_end_line(m, ...) meta_push(m, ##__VA_ARGS__, s8("\n")) 948 #define meta_push_line(m, ...) do { meta_indent(m); meta_push(m, ##__VA_ARGS__, s8("\n")); } while(0) 949 #define meta_begin_scope(m, ...) do { meta_push_line(m, __VA_ARGS__); (m)->indentation_level++; } while(0) 950 #define meta_end_scope(m, ...) do { (m)->indentation_level--; meta_push_line(m, __VA_ARGS__); } while(0) 951 #define meta_push_u64(m, n) stream_append_u64(&(m)->stream, (n)) 952 #define meta_push_i64(m, n) stream_append_i64(&(m)->stream, (n)) 953 #define meta_push_u64_hex(m, n) stream_append_hex_u64(&(m)->stream, (n)) 954 #define meta_push_u64_hex_width(m, n, w) stream_append_hex_u64_width(&(m)->stream, (n), (w)) 955 956 #define meta_begin_matlab_class_cracker(_1, _2, FN, ...) FN 957 #define meta_begin_matlab_class_1(m, name) meta_begin_scope(m, s8("classdef " name)) 958 #define meta_begin_matlab_class_2(m, name, type) \ 959 meta_begin_scope(m, s8("classdef " name " < " type)) 960 961 #define meta_begin_matlab_class(m, ...) \ 962 meta_begin_matlab_class_cracker(__VA_ARGS__, \ 963 meta_begin_matlab_class_2, \ 964 meta_begin_matlab_class_1)(m, __VA_ARGS__) 965 966 function void 967 meta_push_matlab_property(MetaprogramContext *m, s8 name, u64 length, s8 kind) 968 { 969 meta_begin_line(m, name, s8("(1,")); 970 meta_push_u64(m, (u64)length); 971 meta_end_line(m, s8(")"), kind.len > 0 ? s8(" ") : s8(""), kind); 972 } 973 974 function b32 975 meta_end_and_write_matlab(MetaprogramContext *m, char *path) 976 { 977 while (m->indentation_level > 0) meta_end_scope(m, s8("end")); 978 b32 result = meta_write_and_reset(m, path); 979 return result; 980 } 981 982 #define META_ENTRY_KIND_LIST \ 983 X(Invalid) \ 984 X(Array) \ 985 X(Bake) \ 986 X(BakeInt) \ 987 X(BakeFloat) \ 988 X(BeginScope) \ 989 X(Emit) \ 990 X(Embed) \ 991 X(EndScope) \ 992 X(Enumeration) \ 993 X(Expand) \ 994 X(Flags) \ 995 X(MUnion) \ 996 X(String) \ 997 X(Shader) \ 998 X(ShaderGroup) \ 999 X(SubShader) \ 1000 X(Table) 1001 1002 typedef enum { 1003 #define X(k, ...) MetaEntryKind_## k, 1004 META_ENTRY_KIND_LIST 1005 #undef X 1006 MetaEntryKind_Count, 1007 } MetaEntryKind; 1008 1009 #define X(k, ...) #k, 1010 read_only global char *meta_entry_kind_strings[] = {META_ENTRY_KIND_LIST}; 1011 #undef X 1012 1013 #define META_EMIT_LANG_LIST \ 1014 X(C) \ 1015 X(CLibrary) \ 1016 X(MATLAB) 1017 1018 typedef enum { 1019 #define X(k, ...) MetaEmitLang_## k, 1020 META_EMIT_LANG_LIST 1021 #undef X 1022 MetaEmitLang_Count, 1023 } MetaEmitLang; 1024 1025 #define META_KIND_LIST \ 1026 X(M4, m4, float, single, 64, 16) \ 1027 X(SV4, iv4, int32_t, int32, 16, 4) \ 1028 X(UV4, uv4, uint32_t, uint32, 16, 4) \ 1029 X(UV2, uv2, uint32_t, uint32, 8, 2) \ 1030 X(V3, v3, float, single, 12, 3) \ 1031 X(V2, v2, float, single, 8, 2) \ 1032 X(F32, f32, float, single, 4, 1) \ 1033 X(S32, i32, int32_t, int32, 4, 1) \ 1034 X(S16, i16, int16_t, int16, 2, 1) \ 1035 X(S8, i8, int8_t, int8, 1, 1) \ 1036 X(U32, u32, uint32_t, uint32, 4, 1) \ 1037 X(U16, u16, uint16_t, uint16, 2, 1) \ 1038 X(U8, u8, uint8_t, uint8, 1, 1) 1039 1040 typedef enum { 1041 #define X(k, ...) MetaKind_## k, 1042 META_KIND_LIST 1043 #undef X 1044 MetaKind_Count, 1045 } MetaKind; 1046 1047 read_only global u8 meta_kind_byte_sizes[] = { 1048 #define X(_k, _c, _b, _m, bytes, ...) bytes, 1049 META_KIND_LIST 1050 #undef X 1051 }; 1052 1053 read_only global u8 meta_kind_elements[] = { 1054 #define X(_k, _c, _b, _m, _by, elements, ...) elements, 1055 META_KIND_LIST 1056 #undef X 1057 }; 1058 1059 read_only global s8 meta_kind_meta_types[] = { 1060 #define X(k, ...) s8_comp(#k), 1061 META_KIND_LIST 1062 #undef X 1063 }; 1064 1065 read_only global s8 meta_kind_matlab_types[] = { 1066 #define X(_k, _c, _b, m, ...) s8_comp(#m), 1067 META_KIND_LIST 1068 #undef X 1069 }; 1070 1071 read_only global s8 meta_kind_base_c_types[] = { 1072 #define X(_k, _c, base, ...) s8_comp(#base), 1073 META_KIND_LIST 1074 #undef X 1075 }; 1076 1077 read_only global s8 meta_kind_c_types[] = { 1078 #define X(_k, c, ...) s8_comp(#c), 1079 META_KIND_LIST 1080 #undef X 1081 }; 1082 1083 typedef struct { u32 line, column; } MetaLocation; 1084 1085 #define META_ENTRY_ARGUMENT_KIND_LIST \ 1086 X(None) \ 1087 X(String) \ 1088 X(Array) 1089 1090 #define X(k, ...) MetaEntryArgumentKind_## k, 1091 typedef enum {META_ENTRY_ARGUMENT_KIND_LIST} MetaEntryArgumentKind; 1092 #undef X 1093 1094 typedef struct { 1095 MetaEntryArgumentKind kind; 1096 MetaLocation location; 1097 union { 1098 s8 string; 1099 struct { 1100 s8 *strings; 1101 u64 count; 1102 }; 1103 }; 1104 } MetaEntryArgument; 1105 1106 typedef struct { 1107 MetaEntryKind kind; 1108 u32 argument_count; 1109 MetaEntryArgument *arguments; 1110 s8 name; 1111 MetaLocation location; 1112 } MetaEntry; 1113 1114 typedef struct { 1115 MetaEntry *data; 1116 iz count; 1117 iz capacity; 1118 s8 raw; 1119 } MetaEntryStack; 1120 1121 #define META_PARSE_TOKEN_LIST \ 1122 X('@', Entry) \ 1123 X('`', RawString) \ 1124 X('(', BeginArgs) \ 1125 X(')', EndArgs) \ 1126 X('[', BeginArray) \ 1127 X(']', EndArray) \ 1128 X('{', BeginScope) \ 1129 X('}', EndScope) 1130 1131 typedef enum { 1132 MetaParseToken_EOF, 1133 MetaParseToken_String, 1134 #define X(__1, kind, ...) MetaParseToken_## kind, 1135 META_PARSE_TOKEN_LIST 1136 #undef X 1137 MetaParseToken_Count, 1138 } MetaParseToken; 1139 1140 typedef union { 1141 MetaEntryKind kind; 1142 s8 string; 1143 } MetaParseUnion; 1144 1145 typedef struct { 1146 s8 s; 1147 MetaLocation location; 1148 } MetaParsePoint; 1149 1150 typedef struct { 1151 MetaParsePoint p; 1152 MetaParseUnion u; 1153 MetaParsePoint save_point; 1154 } MetaParser; 1155 1156 global char *compiler_file; 1157 global jmp_buf compiler_jmp_buf; 1158 1159 #define meta_parser_save(v) (v)->save_point = (v)->p 1160 #define meta_parser_restore(v) swap((v)->p, (v)->save_point) 1161 #define meta_parser_commit(v) meta_parser_restore(v) 1162 1163 #define meta_compiler_error_message(loc, format, ...) \ 1164 fprintf(stderr, "%s:%u:%u: error: "format, compiler_file, \ 1165 loc.line + 1, loc.column + 1, ##__VA_ARGS__) 1166 1167 #define meta_compiler_error(loc, format, ...) do { \ 1168 meta_compiler_error_message(loc, format, ##__VA_ARGS__); \ 1169 meta_error(); \ 1170 } while (0) 1171 1172 #define meta_entry_error(e, ...) meta_entry_error_column((e), (i32)(e)->location.column, __VA_ARGS__) 1173 #define meta_entry_error_column(e, column, ...) do { \ 1174 meta_compiler_error_message((e)->location, __VA_ARGS__); \ 1175 meta_entry_print((e), 2 * (column), 0); \ 1176 meta_error(); \ 1177 } while(0) 1178 1179 #define meta_entry_pair_error(e, prefix, base_kind) \ 1180 meta_entry_error(e, prefix"@%s() in @%s()\n", \ 1181 meta_entry_kind_strings[(e)->kind], \ 1182 meta_entry_kind_strings[(base_kind)]) 1183 1184 #define meta_entry_nesting_error(e, base_kind) meta_entry_pair_error(e, "invalid nesting: ", base_kind) 1185 1186 #define meta_entry_error_location(e, loc, ...) do { \ 1187 meta_compiler_error_message((loc), __VA_ARGS__); \ 1188 meta_entry_print((e), 1, (i32)(loc).column); \ 1189 meta_error(); \ 1190 } while (0) 1191 1192 function no_return void 1193 meta_error(void) 1194 { 1195 assert(0); 1196 longjmp(compiler_jmp_buf, 1); 1197 } 1198 1199 function void 1200 meta_entry_print(MetaEntry *e, i32 indent, i32 caret) 1201 { 1202 char *kind = meta_entry_kind_strings[e->kind]; 1203 if (e->kind == MetaEntryKind_BeginScope) kind = "{"; 1204 if (e->kind == MetaEntryKind_EndScope) kind = "}"; 1205 1206 fprintf(stderr, "%*s@%s", indent, "", kind); 1207 1208 if (e->argument_count) { 1209 fprintf(stderr, "("); 1210 for (u32 i = 0; i < e->argument_count; i++) { 1211 MetaEntryArgument *a = e->arguments + i; 1212 if (i != 0) fprintf(stderr, " "); 1213 if (a->kind == MetaEntryArgumentKind_Array) { 1214 fprintf(stderr, "["); 1215 for (u64 j = 0; j < a->count; j++) { 1216 if (j != 0) fprintf(stderr, " "); 1217 fprintf(stderr, "%.*s", (i32)a->strings[j].len, a->strings[j].data); 1218 } 1219 fprintf(stderr, "]"); 1220 } else { 1221 fprintf(stderr, "%.*s", (i32)a->string.len, a->string.data); 1222 } 1223 } 1224 fprintf(stderr, ")"); 1225 } 1226 if (e->name.len) fprintf(stderr, " %.*s", (i32)e->name.len, e->name.data); 1227 1228 if (caret >= 0) fprintf(stderr, "\n%*s^", indent + caret, ""); 1229 1230 fprintf(stderr, "\n"); 1231 } 1232 1233 function iz 1234 meta_lookup_string_slow(s8 *strings, iz string_count, s8 s) 1235 { 1236 // TODO(rnp): obviously this is slow 1237 iz result = -1; 1238 for (iz i = 0; i < string_count; i++) { 1239 if (s8_equal(s, strings[i])) { 1240 result = i; 1241 break; 1242 } 1243 } 1244 return result; 1245 } 1246 1247 function MetaEntryKind 1248 meta_entry_kind_from_string(s8 s) 1249 { 1250 #define X(k, ...) s8_comp(#k), 1251 read_only local_persist s8 kinds[] = {META_ENTRY_KIND_LIST}; 1252 #undef X 1253 MetaEntryKind result = MetaEntryKind_Invalid; 1254 iz id = meta_lookup_string_slow(kinds + 1, countof(kinds) - 1, s); 1255 if (id > 0) result = (MetaEntryKind)(id + 1); 1256 return result; 1257 } 1258 1259 function void 1260 meta_parser_trim(MetaParser *p) 1261 { 1262 u8 *s, *end = p->p.s.data + p->p.s.len; 1263 b32 done = 0; 1264 b32 comment = 0; 1265 for (s = p->p.s.data; !done && s != end;) { 1266 switch (*s) { 1267 case '\r': case '\t': case ' ': 1268 { 1269 p->p.location.column++; 1270 }break; 1271 case '\n':{ p->p.location.line++; p->p.location.column = 0; comment = 0; }break; 1272 case '/':{ 1273 comment = ((s + 1) != end && s[1] == '/'); 1274 if (comment) s++; 1275 } /* FALLTHROUGH */ 1276 default:{done = !comment;}break; 1277 } 1278 if (!done) s++; 1279 } 1280 p->p.s.data = s; 1281 p->p.s.len = end - s; 1282 } 1283 1284 function s8 1285 meta_parser_extract_raw_string(MetaParser *p) 1286 { 1287 s8 result = {.data = p->p.s.data}; 1288 for (; result.len < p->p.s.len; result.len++) { 1289 u8 byte = p->p.s.data[result.len]; 1290 p->p.location.column++; 1291 if (byte == '`') { 1292 break; 1293 } else if (byte == '\n') { 1294 p->p.location.column = 0; 1295 p->p.location.line++; 1296 } 1297 } 1298 p->p.s.data += (result.len + 1); 1299 p->p.s.len -= (result.len + 1); 1300 return result; 1301 } 1302 1303 function s8 1304 meta_parser_extract_string(MetaParser *p) 1305 { 1306 s8 result = {.data = p->p.s.data}; 1307 for (; result.len < p->p.s.len; result.len++) { 1308 b32 done = 0; 1309 switch (p->p.s.data[result.len]) { 1310 #define X(t, ...) case t: 1311 META_PARSE_TOKEN_LIST 1312 #undef X 1313 case ' ': case '\n': case '\r': case '\t': 1314 {done = 1;}break; 1315 case '/':{ 1316 done = (result.len + 1 < p->p.s.len) && (p->p.s.data[result.len + 1] == '/'); 1317 }break; 1318 default:{}break; 1319 } 1320 if (done) break; 1321 } 1322 p->p.location.column += (u32)result.len; 1323 p->p.s.data += result.len; 1324 p->p.s.len -= result.len; 1325 return result; 1326 } 1327 1328 function s8 1329 meta_parser_token_name(MetaParser *p, MetaParseToken t) 1330 { 1331 s8 result = s8("\"invalid\""); 1332 read_only local_persist s8 names[MetaParseToken_Count] = { 1333 [MetaParseToken_EOF] = s8_comp("\"EOF\""), 1334 #define X(k, v, ...) [MetaParseToken_## v] = s8_comp(#k), 1335 META_PARSE_TOKEN_LIST 1336 #undef X 1337 }; 1338 if (t >= 0 && t < countof(names)) result = names[t]; 1339 if (t == MetaParseToken_String) result = p->u.string; 1340 if (t == MetaParseToken_RawString) result = (s8){.data = p->u.string.data - 1, .len = p->u.string.len + 1}; 1341 return result; 1342 } 1343 1344 function MetaParseToken 1345 meta_parser_token(MetaParser *p) 1346 { 1347 MetaParseToken result = MetaParseToken_EOF; 1348 meta_parser_save(p); 1349 if (p->p.s.len > 0) { 1350 b32 chop = 1; 1351 switch (p->p.s.data[0]) { 1352 #define X(t, kind, ...) case t:{ result = MetaParseToken_## kind; }break; 1353 META_PARSE_TOKEN_LIST 1354 #undef X 1355 default:{ result = MetaParseToken_String; chop = 0; }break; 1356 } 1357 if (chop) { s8_chop(&p->p.s, 1); p->p.location.column++; } 1358 1359 if (result != MetaParseToken_RawString) meta_parser_trim(p); 1360 switch (result) { 1361 case MetaParseToken_RawString:{ p->u.string = meta_parser_extract_raw_string(p); }break; 1362 case MetaParseToken_String:{ p->u.string = meta_parser_extract_string(p); }break; 1363 1364 /* NOTE(rnp): '{' and '}' are shorthand for @BeginScope and @EndScope */ 1365 case MetaParseToken_BeginScope:{ p->u.kind = MetaEntryKind_BeginScope; }break; 1366 case MetaParseToken_EndScope:{ p->u.kind = MetaEntryKind_EndScope; }break; 1367 1368 /* NOTE(rnp): loose '[' implies implicit @Array() */ 1369 case MetaParseToken_BeginArray:{ p->u.kind = MetaEntryKind_Array; }break; 1370 1371 case MetaParseToken_Entry:{ 1372 s8 kind = meta_parser_extract_string(p); 1373 p->u.kind = meta_entry_kind_from_string(kind); 1374 if (p->u.kind == MetaEntryKind_Invalid) { 1375 meta_compiler_error(p->p.location, "invalid keyword: @%.*s\n", (i32)kind.len, kind.data); 1376 } 1377 }break; 1378 default:{}break; 1379 } 1380 meta_parser_trim(p); 1381 } 1382 1383 return result; 1384 } 1385 1386 function MetaParseToken 1387 meta_parser_peek_token(MetaParser *p) 1388 { 1389 MetaParseToken result = meta_parser_token(p); 1390 meta_parser_restore(p); 1391 return result; 1392 } 1393 1394 function void 1395 meta_parser_unexpected_token(MetaParser *p, MetaParseToken t) 1396 { 1397 meta_parser_restore(p); 1398 s8 token_name = meta_parser_token_name(p, t); 1399 meta_compiler_error(p->p.location, "unexpected token: %.*s\n", (i32)token_name.len, token_name.data); 1400 } 1401 1402 function void 1403 meta_parser_fill_argument_array(MetaParser *p, MetaEntryArgument *array, Arena *arena) 1404 { 1405 array->kind = MetaEntryArgumentKind_Array; 1406 array->strings = arena_aligned_start(*arena, alignof(s8)); 1407 array->location = p->p.location; 1408 for (MetaParseToken token = meta_parser_token(p); 1409 token != MetaParseToken_EndArray; 1410 token = meta_parser_token(p)) 1411 { 1412 switch (token) { 1413 case MetaParseToken_RawString: 1414 case MetaParseToken_String: 1415 { 1416 assert((u8 *)(array->strings + array->count) == arena->beg); 1417 *push_struct(arena, s8) = p->u.string; 1418 array->count++; 1419 }break; 1420 default:{ meta_parser_unexpected_token(p, token); }break; 1421 } 1422 } 1423 } 1424 1425 function void 1426 meta_parser_arguments(MetaParser *p, MetaEntry *e, Arena *arena) 1427 { 1428 if (meta_parser_peek_token(p) == MetaParseToken_BeginArgs) { 1429 meta_parser_commit(p); 1430 1431 e->arguments = arena_aligned_start(*arena, alignof(MetaEntryArgument)); 1432 for (MetaParseToken token = meta_parser_token(p); 1433 token != MetaParseToken_EndArgs; 1434 token = meta_parser_token(p)) 1435 { 1436 e->argument_count++; 1437 MetaEntryArgument *arg = push_struct(arena, MetaEntryArgument); 1438 switch (token) { 1439 case MetaParseToken_RawString: 1440 case MetaParseToken_String: 1441 { 1442 arg->kind = MetaEntryArgumentKind_String; 1443 arg->string = p->u.string; 1444 arg->location = p->p.location; 1445 }break; 1446 case MetaParseToken_BeginArray:{ 1447 meta_parser_fill_argument_array(p, arg, arena); 1448 }break; 1449 default:{ meta_parser_unexpected_token(p, token); }break; 1450 } 1451 } 1452 } 1453 } 1454 1455 typedef struct { 1456 MetaEntry *start; 1457 MetaEntry *one_past_last; 1458 iz consumed; 1459 } MetaEntryScope; 1460 1461 function MetaEntryScope 1462 meta_entry_extract_scope(MetaEntry *base, iz entry_count) 1463 { 1464 assert(base->kind != MetaEntryKind_BeginScope && base->kind != MetaEntryKind_EndScope); 1465 assert(entry_count > 0); 1466 1467 MetaEntryScope result = {.start = base + 1, .consumed = 1}; 1468 iz sub_scope = 0; 1469 for (MetaEntry *e = result.start; result.consumed < entry_count; result.consumed++, e++) { 1470 switch (e->kind) { 1471 case MetaEntryKind_BeginScope:{ sub_scope++; }break; 1472 case MetaEntryKind_EndScope:{ sub_scope--; }break; 1473 default:{}break; 1474 } 1475 if (sub_scope == 0) break; 1476 } 1477 1478 if (sub_scope != 0) 1479 meta_entry_error(base, "unclosed scope for entry\n"); 1480 1481 result.one_past_last = base + result.consumed; 1482 if (result.start->kind == MetaEntryKind_BeginScope) result.start++; 1483 if (result.one_past_last == result.start) result.one_past_last++; 1484 1485 return result; 1486 } 1487 1488 function MetaEntryStack 1489 meta_entry_stack_from_file(Arena *arena, char *file) 1490 { 1491 MetaParser parser = {.p.s = read_entire_file(file, arena)}; 1492 MetaEntryStack result = {.raw = parser.p.s}; 1493 1494 compiler_file = file; 1495 1496 meta_parser_trim(&parser); 1497 1498 for (MetaParseToken token = meta_parser_token(&parser); 1499 token != MetaParseToken_EOF; 1500 token = meta_parser_token(&parser)) 1501 { 1502 MetaEntry *e = da_push(arena, &result); 1503 switch (token) { 1504 case MetaParseToken_RawString:{ 1505 e->kind = MetaEntryKind_String; 1506 e->location = parser.save_point.location; 1507 e->name = parser.u.string; 1508 }break; 1509 case MetaParseToken_BeginArray: 1510 case MetaParseToken_BeginScope: 1511 case MetaParseToken_EndScope: 1512 case MetaParseToken_Entry: 1513 { 1514 e->kind = parser.u.kind; 1515 e->location = parser.save_point.location; 1516 1517 if (token == MetaParseToken_Entry) 1518 meta_parser_arguments(&parser, e, arena); 1519 1520 if (token == MetaParseToken_BeginArray) { 1521 MetaEntryArgument *a = e->arguments = push_struct(arena, MetaEntryArgument); 1522 e->argument_count = 1; 1523 meta_parser_fill_argument_array(&parser, a, arena); 1524 } 1525 1526 if (meta_parser_peek_token(&parser) == MetaParseToken_String) { 1527 meta_parser_commit(&parser); 1528 e->name = parser.u.string; 1529 } 1530 }break; 1531 1532 default:{ meta_parser_unexpected_token(&parser, token); }break; 1533 } 1534 } 1535 1536 return result; 1537 } 1538 1539 #define meta_entry_argument_expected(e, ...) \ 1540 meta_entry_argument_expected_((e), arg_list(s8, __VA_ARGS__)) 1541 function void 1542 meta_entry_argument_expected_(MetaEntry *e, s8 *args, uz count) 1543 { 1544 if (e->argument_count != count) { 1545 meta_compiler_error_message(e->location, "incorrect argument count for entry %s() got: %u expected: %u\n", 1546 meta_entry_kind_strings[e->kind], e->argument_count, (u32)count); 1547 fprintf(stderr, " format: @%s(", meta_entry_kind_strings[e->kind]); 1548 for (uz i = 0; i < count; i++) { 1549 if (i != 0) fprintf(stderr, ", "); 1550 fprintf(stderr, "%.*s", (i32)args[i].len, args[i].data); 1551 } 1552 fprintf(stderr, ")\n"); 1553 meta_error(); 1554 } 1555 } 1556 1557 function MetaEntryArgument 1558 meta_entry_argument_expect(MetaEntry *e, u32 index, MetaEntryArgumentKind kind) 1559 { 1560 #define X(k, ...) #k, 1561 read_only local_persist char *kinds[] = {META_ENTRY_ARGUMENT_KIND_LIST}; 1562 #undef X 1563 1564 assert(e->argument_count > index); 1565 MetaEntryArgument result = e->arguments[index]; 1566 1567 if (result.kind != kind) { 1568 meta_entry_error_location(e, result.location, "unexpected argument kind: expected %s but got: %s\n", 1569 kinds[kind], kinds[result.kind]); 1570 } 1571 1572 if (kind == MetaEntryArgumentKind_Array && result.count == 0) 1573 meta_entry_error_location(e, result.location, "array arguments must have at least 1 element\n"); 1574 1575 return result; 1576 } 1577 1578 typedef struct { 1579 s8_list *data; 1580 iz count; 1581 iz capacity; 1582 } s8_list_table; 1583 1584 typedef struct { 1585 s8 *names_upper; 1586 s8 *names_lower; 1587 u32 floating_point; 1588 u32 entry_count; 1589 u32 shader_id; 1590 } MetaShaderBakeParameters; 1591 DA_STRUCT(MetaShaderBakeParameters, MetaShaderBakeParameters); 1592 1593 typedef struct { 1594 iz kind; 1595 iz variation; 1596 } MetaEnumeration; 1597 1598 typedef struct { 1599 u32 *data; 1600 iz count; 1601 iz capacity; 1602 } MetaIDList; 1603 1604 typedef struct { 1605 MetaIDList global_flag_ids; 1606 MetaIDList shader_enumeration_ids; 1607 MetaShaderBakeParameters *bake_parameters; 1608 u32 name_id; 1609 u32 flag_list_id; 1610 i32 base_shader_id; 1611 } MetaShader; 1612 DA_STRUCT(MetaShader, MetaShader); 1613 1614 typedef struct { 1615 MetaShader *shader; 1616 MetaIDList sub_shaders; 1617 s8 file; 1618 } MetaBaseShader; 1619 DA_STRUCT(MetaBaseShader, MetaBaseShader); 1620 1621 typedef struct { 1622 s8 name; 1623 MetaIDList shaders; 1624 } MetaShaderGroup; 1625 DA_STRUCT(MetaShaderGroup, MetaShaderGroup); 1626 1627 typedef struct { 1628 s8 *fields; 1629 s8 **entries; 1630 u32 field_count; 1631 u32 entry_count; 1632 u32 table_name_id; 1633 } MetaTable; 1634 DA_STRUCT(MetaTable, MetaTable); 1635 1636 typedef struct { 1637 s8 enumeration_name; 1638 s8 *sub_table_names; 1639 u32 sub_table_count; 1640 u32 namespace_id; 1641 1642 MetaLocation location; 1643 } MetaMUnion; 1644 DA_STRUCT(MetaMUnion, MetaMUnion); 1645 1646 typedef enum { 1647 MetaExpansionPartKind_Alignment, 1648 MetaExpansionPartKind_Conditional, 1649 MetaExpansionPartKind_EvalKind, 1650 MetaExpansionPartKind_EvalKindCount, 1651 MetaExpansionPartKind_Reference, 1652 MetaExpansionPartKind_String, 1653 } MetaExpansionPartKind; 1654 1655 typedef enum { 1656 MetaExpansionConditionalArgumentKind_Invalid, 1657 MetaExpansionConditionalArgumentKind_Number, 1658 MetaExpansionConditionalArgumentKind_Evaluation, 1659 MetaExpansionConditionalArgumentKind_Reference, 1660 } MetaExpansionConditionalArgumentKind; 1661 1662 typedef struct { 1663 MetaExpansionConditionalArgumentKind kind; 1664 union { 1665 s8 *strings; 1666 i64 number; 1667 }; 1668 } MetaExpansionConditionalArgument; 1669 1670 typedef enum { 1671 MetaExpansionOperation_Invalid, 1672 MetaExpansionOperation_LessThan, 1673 MetaExpansionOperation_GreaterThan, 1674 } MetaExpansionOperation; 1675 1676 typedef struct { 1677 MetaExpansionConditionalArgument lhs; 1678 MetaExpansionConditionalArgument rhs; 1679 MetaExpansionOperation op; 1680 u32 instruction_skip; 1681 } MetaExpansionConditional; 1682 1683 typedef struct { 1684 MetaExpansionPartKind kind; 1685 union { 1686 s8 string; 1687 s8 *strings; 1688 MetaExpansionConditional conditional; 1689 }; 1690 } MetaExpansionPart; 1691 DA_STRUCT(MetaExpansionPart, MetaExpansionPart); 1692 1693 typedef enum { 1694 MetaEmitOperationKind_Expand, 1695 MetaEmitOperationKind_FileBytes, 1696 MetaEmitOperationKind_String, 1697 } MetaEmitOperationKind; 1698 1699 typedef struct { 1700 MetaExpansionPart *parts; 1701 u32 part_count; 1702 u32 table_id; 1703 } MetaEmitOperationExpansion; 1704 1705 typedef struct { 1706 union { 1707 s8 string; 1708 MetaEmitOperationExpansion expansion_operation; 1709 }; 1710 MetaEmitOperationKind kind; 1711 MetaLocation location; 1712 } MetaEmitOperation; 1713 1714 typedef struct { 1715 MetaEmitOperation *data; 1716 iz count; 1717 iz capacity; 1718 1719 s8 filename; 1720 } MetaEmitOperationList; 1721 1722 typedef struct { 1723 MetaEmitOperationList *data; 1724 iz count; 1725 iz capacity; 1726 } MetaEmitOperationListSet; 1727 1728 typedef struct { 1729 Arena *arena, scratch; 1730 1731 s8 filename; 1732 s8 directory; 1733 1734 s8_list enumeration_kinds; 1735 s8_list_table enumeration_members; 1736 1737 s8_list_table flags_for_shader; 1738 1739 s8_list table_names; 1740 MetaTableList tables; 1741 1742 s8_list munion_namespaces; 1743 MetaMUnionList munions; 1744 1745 MetaEmitOperationListSet emit_sets[MetaEmitLang_Count]; 1746 1747 MetaShaderBakeParametersList shader_bake_parameters; 1748 MetaIDList shader_enumerations; 1749 MetaShaderGroupList shader_groups; 1750 MetaShaderList shaders; 1751 MetaBaseShaderList base_shaders; 1752 s8_list shader_names; 1753 } MetaContext; 1754 1755 function iz 1756 meta_lookup_id_slow(MetaIDList *v, u32 id) 1757 { 1758 // TODO(rnp): obviously this is slow 1759 iz result = -1; 1760 for (iz i = 0; i < v->count; i++) { 1761 if (id == v->data[i]) { 1762 result = i; 1763 break; 1764 } 1765 } 1766 return result; 1767 } 1768 1769 function iz 1770 meta_intern_string(MetaContext *ctx, s8_list *sv, s8 s) 1771 { 1772 iz result = meta_lookup_string_slow(sv->data, sv->count, s); 1773 if (result < 0) { 1774 *da_push(ctx->arena, sv) = s; 1775 result = sv->count - 1; 1776 } 1777 return result; 1778 } 1779 1780 function iz 1781 meta_intern_id(MetaContext *ctx, MetaIDList *v, u32 id) 1782 { 1783 iz result = meta_lookup_id_slow(v, id); 1784 if (result < 0) { 1785 *da_push(ctx->arena, v) = id; 1786 result = v->count - 1; 1787 } 1788 return result; 1789 } 1790 1791 function iz 1792 meta_pack_shader_bake_parameters(MetaContext *ctx, MetaEntry *e, iz entry_count, u32 shader_id, u32 *table_id) 1793 { 1794 assert(e->kind == MetaEntryKind_Bake); 1795 1796 MetaShaderBakeParameters *bp = da_push(ctx->arena, &ctx->shader_bake_parameters); 1797 bp->shader_id = shader_id; 1798 if (table_id) *table_id = (u32)da_index(bp, &ctx->shader_bake_parameters); 1799 1800 if (e->argument_count) meta_entry_argument_expected_(e, 0, 0); 1801 1802 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 1803 if (scope.consumed > 1) { 1804 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 1805 if (row->kind != MetaEntryKind_BakeInt && row->kind != MetaEntryKind_BakeFloat) 1806 meta_entry_nesting_error(row, MetaEntryKind_Bake); 1807 meta_entry_argument_expected(row, s8("name"), s8("name_lower")); 1808 bp->entry_count++; 1809 } 1810 1811 if (bp->entry_count > 32) 1812 meta_entry_error(e, "maximum bake parameter count exceeded: limit: 32\n"); 1813 1814 bp->names_upper = push_array(ctx->arena, s8, bp->entry_count); 1815 bp->names_lower = push_array(ctx->arena, s8, bp->entry_count); 1816 1817 u32 row_index = 0; 1818 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++, row_index++) { 1819 bp->names_upper[row_index] = row->arguments[0].string; 1820 bp->names_lower[row_index] = row->arguments[1].string; 1821 bp->floating_point |= (u32)(row->kind == MetaEntryKind_BakeFloat) << row_index; 1822 } 1823 } 1824 1825 return scope.consumed; 1826 } 1827 1828 function iz 1829 meta_enumeration_id(MetaContext *ctx, s8 kind) 1830 { 1831 iz result = meta_intern_string(ctx, &ctx->enumeration_kinds, kind); 1832 if (ctx->enumeration_kinds.count != ctx->enumeration_members.count) { 1833 da_push(ctx->arena, &ctx->enumeration_members); 1834 assert(result == (ctx->enumeration_members.count - 1)); 1835 } 1836 return result; 1837 } 1838 1839 function void 1840 meta_extend_enumeration(MetaContext *ctx, s8 kind, s8 *variations, uz count) 1841 { 1842 iz kidx = meta_enumeration_id(ctx, kind); 1843 /* NOTE(rnp): may overcommit if duplicates exist in variations */ 1844 da_reserve(ctx->arena, ctx->enumeration_members.data + kidx, (iz)count); 1845 for (uz i = 0; i < count; i++) 1846 meta_intern_string(ctx, ctx->enumeration_members.data + kidx, variations[i]); 1847 } 1848 1849 function MetaEnumeration 1850 meta_commit_enumeration(MetaContext *ctx, s8 kind, s8 variation) 1851 { 1852 iz kidx = meta_enumeration_id(ctx, kind); 1853 iz vidx = meta_intern_string(ctx, ctx->enumeration_members.data + kidx, variation); 1854 MetaEnumeration result = {.kind = kidx, .variation = vidx}; 1855 return result; 1856 } 1857 1858 function u16 1859 meta_pack_shader_name(MetaContext *ctx, s8 base_name, MetaLocation loc) 1860 { 1861 iz result = meta_intern_string(ctx, &ctx->shader_names, base_name); 1862 if (result > (iz)U16_MAX) 1863 meta_compiler_error(loc, "maximum base shaders exceeded: limit: %lu\n", U16_MAX); 1864 return (u16)result; 1865 } 1866 1867 function u8 1868 meta_commit_shader_flag(MetaContext *ctx, u32 flag_list_id, s8 flag, MetaEntry *e) 1869 { 1870 assert(flag_list_id < ctx->flags_for_shader.count); 1871 iz index = meta_intern_string(ctx, ctx->flags_for_shader.data + flag_list_id, flag); 1872 if (index > 31) meta_entry_error(e, "maximum shader local flags exceeded: limit: 32\n"); 1873 u8 result = (u8)index; 1874 return result; 1875 } 1876 1877 function iz 1878 meta_pack_shader(MetaContext *ctx, MetaShaderGroup *sg, Arena scratch, MetaEntry *entries, iz entry_count) 1879 { 1880 assert(entries[0].kind == MetaEntryKind_Shader); 1881 1882 MetaShader *s = da_push(ctx->arena, &ctx->shaders); 1883 *da_push(ctx->arena, &sg->shaders) = (u32)da_index(s, &ctx->shaders); 1884 { 1885 s8_list *flag_list = da_push(ctx->arena, &ctx->flags_for_shader); 1886 s->flag_list_id = (u32)da_index(flag_list, &ctx->flags_for_shader); 1887 } 1888 s->name_id = meta_pack_shader_name(ctx, entries->name, entries->location); 1889 s->base_shader_id = -1; 1890 1891 MetaBaseShader *base_shader = 0; 1892 if (entries->argument_count > 1) { 1893 meta_entry_argument_expected(entries, s8("[file_name]")); 1894 } else if (entries->argument_count == 1) { 1895 base_shader = da_push(ctx->arena, &ctx->base_shaders); 1896 base_shader->file = meta_entry_argument_expect(entries, 0, MetaEntryArgumentKind_String).string; 1897 base_shader->shader = s; 1898 s->base_shader_id = (i32)da_index(base_shader, &ctx->base_shaders); 1899 } 1900 1901 i32 stack_items[32]; 1902 struct { i32 *data; iz capacity; iz count; } stack = {stack_items, countof(stack_items), 0}; 1903 1904 iz result; 1905 b32 in_sub_shader = 0; 1906 for (result = 0; result < entry_count; result++) { 1907 MetaEntry *e = entries + result; 1908 switch (e->kind) { 1909 case MetaEntryKind_BeginScope:{}break; 1910 case MetaEntryKind_SubShader:{ 1911 if (in_sub_shader) goto error; 1912 in_sub_shader = 1; 1913 } /* FALLTHROUGH */ 1914 case MetaEntryKind_Shader: 1915 { 1916 *da_push(&scratch, &stack) = (i32)result; 1917 if ((result + 1 < entry_count) && entries[result + 1].kind == MetaEntryKind_BeginScope) 1918 break; 1919 } /* FALLTHROUGH */ 1920 case MetaEntryKind_EndScope:{ 1921 i32 index = stack.data[--stack.count]; 1922 MetaEntry *ended = entries + index; 1923 if (index != 0) { 1924 if (stack.count > 0 && entries[stack.data[stack.count - 1]].kind == MetaEntryKind_Shader) { 1925 if (ended->kind == MetaEntryKind_SubShader) { 1926 if (!base_shader) { 1927 meta_entry_error(ended, "invalid nesting: @%s in @%s\n" 1928 "@%s only allowed in base shaders (shaders with a backing file)\n", 1929 meta_entry_kind_strings[ended->kind], 1930 meta_entry_kind_strings[MetaEntryKind_Shader], 1931 meta_entry_kind_strings[ended->kind]); 1932 } 1933 MetaShader *ss = da_push(ctx->arena, &ctx->shaders); 1934 u32 sid = (u32)da_index(ss, &ctx->shaders); 1935 *da_push(ctx->arena, &sg->shaders) = sid; 1936 *da_push(ctx->arena, &base_shader->sub_shaders) = sid; 1937 1938 ss->flag_list_id = s->flag_list_id; 1939 ss->base_shader_id = s->base_shader_id; 1940 ss->name_id = meta_pack_shader_name(ctx, ended->name, ended->location); 1941 meta_commit_shader_flag(ctx, s->flag_list_id, ended->name, ended); 1942 in_sub_shader = 0; 1943 } 1944 } 1945 } 1946 }break; 1947 case MetaEntryKind_Enumeration:{ 1948 meta_entry_argument_expected(e, s8("kind")); 1949 s8 kind = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 1950 iz kid = meta_enumeration_id(ctx, kind); 1951 meta_intern_id(ctx, &s->shader_enumeration_ids, 1952 (u32)meta_intern_id(ctx, &ctx->shader_enumerations, (u32)kid)); 1953 }break; 1954 case MetaEntryKind_Flags:{ 1955 meta_entry_argument_expected(e, s8("[flag ...]")); 1956 MetaEntryArgument flags = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_Array); 1957 for (u32 index = 0; index < flags.count; index++) 1958 meta_commit_shader_flag(ctx, s->flag_list_id, flags.strings[index], e); 1959 }break; 1960 case MetaEntryKind_Bake:{ 1961 if (s->bake_parameters) { 1962 meta_entry_error(e, "invalid @%s in @%s: only one @%s allowed per @%s\n", 1963 meta_entry_kind_strings[e->kind], meta_entry_kind_strings[MetaEntryKind_Shader], 1964 meta_entry_kind_strings[e->kind], meta_entry_kind_strings[MetaEntryKind_Shader]); 1965 } 1966 u32 table_id; 1967 result += meta_pack_shader_bake_parameters(ctx, e, entry_count - result, (u32)da_index(s, &ctx->shaders), &table_id); 1968 s->bake_parameters = ctx->shader_bake_parameters.data + table_id; 1969 }break; 1970 1971 default: 1972 error: 1973 { 1974 meta_entry_nesting_error(e, MetaEntryKind_Shader); 1975 }break; 1976 } 1977 if (stack.count == 0) 1978 break; 1979 } 1980 1981 return result; 1982 } 1983 1984 function void 1985 meta_expansion_string_split(s8 string, s8 *left, s8 *inner, s8 *remainder, MetaLocation loc) 1986 { 1987 b32 found = 0; 1988 for (u8 *s = string.data, *e = s + string.len; (s + 1) != e; s++) { 1989 u32 val = (u32)'$' << 8u | (u32)'('; 1990 u32 test = (u32)s[0] << 8u | s[1]; 1991 if (test == val) { 1992 if (left) { 1993 left->data = string.data; 1994 left->len = s - string.data; 1995 } 1996 1997 u8 *start = s + 2; 1998 while (s != e && *s != ')') s++; 1999 if (s == e) { 2000 meta_compiler_error_message(loc, "unterminated expansion in raw string:\n %.*s\n", 2001 (i32)string.len, string.data); 2002 fprintf(stderr, " %.*s^\n", (i32)(start - string.data), ""); 2003 meta_error(); 2004 } 2005 2006 if (inner) { 2007 inner->data = start; 2008 inner->len = s - start; 2009 } 2010 2011 if (remainder) { 2012 remainder->data = s + 1; 2013 remainder->len = string.len - (remainder->data - string.data); 2014 } 2015 found = 1; 2016 break; 2017 } 2018 } 2019 if (!found) { 2020 if (left) *left = string; 2021 if (inner) *inner = (s8){0}; 2022 if (remainder) *remainder = (s8){0}; 2023 } 2024 } 2025 2026 function MetaExpansionPart * 2027 meta_push_expansion_part(MetaContext *ctx, Arena *arena, MetaExpansionPartList *parts, 2028 MetaExpansionPartKind kind, s8 string, MetaTable *t, MetaLocation loc) 2029 { 2030 MetaExpansionPart *result = da_push(arena, parts); 2031 result->kind = kind; 2032 switch (kind) { 2033 case MetaExpansionPartKind_Alignment: 2034 case MetaExpansionPartKind_Conditional: 2035 {}break; 2036 case MetaExpansionPartKind_EvalKind: 2037 case MetaExpansionPartKind_EvalKindCount: 2038 case MetaExpansionPartKind_Reference: 2039 { 2040 iz index = meta_lookup_string_slow(t->fields, t->field_count, string); 2041 result->strings = t->entries[index]; 2042 if (index < 0) { 2043 /* TODO(rnp): fix this location to point directly at the field in the string */ 2044 s8 table_name = ctx->table_names.data[t->table_name_id]; 2045 meta_compiler_error(loc, "table \"%.*s\" does not contain member: %.*s\n", 2046 (i32)table_name.len, table_name.data, (i32)string.len, string.data); 2047 } 2048 }break; 2049 case MetaExpansionPartKind_String:{ result->string = string; }break; 2050 InvalidDefaultCase; 2051 } 2052 return result; 2053 } 2054 2055 #define META_EXPANSION_TOKEN_LIST \ 2056 X('|', Alignment) \ 2057 X('%', TypeEval) \ 2058 X('#', TypeEvalElements) \ 2059 X('"', Quote) \ 2060 X('-', Dash) \ 2061 X('>', GreaterThan) \ 2062 X('<', LessThan) \ 2063 2064 typedef enum { 2065 MetaExpansionToken_EOF, 2066 MetaExpansionToken_Identifier, 2067 MetaExpansionToken_Number, 2068 MetaExpansionToken_String, 2069 #define X(__1, kind, ...) MetaExpansionToken_## kind, 2070 META_EXPANSION_TOKEN_LIST 2071 #undef X 2072 MetaExpansionToken_Count, 2073 } MetaExpansionToken; 2074 2075 read_only global s8 meta_expansion_token_strings[] = { 2076 s8_comp("EOF"), 2077 s8_comp("Indentifier"), 2078 s8_comp("Number"), 2079 s8_comp("String"), 2080 #define X(s, kind, ...) s8_comp(#s), 2081 META_EXPANSION_TOKEN_LIST 2082 #undef X 2083 }; 2084 2085 typedef struct { 2086 s8 s; 2087 union { 2088 i64 number; 2089 s8 string; 2090 }; 2091 s8 save; 2092 MetaLocation loc; 2093 } MetaExpansionParser; 2094 2095 #define meta_expansion_save(v) (v)->save = (v)->s 2096 #define meta_expansion_restore(v) swap((v)->s, (v)->save) 2097 #define meta_expansion_commit(v) meta_expansion_restore(v) 2098 2099 #define meta_expansion_expected(loc, e, g) \ 2100 meta_compiler_error(loc, "invalid expansion string: expected %.*s after %.*s\n", \ 2101 (i32)meta_expansion_token_strings[e].len, meta_expansion_token_strings[e].data, \ 2102 (i32)meta_expansion_token_strings[g].len, meta_expansion_token_strings[g].data) 2103 2104 function s8 2105 meta_expansion_extract_string(MetaExpansionParser *p) 2106 { 2107 s8 result = {.data = p->s.data}; 2108 for (; result.len < p->s.len; result.len++) { 2109 b32 done = 0; 2110 switch (p->s.data[result.len]) { 2111 #define X(t, ...) case t: 2112 META_EXPANSION_TOKEN_LIST 2113 #undef X 2114 case ' ': 2115 {done = 1;}break; 2116 default:{}break; 2117 } 2118 if (done) break; 2119 } 2120 p->s.data += result.len; 2121 p->s.len -= result.len; 2122 return result; 2123 } 2124 2125 function MetaExpansionToken 2126 meta_expansion_token(MetaExpansionParser *p) 2127 { 2128 MetaExpansionToken result = MetaExpansionToken_EOF; 2129 meta_expansion_save(p); 2130 if (p->s.len > 0) { 2131 b32 chop = 1; 2132 switch (p->s.data[0]) { 2133 #define X(t, kind, ...) case t:{ result = MetaExpansionToken_## kind; }break; 2134 META_EXPANSION_TOKEN_LIST 2135 #undef X 2136 default:{ 2137 chop = 0; 2138 if (BETWEEN(p->s.data[0], '0', '9')) result = MetaExpansionToken_Number; 2139 else result = MetaExpansionToken_Identifier; 2140 }break; 2141 } 2142 if (chop) { 2143 s8_chop(&p->s, 1); 2144 p->s = s8_trim(p->s); 2145 } 2146 2147 switch (result) { 2148 case MetaExpansionToken_Number:{ 2149 IntegerConversion integer = integer_from_s8(p->s); 2150 if (integer.result != IntegerConversionResult_Success) { 2151 /* TODO(rnp): point at start */ 2152 meta_compiler_error(p->loc, "invalid integer in expansion string\n"); 2153 } 2154 p->number = integer.S64; 2155 p->s = integer.unparsed; 2156 }break; 2157 case MetaExpansionToken_Identifier:{ p->string = meta_expansion_extract_string(p); }break; 2158 default:{}break; 2159 } 2160 p->s = s8_trim(p->s); 2161 } 2162 return result; 2163 } 2164 2165 function MetaExpansionPart * 2166 meta_expansion_start_conditional(MetaContext *ctx, Arena *arena, MetaExpansionPartList *ops, 2167 MetaExpansionParser *p, MetaExpansionToken token, b32 negate) 2168 { 2169 MetaExpansionPart *result = meta_push_expansion_part(ctx, arena, ops, MetaExpansionPartKind_Conditional, 2170 s8(""), 0, p->loc); 2171 switch (token) { 2172 case MetaExpansionToken_Number:{ 2173 result->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Number; 2174 result->conditional.lhs.number = negate ? -p->number : p->number; 2175 }break; 2176 default:{}break; 2177 } 2178 return result; 2179 } 2180 2181 function void 2182 meta_expansion_end_conditional(MetaExpansionPart *ep, MetaExpansionParser *p, MetaExpansionToken token, b32 negate) 2183 { 2184 if (ep->conditional.rhs.kind != MetaExpansionConditionalArgumentKind_Invalid) { 2185 meta_compiler_error(p->loc, "invalid expansion conditional: duplicate right hand expression: '%.*s'\n", 2186 (i32)p->save.len, p->save.data); 2187 } 2188 switch (token) { 2189 case MetaExpansionToken_Number:{ 2190 ep->conditional.rhs.kind = MetaExpansionConditionalArgumentKind_Number; 2191 ep->conditional.rhs.number = negate ? -p->number : p->number; 2192 }break; 2193 default:{}break; 2194 } 2195 } 2196 2197 function MetaExpansionPartList 2198 meta_generate_expansion_set(MetaContext *ctx, Arena *arena, s8 expansion_string, MetaTable *t, MetaLocation loc) 2199 { 2200 MetaExpansionPartList result = {0}; 2201 s8 left = {0}, inner, remainder = expansion_string; 2202 do { 2203 meta_expansion_string_split(remainder, &left, &inner, &remainder, loc); 2204 if (left.len) meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_String, left, t, loc); 2205 if (inner.len) { 2206 MetaExpansionParser p[1] = {{.s = inner, .loc = loc}}; 2207 2208 MetaExpansionPart *test_part = 0; 2209 b32 count_test_parts = 0; 2210 2211 for (MetaExpansionToken token = meta_expansion_token(p); 2212 token != MetaExpansionToken_EOF; 2213 token = meta_expansion_token(p)) 2214 { 2215 if (count_test_parts) test_part->conditional.instruction_skip++; 2216 switch (token) { 2217 case MetaExpansionToken_Alignment:{ 2218 meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Alignment, p->s, t, loc); 2219 }break; 2220 2221 case MetaExpansionToken_Identifier:{ 2222 meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Reference, p->string, t, loc); 2223 }break; 2224 2225 case MetaExpansionToken_TypeEval: 2226 case MetaExpansionToken_TypeEvalElements: 2227 { 2228 if (meta_expansion_token(p) != MetaExpansionToken_Identifier) { 2229 loc.column += (u32)(p->save.data - expansion_string.data); 2230 meta_expansion_expected(loc, MetaExpansionToken_Identifier, token); 2231 } 2232 MetaExpansionPartKind kind = token == MetaExpansionToken_TypeEval ? 2233 MetaExpansionPartKind_EvalKind : 2234 MetaExpansionPartKind_EvalKindCount; 2235 meta_push_expansion_part(ctx, arena, &result, kind, p->string, t, loc); 2236 }break; 2237 2238 case MetaExpansionToken_Quote:{ 2239 u8 *point = p->s.data; 2240 s8 string = meta_expansion_extract_string(p); 2241 token = meta_expansion_token(p); 2242 if (token != MetaExpansionToken_Quote) { 2243 loc.column += (u32)(point - expansion_string.data); 2244 /* TODO(rnp): point at start */ 2245 meta_compiler_error(loc, "unterminated string in expansion\n"); 2246 } 2247 meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_String, string, t, loc); 2248 }break; 2249 2250 case MetaExpansionToken_Dash:{ 2251 token = meta_expansion_token(p); 2252 switch (token) { 2253 case MetaExpansionToken_GreaterThan:{ 2254 if (!test_part) goto error; 2255 if (test_part->conditional.lhs.kind == MetaExpansionConditionalArgumentKind_Invalid || 2256 test_part->conditional.rhs.kind == MetaExpansionConditionalArgumentKind_Invalid) 2257 { 2258 b32 lhs = test_part->conditional.lhs.kind == MetaExpansionConditionalArgumentKind_Invalid; 2259 b32 rhs = test_part->conditional.rhs.kind == MetaExpansionConditionalArgumentKind_Invalid; 2260 if (lhs && rhs) 2261 meta_compiler_error(loc, "expansion string test terminated without arguments\n"); 2262 meta_compiler_error(loc, "expansion string test terminated without %s argument\n", 2263 lhs? "left" : "right"); 2264 } 2265 count_test_parts = 1; 2266 }break; 2267 case MetaExpansionToken_Number:{ 2268 if (test_part) meta_expansion_end_conditional(test_part, p, token, 1); 2269 else test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 1); 2270 }break; 2271 default:{ goto error; }break; 2272 } 2273 }break; 2274 2275 case MetaExpansionToken_Number:{ 2276 if (test_part) meta_expansion_end_conditional(test_part, p, token, 0); 2277 else test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 0); 2278 }break; 2279 2280 case MetaExpansionToken_GreaterThan: 2281 case MetaExpansionToken_LessThan: 2282 { 2283 if (test_part && test_part->conditional.op != MetaExpansionOperation_Invalid) goto error; 2284 if (!test_part) { 2285 if (result.count == 0) { 2286 meta_compiler_error(p->loc, "invalid expansion conditional: missing left hand side\n"); 2287 } 2288 2289 s8 *strings = result.data[result.count - 1].strings; 2290 MetaExpansionPartKind last_kind = result.data[result.count - 1].kind; 2291 if (last_kind != MetaExpansionPartKind_EvalKindCount && 2292 last_kind != MetaExpansionPartKind_Reference) 2293 { 2294 meta_compiler_error(p->loc, "invalid expansion conditional: left hand side not numeric\n"); 2295 } 2296 result.count--; 2297 test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 0); 2298 if (last_kind == MetaExpansionPartKind_EvalKindCount) { 2299 test_part->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Evaluation; 2300 } else { 2301 test_part->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Reference; 2302 } 2303 test_part->conditional.lhs.strings = strings; 2304 } 2305 test_part->conditional.op = token == MetaExpansionToken_LessThan ? 2306 MetaExpansionOperation_LessThan : 2307 MetaExpansionOperation_GreaterThan; 2308 }break; 2309 2310 error: 2311 default: 2312 { 2313 meta_compiler_error(loc, "invalid nested %.*s in expansion string\n", 2314 (i32)meta_expansion_token_strings[token].len, 2315 meta_expansion_token_strings[token].data); 2316 }break; 2317 } 2318 } 2319 } 2320 } while (remainder.len); 2321 return result; 2322 } 2323 2324 function iz 2325 meta_expand(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count, MetaEmitOperationList *ops) 2326 { 2327 assert(e->kind == MetaEntryKind_Expand); 2328 2329 /* TODO(rnp): for now this requires that the @Table came first */ 2330 meta_entry_argument_expected(e, s8("table_name")); 2331 s8 table_name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2332 2333 MetaTable *t = ctx->tables.data + meta_lookup_string_slow(ctx->table_names.data, 2334 ctx->table_names.count, table_name); 2335 if (t < ctx->tables.data) 2336 meta_entry_error(e, "undefined table %.*s\n", (i32)table_name.len, table_name.data); 2337 2338 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 2339 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 2340 switch (row->kind) { 2341 case MetaEntryKind_String:{ 2342 if (!ops) goto error; 2343 2344 MetaExpansionPartList parts = meta_generate_expansion_set(ctx, ctx->arena, row->name, t, row->location); 2345 2346 MetaEmitOperation *op = da_push(ctx->arena, ops); 2347 op->kind = MetaEmitOperationKind_Expand; 2348 op->location = row->location; 2349 op->expansion_operation.parts = parts.data; 2350 op->expansion_operation.part_count = (u32)parts.count; 2351 op->expansion_operation.table_id = (u32)da_index(t, &ctx->tables); 2352 }break; 2353 case MetaEntryKind_Enumeration:{ 2354 if (ops) meta_entry_nesting_error(row, MetaEntryKind_Emit); 2355 2356 meta_entry_argument_expected(row, s8("kind"), s8("`raw_string`")); 2357 s8 kind = meta_entry_argument_expect(row, 0, MetaEntryArgumentKind_String).string; 2358 s8 expand = meta_entry_argument_expect(row, 1, MetaEntryArgumentKind_String).string; 2359 2360 MetaExpansionPartList parts = meta_generate_expansion_set(ctx, &scratch, expand, t, row->location); 2361 s8 *variations = push_array(&scratch, s8, t->entry_count); 2362 for (u32 expansion = 0; expansion < t->entry_count; expansion++) { 2363 Stream sb = arena_stream(*ctx->arena); 2364 for (iz part = 0; part < parts.count; part++) { 2365 MetaExpansionPart *p = parts.data + part; 2366 u32 index = 0; 2367 if (p->kind == MetaExpansionPartKind_Reference) index = expansion; 2368 stream_append_s8(&sb, p->strings[index]); 2369 } 2370 variations[expansion] = arena_stream_commit(ctx->arena, &sb); 2371 } 2372 meta_extend_enumeration(ctx, kind, variations, t->entry_count); 2373 }break; 2374 error: 2375 default: 2376 { 2377 meta_entry_nesting_error(row, MetaEntryKind_Expand); 2378 }break; 2379 } 2380 } 2381 return scope.consumed; 2382 } 2383 2384 function void 2385 meta_embed(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count) 2386 { 2387 assert(e->kind == MetaEntryKind_Embed); 2388 2389 meta_entry_argument_expected(e, s8("filename")); 2390 s8 filename = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2391 2392 MetaEmitOperationList *ops = da_push(ctx->arena, ctx->emit_sets + MetaEmitLang_C); 2393 if (e->name.len == 0) meta_entry_error(e, "name must be provided for output array"); 2394 2395 MetaEmitOperation *op; 2396 op = da_push(ctx->arena, ops); 2397 op->kind = MetaEmitOperationKind_String; 2398 op->string = push_s8_from_parts(ctx->arena, s8(""), s8("read_only global u8 "), e->name, s8("[] = {")); 2399 2400 op = da_push(ctx->arena, ops); 2401 op->kind = MetaEmitOperationKind_FileBytes; 2402 op->string = filename; 2403 2404 op = da_push(ctx->arena, ops); 2405 op->kind = MetaEmitOperationKind_String; 2406 op->string = s8("};"); 2407 } 2408 2409 function MetaKind 2410 meta_map_kind(s8 kind, s8 table_name, MetaLocation location) 2411 { 2412 iz id = meta_lookup_string_slow(meta_kind_meta_types, MetaKind_Count, kind); 2413 if (id < 0) { 2414 meta_compiler_error(location, "Invalid Kind in '%.*s' table expansion: %.*s\n", 2415 (i32)table_name.len, table_name.data, (i32)kind.len, kind.data); 2416 } 2417 MetaKind result = (MetaKind)id; 2418 return result; 2419 } 2420 2421 function MetaEmitLang 2422 meta_map_emit_lang(s8 lang, MetaEntry *e) 2423 { 2424 #define X(k, ...) s8_comp(#k), 2425 read_only local_persist s8 meta_lang_strings[] = {META_EMIT_LANG_LIST}; 2426 #undef X 2427 2428 iz id = meta_lookup_string_slow(meta_lang_strings, MetaEmitLang_Count, lang); 2429 if (id < 0) { 2430 #define X(k, ...) #k ", " 2431 meta_entry_error(e, "Unknown Emit Language: '%.*s'\nPossible Values: " 2432 META_EMIT_LANG_LIST "\n", (i32)lang.len, lang.data); 2433 #undef X 2434 } 2435 MetaEmitLang result = (MetaEmitLang)id; 2436 return result; 2437 } 2438 2439 function iz 2440 meta_pack_emit(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count) 2441 { 2442 assert(e->kind == MetaEntryKind_Emit); 2443 2444 MetaEmitLang lang = MetaEmitLang_C; 2445 if (e->argument_count) { 2446 meta_entry_argument_expected(e, s8("emit_language")); 2447 s8 name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2448 lang = meta_map_emit_lang(name, e); 2449 } 2450 2451 MetaEmitOperationList *ops = da_push(ctx->arena, ctx->emit_sets + lang); 2452 /* TODO(rnp): probably we should check this is unique */ 2453 ops->filename = e->name; 2454 2455 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 2456 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 2457 switch (row->kind) { 2458 case MetaEntryKind_String:{ 2459 MetaEmitOperation *op = da_push(ctx->arena, ops); 2460 op->kind = MetaEmitOperationKind_String; 2461 op->string = row->name; 2462 op->location = row->location; 2463 }break; 2464 case MetaEntryKind_Expand:{ 2465 row += meta_expand(ctx, scratch, row, entry_count - (row - e), ops); 2466 }break; 2467 default:{ meta_entry_nesting_error(row, MetaEntryKind_Emit); }break; 2468 } 2469 } 2470 return scope.consumed; 2471 } 2472 2473 function void 2474 meta_pack_munion(MetaContext *ctx, MetaEntry *e, iz entry_count) 2475 { 2476 assert(e->kind == MetaEntryKind_MUnion); 2477 2478 MetaMUnion *mu = da_push(ctx->arena, &ctx->munions); 2479 mu->location = e->location; 2480 2481 iz namespace_id = meta_lookup_string_slow(ctx->munion_namespaces.data, 2482 ctx->munion_namespaces.count, e->name); 2483 if (namespace_id >= 0) meta_entry_error(e, "MUnion redefined\n"); 2484 2485 s8 *m_name = da_push(ctx->arena, &ctx->munion_namespaces); 2486 mu->namespace_id = (u32)da_index(m_name, &ctx->munion_namespaces); 2487 *m_name = e->name; 2488 2489 meta_entry_argument_expected(e, s8("enumeration_name"), s8("[parameter_table_name ...]")); 2490 2491 MetaEntryArgument sub_tables = meta_entry_argument_expect(e, 1, MetaEntryArgumentKind_Array); 2492 mu->enumeration_name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2493 mu->sub_table_names = sub_tables.strings; 2494 mu->sub_table_count = (u32)sub_tables.count; 2495 } 2496 2497 function iz 2498 meta_pack_table(MetaContext *ctx, MetaEntry *e, iz entry_count) 2499 { 2500 assert(e->kind == MetaEntryKind_Table); 2501 2502 MetaTable *t = da_push(ctx->arena, &ctx->tables); 2503 iz table_name_id = meta_lookup_string_slow(ctx->table_names.data, ctx->table_names.count, e->name); 2504 if (table_name_id >= 0) meta_entry_error(e, "table redefined\n"); 2505 2506 s8 *t_name = da_push(ctx->arena, &ctx->table_names); 2507 t->table_name_id = (u32)da_index(t_name, &ctx->table_names); 2508 *t_name = e->name; 2509 2510 meta_entry_argument_expected(e, s8("[field ...]")); 2511 MetaEntryArgument fields = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_Array); 2512 t->fields = fields.strings; 2513 t->field_count = (u32)fields.count; 2514 2515 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 2516 if (scope.consumed > 1) { 2517 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 2518 if (row->kind != MetaEntryKind_Array) 2519 meta_entry_nesting_error(row, MetaEntryKind_Table); 2520 2521 MetaEntryArgument entries = meta_entry_argument_expect(row, 0, MetaEntryArgumentKind_Array); 2522 if (entries.count != t->field_count) { 2523 meta_compiler_error_message(row->location, "incorrect field count for @%s entry got: %zu expected: %u\n", 2524 meta_entry_kind_strings[MetaEntryKind_Table], 2525 entries.count, t->field_count); 2526 fprintf(stderr, " fields: ["); 2527 for (uz i = 0; i < t->field_count; i++) { 2528 if (i != 0) fprintf(stderr, ", "); 2529 fprintf(stderr, "%.*s", (i32)t->fields[i].len, t->fields[i].data); 2530 } 2531 fprintf(stderr, "]\n"); 2532 meta_error(); 2533 } 2534 t->entry_count++; 2535 } 2536 2537 t->entries = push_array(ctx->arena, s8 *, t->field_count); 2538 for (u32 field = 0; field < t->field_count; field++) 2539 t->entries[field] = push_array(ctx->arena, s8, t->entry_count); 2540 2541 u32 row_index = 0; 2542 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++, row_index++) { 2543 s8 *fs = row->arguments->strings; 2544 for (u32 field = 0; field < t->field_count; field++) 2545 t->entries[field][row_index] = fs[field]; 2546 } 2547 } 2548 2549 return scope.consumed; 2550 } 2551 2552 function CommandList 2553 meta_extract_emit_file_dependencies(MetaContext *ctx, Arena *arena) 2554 { 2555 CommandList result = {0}; 2556 for (iz set = 0; set < ctx->emit_sets[MetaEmitLang_C].count; set++) { 2557 MetaEmitOperationList *ops = ctx->emit_sets[MetaEmitLang_C].data + set; 2558 for (iz opcode = 0; opcode < ops->count; opcode++) { 2559 MetaEmitOperation *op = ops->data + opcode; 2560 switch (op->kind) { 2561 case MetaEmitOperationKind_FileBytes:{ 2562 s8 filename = push_s8_from_parts(arena, s8(OS_PATH_SEPARATOR), ctx->directory, op->string); 2563 *da_push(arena, &result) = (c8 *)filename.data; 2564 }break; 2565 default:{}break; 2566 } 2567 } 2568 } 2569 return result; 2570 } 2571 2572 function void 2573 metagen_push_byte_array(MetaprogramContext *m, s8 bytes) 2574 { 2575 for (iz i = 0; i < bytes.len; i++) { 2576 b32 end_line = (i != 0) && (i % 16) == 0; 2577 if (i != 0) meta_push(m, end_line ? s8(",") : s8(", ")); 2578 if (end_line) meta_end_line(m); 2579 if ((i % 16) == 0) meta_indent(m); 2580 meta_push(m, s8("0x")); 2581 meta_push_u64_hex(m, bytes.data[i]); 2582 } 2583 meta_end_line(m); 2584 } 2585 2586 function void 2587 metagen_push_table(MetaprogramContext *m, Arena scratch, s8 row_start, s8 row_end, 2588 s8 **column_strings, uz rows, uz columns) 2589 { 2590 u32 *column_widths = 0; 2591 if (columns > 1) { 2592 column_widths = push_array(&scratch, u32, (iz)columns - 1); 2593 for (uz column = 0; column < columns - 1; column++) { 2594 s8 *strings = column_strings[column]; 2595 for (uz row = 0; row < rows; row++) 2596 column_widths[column] = MAX(column_widths[column], (u32)strings[row].len); 2597 } 2598 } 2599 2600 for (uz row = 0; row < rows; row++) { 2601 meta_begin_line(m, row_start); 2602 for (uz column = 0; column < columns; column++) { 2603 s8 text = column_strings[column][row]; 2604 meta_push(m, text); 2605 i32 pad = columns > 1 ? 1 : 0; 2606 if (column_widths && column < columns - 1) 2607 pad += (i32)column_widths[column] - (i32)text.len; 2608 if (column < columns - 1) meta_pad(m, ' ', pad); 2609 } 2610 meta_end_line(m, row_end); 2611 } 2612 } 2613 2614 function i64 2615 meta_expansion_part_conditional_argument(MetaExpansionConditionalArgument a, u32 entry, 2616 s8 table_name, MetaLocation loc) 2617 { 2618 i64 result = 0; 2619 switch (a.kind) { 2620 case MetaExpansionConditionalArgumentKind_Number:{ 2621 result = a.number; 2622 }break; 2623 2624 case MetaExpansionConditionalArgumentKind_Evaluation: 2625 { 2626 s8 string = a.strings[entry]; 2627 MetaKind kind = meta_map_kind(string, table_name, loc); 2628 result = meta_kind_elements[kind]; 2629 }break; 2630 2631 case MetaExpansionConditionalArgumentKind_Reference:{ 2632 s8 string = a.strings[entry]; 2633 IntegerConversion integer = integer_from_s8(string); 2634 if (integer.result != IntegerConversionResult_Success) { 2635 meta_compiler_error(loc, "Invalid integer in '%.*s' table expansion: %.*s\n", 2636 (i32)table_name.len, table_name.data, (i32)string.len, string.data); 2637 } 2638 result = integer.S64; 2639 }break; 2640 2641 InvalidDefaultCase; 2642 } 2643 2644 return result; 2645 } 2646 2647 function b32 2648 meta_expansion_part_conditional(MetaExpansionPart *p, u32 entry, s8 table_name, MetaLocation loc) 2649 { 2650 assert(p->kind == MetaExpansionPartKind_Conditional); 2651 b32 result = 0; 2652 i64 lhs = meta_expansion_part_conditional_argument(p->conditional.lhs, entry, table_name, loc); 2653 i64 rhs = meta_expansion_part_conditional_argument(p->conditional.rhs, entry, table_name, loc); 2654 switch (p->conditional.op) { 2655 case MetaExpansionOperation_LessThan:{ result = lhs < rhs; }break; 2656 case MetaExpansionOperation_GreaterThan:{ result = lhs > rhs; }break; 2657 InvalidDefaultCase; 2658 } 2659 return result; 2660 } 2661 2662 function void 2663 metagen_run_emit(MetaprogramContext *m, MetaContext *ctx, MetaEmitOperationList *ops, 2664 s8 *evaluation_table) 2665 { 2666 for (iz opcode = 0; opcode < ops->count; opcode++) { 2667 MetaEmitOperation *op = ops->data + opcode; 2668 switch (op->kind) { 2669 case MetaEmitOperationKind_String:{ meta_push_line(m, op->string); }break; 2670 case MetaEmitOperationKind_FileBytes:{ 2671 Arena scratch = m->scratch; 2672 s8 filename = push_s8_from_parts(&scratch, s8(OS_PATH_SEPARATOR), ctx->directory, op->string); 2673 s8 file = read_entire_file((c8 *)filename.data, &scratch); 2674 m->indentation_level++; 2675 metagen_push_byte_array(m, file); 2676 m->indentation_level--; 2677 }break; 2678 case MetaEmitOperationKind_Expand:{ 2679 Arena scratch = m->scratch; 2680 2681 MetaEmitOperationExpansion *eop = &op->expansion_operation; 2682 MetaTable *t = ctx->tables.data + eop->table_id; 2683 s8 table_name = ctx->table_names.data[t->table_name_id]; 2684 2685 u32 alignment_count = 1; 2686 u32 evaluation_count = 0; 2687 for (u32 part = 0; part < eop->part_count; part++) { 2688 if (eop->parts[part].kind == MetaExpansionPartKind_Alignment) 2689 alignment_count++; 2690 if (eop->parts[part].kind == MetaExpansionPartKind_EvalKind || 2691 eop->parts[part].kind == MetaExpansionPartKind_EvalKindCount) 2692 evaluation_count++; 2693 } 2694 2695 MetaKind **evaluation_columns = push_array(&scratch, MetaKind *, evaluation_count); 2696 for (u32 column = 0; column < evaluation_count; column++) 2697 evaluation_columns[column] = push_array(&scratch, MetaKind, t->entry_count); 2698 2699 for (u32 part = 0; part < eop->part_count; part++) { 2700 u32 eval_column = 0; 2701 MetaExpansionPart *p = eop->parts + part; 2702 if (p->kind == MetaExpansionPartKind_EvalKind) { 2703 for (u32 entry = 0; entry < t->entry_count; entry++) { 2704 evaluation_columns[eval_column][entry] = meta_map_kind(p->strings[entry], 2705 table_name, op->location); 2706 } 2707 eval_column++; 2708 } 2709 } 2710 2711 s8 **columns = push_array(&scratch, s8 *, alignment_count); 2712 for (u32 column = 0; column < alignment_count; column++) 2713 columns[column] = push_array(&scratch, s8, t->entry_count); 2714 2715 Stream sb = arena_stream(scratch); 2716 for (u32 entry = 0; entry < t->entry_count; entry++) { 2717 u32 column = 0; 2718 u32 eval_column = 0; 2719 for (u32 part = 0; part < eop->part_count; part++) { 2720 MetaExpansionPart *p = eop->parts + part; 2721 switch (p->kind) { 2722 case MetaExpansionPartKind_Alignment:{ 2723 columns[column][entry] = arena_stream_commit_and_reset(&scratch, &sb); 2724 column++; 2725 }break; 2726 2727 case MetaExpansionPartKind_Conditional:{ 2728 if (!meta_expansion_part_conditional(p, entry, table_name, op->location)) 2729 part += p->conditional.instruction_skip; 2730 }break; 2731 2732 case MetaExpansionPartKind_EvalKind:{ 2733 s8 kind = evaluation_table[evaluation_columns[eval_column][entry]]; 2734 stream_append_s8(&sb, kind); 2735 }break; 2736 2737 case MetaExpansionPartKind_EvalKindCount:{ 2738 stream_append_u64(&sb, meta_kind_elements[evaluation_columns[eval_column][entry]]); 2739 }break; 2740 2741 case MetaExpansionPartKind_Reference: 2742 case MetaExpansionPartKind_String: 2743 { 2744 s8 string = p->kind == MetaExpansionPartKind_Reference ? p->strings[entry] : p->string; 2745 stream_append_s8(&sb, string); 2746 }break; 2747 } 2748 } 2749 2750 columns[column][entry] = arena_stream_commit_and_reset(&scratch, &sb); 2751 } 2752 metagen_push_table(m, scratch, s8(""), s8(""), columns, t->entry_count, alignment_count); 2753 }break; 2754 InvalidDefaultCase; 2755 } 2756 } 2757 meta_end_line(m); 2758 } 2759 2760 function void 2761 metagen_run_emit_set(MetaprogramContext *m, MetaContext *ctx, MetaEmitOperationListSet *emit_set, 2762 s8 *evaluation_table) 2763 { 2764 for (iz set = 0; set < emit_set->count; set++) { 2765 MetaEmitOperationList *ops = emit_set->data + set; 2766 metagen_run_emit(m, ctx, ops, evaluation_table); 2767 } 2768 } 2769 2770 function void 2771 metagen_push_counted_enum_body(MetaprogramContext *m, s8 kind, s8 prefix, s8 mid, s8 suffix, s8 *ids, iz ids_count) 2772 { 2773 iz max_id_length = 0; 2774 for (iz id = 0; id < ids_count; id++) 2775 max_id_length = MAX(max_id_length, ids[id].len); 2776 2777 for (iz id = 0; id < ids_count; id++) { 2778 meta_begin_line(m, prefix, kind, ids[id]); 2779 meta_pad(m, ' ', 1 + (i32)(max_id_length - ids[id].len)); 2780 meta_push(m, mid); 2781 meta_push_u64(m, (u64)id); 2782 meta_end_line(m, suffix); 2783 } 2784 } 2785 2786 function void 2787 metagen_push_c_enum(MetaprogramContext *m, Arena scratch, s8 kind, s8 *ids, iz ids_count) 2788 { 2789 s8 kind_full = push_s8_from_parts(&scratch, s8(""), kind, s8("_")); 2790 meta_begin_scope(m, s8("typedef enum {")); 2791 metagen_push_counted_enum_body(m, kind_full, s8(""), s8("= "), s8(","), ids, ids_count); 2792 meta_push_line(m, kind_full, s8("Count,")); 2793 meta_end_scope(m, s8("} "), kind, s8(";\n")); 2794 } 2795 2796 function void 2797 metagen_push_c_flag_enum(MetaprogramContext *m, Arena scratch, s8 kind, s8 *ids, iz ids_count) 2798 { 2799 s8 kind_full = push_s8_from_parts(&scratch, s8(""), kind, s8("_")); 2800 meta_begin_scope(m, s8("typedef enum {")); 2801 metagen_push_counted_enum_body(m, kind_full, s8(""), s8("= (1 << "), s8("),"), ids, ids_count); 2802 meta_end_scope(m, s8("} "), kind, s8(";\n")); 2803 } 2804 2805 function void 2806 meta_push_shader_reload_info(MetaprogramContext *m, MetaContext *ctx) 2807 { 2808 /////////////////////////////// 2809 // NOTE(rnp): reloadable infos 2810 meta_begin_scope(m, s8("read_only global BeamformerShaderKind beamformer_reloadable_shader_kinds[] = {")); 2811 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2812 MetaShader *s = ctx->base_shaders.data[shader].shader; 2813 meta_push_line(m, s8("BeamformerShaderKind_"), ctx->shader_names.data[s->name_id], s8(",")); 2814 } 2815 meta_end_scope(m, s8("};\n")); 2816 2817 meta_begin_scope(m, s8("read_only global s8 beamformer_reloadable_shader_files[] = {")); 2818 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) 2819 meta_push_line(m, s8("s8_comp(\""), ctx->base_shaders.data[shader].file, s8("\"),")); 2820 meta_end_scope(m, s8("};\n")); 2821 2822 { 2823 meta_begin_scope(m, s8("read_only global i32 beamformer_shader_reloadable_index_by_shader[] = {")); 2824 for (iz shader = 0; shader < ctx->shaders.count; shader++) { 2825 meta_indent(m); 2826 meta_push_i64(m, ctx->shaders.data[shader].base_shader_id); 2827 meta_end_line(m, s8(",")); 2828 } 2829 meta_end_scope(m, s8("};\n")); 2830 } 2831 2832 { 2833 u32 info_index = 0; 2834 for (iz group = 0; group < ctx->shader_groups.count; group++) { 2835 MetaShaderGroup *sg = ctx->shader_groups.data + group; 2836 meta_begin_line(m, s8("read_only global i32 beamformer_reloadable_")); 2837 for (iz i = 0; i < sg->name.len; i++) 2838 stream_append_byte(&m->stream, TOLOWER(sg->name.data[i])); 2839 meta_begin_scope(m, s8("_shader_info_indices[] = {")); 2840 2841 for (iz shader = 0; shader < sg->shaders.count; shader++) { 2842 MetaShader *s = ctx->shaders.data + sg->shaders.data[shader]; 2843 /* TODO(rnp): store base shader list in a better format */ 2844 for (iz base_shader = 0; base_shader < ctx->base_shaders.count; base_shader++) { 2845 MetaBaseShader *bs = ctx->base_shaders.data + base_shader; 2846 if (bs->shader == s) { 2847 meta_indent(m); 2848 meta_push_u64(m, info_index++); 2849 meta_end_line(m, s8(",")); 2850 break; 2851 } 2852 } 2853 } 2854 meta_end_scope(m, s8("};\n")); 2855 } 2856 } 2857 2858 //////////////////////////////////// 2859 // NOTE(rnp): shader header strings 2860 meta_begin_scope(m, s8("read_only global s8 beamformer_shader_global_header_strings[] = {")); 2861 for (iz ref = 0; ref < ctx->shader_enumerations.count; ref++) { 2862 u32 kind = ctx->shader_enumerations.data[ref]; 2863 s8_list *sub_list = ctx->enumeration_members.data + kind; 2864 s8 kind_name = push_s8_from_parts(&m->scratch, s8(""), ctx->enumeration_kinds.data[kind], s8("_")); 2865 meta_push_line(m, s8("s8_comp(\"\"")); 2866 metagen_push_counted_enum_body(m, kind_name, s8("\"#define "), s8(""), s8("\\n\""), 2867 sub_list->data, sub_list->count); 2868 meta_push_line(m, s8("\"\\n\"),")); 2869 m->scratch = ctx->scratch; 2870 } 2871 meta_end_scope(m, s8("};\n")); 2872 2873 meta_begin_scope(m, s8("read_only global s8 *beamformer_shader_flag_strings[] = {")); 2874 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2875 MetaShader *s = ctx->base_shaders.data[shader].shader; 2876 s8_list *flag_list = ctx->flags_for_shader.data + s->flag_list_id; 2877 2878 if (flag_list->count) { 2879 meta_begin_scope(m, s8("(s8 []){")); 2880 for (iz flag = 0; flag < flag_list->count; flag++) 2881 meta_push_line(m, s8("s8_comp(\""), flag_list->data[flag], s8("\"),")); 2882 meta_end_scope(m, s8("},")); 2883 } else { 2884 meta_push_line(m, s8("0,")); 2885 } 2886 } 2887 meta_end_scope(m, s8("};\n")); 2888 2889 meta_begin_scope(m, s8("read_only global u8 beamformer_shader_flag_strings_count[] = {")); 2890 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2891 MetaShader *s = ctx->base_shaders.data[shader].shader; 2892 s8_list *flag_list = ctx->flags_for_shader.data + s->flag_list_id; 2893 2894 meta_indent(m); 2895 meta_push_u64(m, (u64)flag_list->count); 2896 meta_end_line(m, s8(",")); 2897 } 2898 meta_end_scope(m, s8("};\n")); 2899 } 2900 2901 function void 2902 meta_push_shader_bake(MetaprogramContext *m, MetaContext *ctx) 2903 { 2904 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2905 MetaShader *s = ctx->base_shaders.data[shader].shader; 2906 s8 shader_name = ctx->shader_names.data[s->name_id]; 2907 meta_begin_line(m, s8("read_only global u8 beamformer_shader_")); 2908 for (iz i = 0; i < shader_name.len; i++) 2909 stream_append_byte(&m->stream, TOLOWER(shader_name.data[i])); 2910 meta_begin_scope(m, s8("_bytes[] = {")); { 2911 Arena scratch = m->scratch; 2912 s8 filename = push_s8_from_parts(&scratch, s8(OS_PATH_SEPARATOR), s8("shaders"), 2913 ctx->base_shaders.data[shader].file); 2914 s8 file = read_entire_file((c8 *)filename.data, &scratch); 2915 metagen_push_byte_array(m, file); 2916 } meta_end_scope(m, s8("};\n")); 2917 } 2918 2919 meta_begin_scope(m, s8("read_only global s8 beamformer_shader_data[] = {")); { 2920 Arena scratch = m->scratch; 2921 s8 *columns[2]; 2922 columns[0] = push_array(&m->scratch, s8, ctx->base_shaders.count); 2923 columns[1] = push_array(&m->scratch, s8, ctx->base_shaders.count); 2924 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2925 MetaShader *s = ctx->base_shaders.data[shader].shader; 2926 s8 shader_name = ctx->shader_names.data[s->name_id]; 2927 2928 Stream sb = arena_stream(m->scratch); 2929 for (iz i = 0; i < shader_name.len; i++) 2930 stream_append_byte(&sb, TOLOWER(shader_name.data[i])); 2931 stream_append_s8(&sb, s8("_bytes,")); 2932 columns[0][shader] = arena_stream_commit_and_reset(&m->scratch, &sb); 2933 2934 stream_append_s8(&sb, s8(".len = countof(beamformer_shader_")); 2935 for (iz i = 0; i < shader_name.len; i++) 2936 stream_append_byte(&sb, TOLOWER(shader_name.data[i])); 2937 columns[1][shader] = arena_stream_commit(&m->scratch, &sb); 2938 } 2939 metagen_push_table(m, m->scratch, s8("{.data = beamformer_shader_"), s8("_bytes)},"), columns, 2940 (uz)ctx->base_shaders.count, 2); 2941 m->scratch = scratch; 2942 } meta_end_scope(m, s8("};\n")); 2943 } 2944 2945 function b32 2946 metagen_emit_c_code(MetaContext *ctx, Arena arena) 2947 { 2948 os_make_directory("generated"); 2949 char *out_meta = "generated" OS_PATH_SEPARATOR "beamformer.meta.c"; 2950 char *out_shaders = "generated" OS_PATH_SEPARATOR "beamformer_shaders.c"; 2951 2952 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 2953 2954 if (setjmp(compiler_jmp_buf)) { 2955 build_fatal("Failed to generate C Code"); 2956 } 2957 2958 b32 result = 1; 2959 2960 //////////////////////////// 2961 // NOTE(rnp): shader baking 2962 { 2963 char **deps = push_array(&m->scratch, char *, ctx->base_shaders.count); 2964 for (iz i = 0; i < ctx->base_shaders.count; i++) { 2965 MetaBaseShader *b = ctx->base_shaders.data + i; 2966 deps[i] = (c8 *)push_s8_from_parts(&m->scratch, s8(OS_PATH_SEPARATOR), s8("shaders"), b->file).data; 2967 } 2968 if (needs_rebuild_(out_shaders, deps, ctx->base_shaders.count)) { 2969 build_log_generate("Bake Shaders"); 2970 meta_push(m, c_file_header); 2971 meta_push_shader_bake(m, ctx); 2972 result &= meta_write_and_reset(m, out_shaders); 2973 } 2974 m->scratch = ctx->scratch; 2975 } 2976 2977 if (!needs_rebuild(out_meta, "beamformer.meta")) 2978 return result; 2979 2980 build_log_generate("Core C Code"); 2981 2982 meta_push(m, c_file_header); 2983 2984 ///////////////////////// 2985 // NOTE(rnp): enumarents 2986 for (iz kind = 0; kind < ctx->enumeration_kinds.count; kind++) { 2987 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8("Beamformer"), ctx->enumeration_kinds.data[kind]); 2988 metagen_push_c_enum(m, m->scratch, enum_name, ctx->enumeration_members.data[kind].data, 2989 ctx->enumeration_members.data[kind].count); 2990 m->scratch = ctx->scratch; 2991 } 2992 2993 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2994 MetaShader *s = ctx->base_shaders.data[shader].shader; 2995 s8_list flag_list = ctx->flags_for_shader.data[s->flag_list_id]; 2996 if (flag_list.count) { 2997 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8("BeamformerShader"), 2998 ctx->shader_names.data[s->name_id], s8("Flags")); 2999 metagen_push_c_flag_enum(m, m->scratch, enum_name, flag_list.data, flag_list.count); 3000 m->scratch = ctx->scratch; 3001 } 3002 } 3003 3004 { 3005 s8 kind = s8("BeamformerShaderKind"); 3006 s8 kind_full = s8("BeamformerShaderKind_"); 3007 meta_begin_scope(m, s8("typedef enum {")); 3008 metagen_push_counted_enum_body(m, kind_full, s8(""), s8("= "), s8(","), 3009 ctx->shader_names.data, ctx->shader_names.count); 3010 meta_push_line(m, kind_full, s8("Count,\n")); 3011 3012 s8 *columns[2]; 3013 columns[0] = push_array(&m->scratch, s8, ctx->shader_groups.count * 3); 3014 columns[1] = push_array(&m->scratch, s8, ctx->shader_groups.count * 3); 3015 3016 for (iz group = 0; group < ctx->shader_groups.count; group++) { 3017 MetaShaderGroup *sg = ctx->shader_groups.data + group; 3018 3019 s8 first_name = ctx->shader_names.data[ctx->shaders.data[sg->shaders.data[0]].name_id]; 3020 s8 last_name = ctx->shader_names.data[ctx->shaders.data[sg->shaders.data[sg->shaders.count - 1]].name_id]; 3021 3022 columns[0][3 * group + 0] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), sg->name, s8("First")); 3023 columns[1][3 * group + 0] = push_s8_from_parts(&m->scratch, s8(""), s8("= "), kind, s8("_"), first_name); 3024 3025 columns[0][3 * group + 1] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), sg->name, s8("Last")); 3026 columns[1][3 * group + 1] = push_s8_from_parts(&m->scratch, s8(""),s8("= "), kind, s8("_"), last_name); 3027 3028 columns[0][3 * group + 2] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), sg->name, s8("Count")); 3029 Stream sb = arena_stream(m->scratch); 3030 stream_append_s8(&sb, s8("= ")); 3031 stream_append_u64(&sb, (u64)sg->shaders.count); 3032 columns[1][3 * group + 2] = arena_stream_commit(&m->scratch, &sb); 3033 } 3034 metagen_push_table(m, m->scratch, s8(""), s8(","), columns, (uz)ctx->shader_groups.count * 3, 2); 3035 3036 meta_end_scope(m, s8("} "), kind, s8(";\n")); 3037 m->scratch = ctx->scratch; 3038 } 3039 3040 ////////////////////// 3041 // NOTE(rnp): structs 3042 for (u32 bake = 0; bake < ctx->shader_bake_parameters.count; bake++) { 3043 Arena tmp = m->scratch; 3044 MetaShaderBakeParameters *b = ctx->shader_bake_parameters.data + bake; 3045 MetaShader *s = ctx->shaders.data + b->shader_id; 3046 s8 name = push_s8_from_parts(&m->scratch, s8(""), s8("BeamformerShader"), 3047 ctx->shader_names.data[s->name_id], s8("BakeParameters")); 3048 meta_begin_scope(m, s8("typedef struct {")); 3049 for (u32 entry = 0; entry < b->entry_count; entry++) { 3050 s8 kind = (b->floating_point & (1 << entry))? s8("f32 ") : s8("u32 "); 3051 meta_push_line(m, kind, b->names_lower[entry], s8(";")); 3052 } 3053 meta_end_scope(m, s8("} "), name, s8(";\n")); 3054 m->scratch = tmp; 3055 } 3056 3057 // shader bake parameter struct 3058 meta_begin_scope(m, s8("typedef struct {")); 3059 { 3060 meta_begin_scope(m, s8("union {")); 3061 { 3062 Arena tmp = m->scratch; 3063 s8 *columns[2]; 3064 columns[0] = push_array(&m->scratch, s8, ctx->shader_bake_parameters.count); 3065 columns[1] = push_array(&m->scratch, s8, ctx->shader_bake_parameters.count); 3066 for (u32 bake = 0; bake < ctx->shader_bake_parameters.count; bake++) { 3067 MetaShaderBakeParameters *b = ctx->shader_bake_parameters.data + bake; 3068 MetaShader *s = ctx->shaders.data + b->shader_id; 3069 columns[0][bake] = push_s8_from_parts(&m->scratch, s8(""), s8("BeamformerShader"), 3070 ctx->shader_names.data[s->name_id], s8("BakeParameters")); 3071 columns[1][bake] = ctx->shader_names.data[s->name_id]; 3072 } 3073 metagen_push_table(m, m->scratch, s8(""), s8(";"), columns, 3074 (uz)ctx->shader_bake_parameters.count, 2); 3075 m->scratch = tmp; 3076 } meta_end_scope(m, s8("};")); 3077 s8 names[] = {s8("data_kind"), s8("flags")}; 3078 s8 types[] = {s8("u32"), s8("u32")}; 3079 metagen_push_table(m, m->scratch, s8(""), s8(";"), (s8 *[]){types, names}, countof(names), 2); 3080 } meta_end_scope(m, s8("} BeamformerShaderBakeParameters;\n")); 3081 3082 metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_C, meta_kind_c_types); 3083 3084 ///////////////////////////////// 3085 // NOTE(rnp): shader info tables 3086 meta_begin_scope(m, s8("read_only global s8 beamformer_shader_names[] = {")); 3087 metagen_push_table(m, m->scratch, s8("s8_comp(\""), s8("\"),"), &ctx->shader_names.data, 3088 (uz)ctx->shader_names.count, 1); 3089 meta_end_scope(m, s8("};\n")); 3090 3091 meta_push_shader_reload_info(m, ctx); 3092 3093 meta_begin_scope(m, s8("read_only global i32 *beamformer_shader_header_vectors[] = {")); 3094 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 3095 MetaShader *s = ctx->base_shaders.data[shader].shader; 3096 if (s->global_flag_ids.count || s->shader_enumeration_ids.count) { 3097 meta_begin_line(m, s8("(i32 []){")); 3098 for (iz id = 0; id < s->global_flag_ids.count; id++) { 3099 if (id != 0) meta_push(m, s8(", ")); 3100 meta_push_u64(m, s->global_flag_ids.data[id]); 3101 } 3102 for (iz id = 0; id < s->shader_enumeration_ids.count; id++) { 3103 if (id != 0 || s->global_flag_ids.count) meta_push(m, s8(", ")); 3104 meta_push_u64(m, s->shader_enumeration_ids.data[id]); 3105 } 3106 meta_end_line(m, s8("},")); 3107 } else { 3108 meta_push_line(m, s8("0,")); 3109 } 3110 } 3111 meta_end_scope(m, s8("};\n")); 3112 3113 meta_begin_scope(m, s8("read_only global i32 beamformer_shader_header_vector_lengths[] = {")); 3114 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 3115 MetaShader *s = ctx->base_shaders.data[shader].shader; 3116 meta_indent(m); 3117 meta_push_u64(m, (u64)(s->global_flag_ids.count + s->shader_enumeration_ids.count)); 3118 meta_end_line(m, s8(",")); 3119 } 3120 meta_end_scope(m, s8("};\n")); 3121 3122 meta_begin_scope(m, s8("read_only global s8 *beamformer_shader_bake_parameter_names[] = {")); 3123 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 3124 MetaShader *s = ctx->base_shaders.data[shader].shader; 3125 if (s->bake_parameters) { 3126 meta_begin_scope(m, s8("(s8 []){")); 3127 for (u32 index = 0; index < s->bake_parameters->entry_count; index++) 3128 meta_push_line(m, s8("s8_comp(\""), s->bake_parameters->names_upper[index], s8("\"),")); 3129 meta_end_scope(m, s8("},")); 3130 } else { 3131 meta_push_line(m, s8("0,")); 3132 } 3133 } 3134 meta_end_scope(m, s8("};\n")); 3135 3136 meta_begin_scope(m, s8("read_only global u32 beamformer_shader_bake_parameter_float_bits[] = {")); 3137 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 3138 MetaShader *s = ctx->base_shaders.data[shader].shader; 3139 meta_begin_line(m, s8("0x")); 3140 meta_push_u64_hex_width(m, s->bake_parameters? s->bake_parameters->floating_point : 0, 8); 3141 meta_end_line(m, s8("UL,")); 3142 } 3143 meta_end_scope(m, s8("};\n")); 3144 3145 meta_begin_scope(m, s8("read_only global i32 beamformer_shader_bake_parameter_counts[] = {")); 3146 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 3147 MetaShader *s = ctx->base_shaders.data[shader].shader; 3148 if (s->bake_parameters) { 3149 meta_indent(m); 3150 meta_push_u64(m, s->bake_parameters->entry_count); 3151 meta_end_line(m, s8(",")); 3152 } else { 3153 meta_push_line(m, s8("0,")); 3154 } 3155 } 3156 meta_end_scope(m, s8("};\n")); 3157 3158 //fprintf(stderr, "%.*s\n", (i32)m.stream.widx, m.stream.data); 3159 3160 result = meta_write_and_reset(m, out_meta); 3161 3162 return result; 3163 } 3164 3165 function b32 3166 metagen_matlab_union(MetaprogramContext *m, MetaContext *ctx, MetaMUnion *mu, s8 outdir) 3167 { 3168 b32 result = 1; 3169 Arena scratch = m->scratch; 3170 3171 iz enumeration_id = meta_lookup_string_slow(ctx->enumeration_kinds.data, ctx->enumeration_kinds.count, 3172 mu->enumeration_name); 3173 if (enumeration_id < 0) { 3174 meta_compiler_error(mu->location, "Kind Enumeration '%.*s' requested by @MUnion not defined\n", 3175 (i32)mu->enumeration_name.len, mu->enumeration_name.data); 3176 } 3177 3178 s8_list *enumeration_members = ctx->enumeration_members.data + enumeration_id; 3179 if ((u32)enumeration_members->count != mu->sub_table_count) { 3180 meta_compiler_error(mu->location, "'%.*s' contains %u members but %u were requested by @MUnion\n", 3181 (i32)mu->enumeration_name.len, mu->enumeration_name.data, 3182 (u32)enumeration_members->count, mu->sub_table_count); 3183 } 3184 3185 struct table_match { 3186 MetaTable *table; 3187 MetaKind *kinds; 3188 u32 name_index; 3189 } *table_matches; 3190 3191 table_matches = push_array(&scratch, struct table_match, mu->sub_table_count); 3192 for (u32 index = 0; index < mu->sub_table_count; index++) { 3193 s8 sub_table_name = mu->sub_table_names[index]; 3194 MetaTable *t = ctx->tables.data + meta_lookup_string_slow(ctx->table_names.data, 3195 ctx->table_names.count, sub_table_name); 3196 if (t < ctx->tables.data) { 3197 meta_compiler_error(mu->location, "Parameter Table '%.*s' requested by @MUnion not defined\n", 3198 (i32)sub_table_name.len, sub_table_name.data); 3199 } 3200 3201 iz type_index = meta_lookup_string_slow(t->fields, t->field_count, s8("type")); 3202 iz name_index = meta_lookup_string_slow(t->fields, t->field_count, s8("name")); 3203 if (type_index < 0) { 3204 meta_compiler_error_message(mu->location, "'%.*s' missing fields required by @MUnion: type\n", 3205 (i32)sub_table_name.len, sub_table_name.data); 3206 } 3207 if (name_index < 0) { 3208 meta_compiler_error_message(mu->location, "'%.*s' missing fields required by @MUnion: name\n", 3209 (i32)sub_table_name.len, sub_table_name.data); 3210 } 3211 if (type_index < 0 || name_index < 0) meta_error(); 3212 3213 table_matches[index].table = t; 3214 table_matches[index].kinds = push_array(&scratch, MetaKind, t->entry_count); 3215 table_matches[index].name_index = (u32)name_index; 3216 3217 for (u32 entry = 0; entry < t->entry_count; entry++) { 3218 table_matches[index].kinds[entry] = meta_map_kind(t->entries[type_index][entry], sub_table_name, 3219 mu->location); 3220 } 3221 } 3222 3223 u32 max_parameter_size = 0; 3224 for (iz member = 0; member < enumeration_members->count; member++) { 3225 u32 parameter_size = 0; 3226 for (u32 prop = 0; prop < table_matches[member].table->entry_count; prop++) 3227 parameter_size += meta_kind_byte_sizes[table_matches[member].kinds[prop]]; 3228 max_parameter_size = MAX(parameter_size, max_parameter_size); 3229 } 3230 3231 Arena scratch_temp = scratch; 3232 s8 namespace = ctx->munion_namespaces.data[mu->namespace_id]; 3233 3234 s8 outfile = push_s8_from_parts(&scratch, s8(OS_PATH_SEPARATOR), outdir, s8("Base.m")); 3235 meta_begin_scope(m, s8("classdef Base")); 3236 { 3237 meta_begin_scope(m, s8("methods")); 3238 { 3239 meta_begin_scope(m, s8("function out = Pack(obj)")); 3240 { 3241 meta_begin_line(m, s8("out = zeros(1, ")); 3242 meta_push_u64(m, max_parameter_size); 3243 meta_end_line(m, s8(", 'uint8');")); 3244 meta_push_line(m, s8("fields = struct2cell(struct(obj));")); 3245 meta_push_line(m, s8("offset = 1;")); 3246 meta_begin_scope(m, s8("for i = 1:numel(fields)")); 3247 { 3248 meta_push_line(m, s8("bytes = typecast(fields{i}, 'uint8');")); 3249 meta_push_line(m, s8("out(offset:(offset + numel(bytes) - 1)) = bytes;")); 3250 meta_push_line(m, s8("offset = offset + numel(bytes);")); 3251 } meta_end_scope(m, s8("end")); 3252 } meta_end_scope(m, s8("end")); 3253 } meta_end_scope(m, s8("end")); 3254 } meta_end_scope(m, s8("end")); 3255 result &= meta_end_and_write_matlab(m, (c8 *)outfile.data); 3256 scratch = scratch_temp; 3257 3258 for (iz member = 0; member < enumeration_members->count; member++) { 3259 MetaTable *t = table_matches[member].table; 3260 MetaKind *kinds = table_matches[member].kinds; 3261 3262 s8 sub_name = enumeration_members->data[member]; 3263 outfile = push_s8_from_parts(&scratch, s8(""), outdir, s8(OS_PATH_SEPARATOR), sub_name, s8(".m")); 3264 meta_begin_scope(m, s8("classdef "), sub_name, s8(" < OGLBeamformer"), namespace, s8(".Base")); 3265 { 3266 meta_begin_scope(m, s8("properties")); 3267 { 3268 u32 name_index = table_matches[member].name_index; 3269 for (u32 prop = 0; prop < t->entry_count; prop++) { 3270 meta_begin_line(m, t->entries[name_index][prop], s8("(1,")); 3271 meta_push_u64(m, meta_kind_elements[kinds[prop]]); 3272 meta_end_line(m, s8(") "), meta_kind_matlab_types[kinds[prop]]); 3273 } 3274 } meta_end_scope(m, s8("end")); 3275 } meta_end_scope(m, s8("end")); 3276 result &= meta_end_and_write_matlab(m, (c8 *)outfile.data); 3277 scratch = scratch_temp; 3278 } 3279 3280 return result; 3281 } 3282 3283 function b32 3284 metagen_emit_matlab_code(MetaContext *ctx, Arena arena) 3285 { 3286 b32 result = 1; 3287 if (!needs_rebuild(OUTPUT("matlab/OGLBeamformerLiveImagingParameters.m"), "beamformer_parameters.h", "beamformer.meta")) 3288 return result; 3289 3290 build_log_generate("MATLAB Bindings"); 3291 char *base_directory = OUTPUT("matlab"); 3292 if (!os_remove_directory(base_directory)) 3293 build_fatal("failed to remove directory: %s", base_directory); 3294 3295 if (setjmp(compiler_jmp_buf)) { 3296 os_remove_directory(base_directory); 3297 build_log_error("Failed to generate MATLAB Bindings"); 3298 return 0; 3299 } 3300 3301 os_make_directory(base_directory); 3302 3303 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 3304 3305 #define X(name, flag, ...) meta_push_line(m, s8(#name " (" str(flag) ")")); 3306 meta_begin_matlab_class(m, "OGLBeamformerLiveFeedbackFlags", "int32"); 3307 meta_begin_scope(m, s8("enumeration")); 3308 BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST 3309 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerLiveFeedbackFlags.m")); 3310 #undef X 3311 3312 #define X(name, __t, __s, elements, ...) meta_push_matlab_property(m, s8(#name), elements, s8("")); 3313 meta_begin_matlab_class(m, "OGLBeamformerLiveImagingParameters"); 3314 meta_begin_scope(m, s8("properties")); 3315 BEAMFORMER_LIVE_IMAGING_PARAMETERS_LIST 3316 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerLiveImagingParameters.m")); 3317 #undef X 3318 3319 meta_begin_matlab_class(m, "OGLBeamformerShaderStage", "int32"); 3320 meta_begin_scope(m, s8("enumeration")); 3321 { 3322 iz index = -1; 3323 for (iz group = 0; group < ctx->shader_groups.count; group++) { 3324 if (s8_equal(ctx->shader_groups.data[group].name, s8("Compute"))) { 3325 index = group; 3326 break; 3327 } 3328 } 3329 if (index != -1) { 3330 MetaShaderGroup *sg = ctx->shader_groups.data + index; 3331 /* TODO(rnp): this assumes that the shaders are sequential */ 3332 s8 *names = ctx->shader_names.data + ctx->shaders.data[0].name_id; 3333 metagen_push_counted_enum_body(m, s8(""), s8(""), s8("("), s8(")"), names, sg->shaders.count); 3334 } else { 3335 build_log_failure("failed to find Compute shader group in meta info\n"); 3336 } 3337 result &= index != -1; 3338 } 3339 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerShaderStage.m")); 3340 3341 for (iz kind = 0; kind < ctx->enumeration_kinds.count; kind++) { 3342 Arena scratch = ctx->scratch; 3343 s8 name = ctx->enumeration_kinds.data[kind]; 3344 s8 output = push_s8_from_parts(&scratch, s8(""), s8(OUTPUT("matlab/OGLBeamformer")), name, s8(".m")); 3345 s8_list *kinds = ctx->enumeration_members.data + kind; 3346 meta_begin_scope(m, s8("classdef OGLBeamformer"), name, s8(" < int32")); 3347 meta_begin_scope(m, s8("enumeration")); 3348 s8 prefix = s8(""); 3349 if (kinds->count > 0 && ISDIGIT(kinds->data[0].data[0])) prefix = s8("m"); 3350 metagen_push_counted_enum_body(m, s8(""), prefix, s8("("), s8(")"), kinds->data, kinds->count); 3351 result &= meta_end_and_write_matlab(m, (c8 *)output.data); 3352 } 3353 3354 //////////////////// 3355 // NOTE: emit files 3356 { 3357 MetaEmitOperationListSet *emit_set = ctx->emit_sets + MetaEmitLang_MATLAB; 3358 for (u32 list = 0; list < emit_set->count; list++) { 3359 MetaEmitOperationList *ops = emit_set->data + list; 3360 Arena scratch = m->scratch; 3361 s8 output = push_s8_from_parts(&m->scratch, s8(""), 3362 s8(OUTPUT("matlab") OS_PATH_SEPARATOR "OGLBeamformer"), 3363 ops->filename, s8(".m")); 3364 meta_push_line(m, s8("% GENERATED CODE")); 3365 metagen_run_emit(m, ctx, ops, meta_kind_matlab_types); 3366 result &= meta_write_and_reset(m, (c8 *)output.data); 3367 m->scratch = scratch; 3368 } 3369 } 3370 3371 ////////////////// 3372 // NOTE: MUnions 3373 for (iz munion = 0; munion < ctx->munions.count; munion++) { 3374 Arena scratch = m->scratch; 3375 MetaMUnion *mu = ctx->munions.data + munion; 3376 s8 namespace = ctx->munion_namespaces.data[mu->namespace_id]; 3377 s8 outdir = push_s8_from_parts(&m->scratch, s8(""), s8(OUTPUT("matlab")), 3378 s8(OS_PATH_SEPARATOR "+OGLBeamformer"), namespace); 3379 os_make_directory((c8 *)outdir.data); 3380 result &= metagen_matlab_union(m, ctx, mu, outdir); 3381 m->scratch = scratch; 3382 } 3383 3384 return result; 3385 } 3386 3387 function b32 3388 metagen_emit_helper_library_header(MetaContext *ctx, Arena arena) 3389 { 3390 b32 result = 1; 3391 char *out = OUTPUT("ogl_beamformer_lib.h"); 3392 if (!needs_rebuild(out, "lib/ogl_beamformer_lib_base.h", "beamformer.meta")) 3393 return result; 3394 3395 build_log_generate("Library Header"); 3396 3397 s8 parameters_header = read_entire_file("beamformer_parameters.h", &arena); 3398 s8 base_header = read_entire_file("lib/ogl_beamformer_lib_base.h", &arena); 3399 3400 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 3401 3402 meta_push(m, c_file_header); 3403 meta_push_line(m, s8("#include <stdint.h>\n")); 3404 3405 ///////////////////////// 3406 // NOTE(rnp): enumarents 3407 for (iz kind = 0; kind < ctx->enumeration_kinds.count; kind++) { 3408 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8("Beamformer"), ctx->enumeration_kinds.data[kind]); 3409 metagen_push_c_enum(m, m->scratch, enum_name, ctx->enumeration_members.data[kind].data, 3410 ctx->enumeration_members.data[kind].count); 3411 m->scratch = ctx->scratch; 3412 } 3413 3414 { 3415 iz index = -1; 3416 for (iz group = 0; group < ctx->shader_groups.count; group++) { 3417 if (s8_equal(ctx->shader_groups.data[group].name, s8("Compute"))) { 3418 index = group; 3419 break; 3420 } 3421 } 3422 3423 if (index != -1) { 3424 u64 offset = 0; 3425 for (iz group = 0; group < index; group++) 3426 offset += (u64)ctx->shader_groups.data[group].shaders.count; 3427 3428 MetaShaderGroup *sg = ctx->shader_groups.data + index; 3429 s8 *columns[2]; 3430 columns[0] = push_array(&m->scratch, s8, sg->shaders.count); 3431 columns[1] = push_array(&m->scratch, s8, sg->shaders.count); 3432 3433 Stream sb = arena_stream(m->scratch); 3434 for (iz id = 0; id < sg->shaders.count; id++) { 3435 stream_append_s8(&sb, s8("= ")); 3436 stream_append_u64(&sb, offset + (u64)id); 3437 3438 MetaShader *s = ctx->shaders.data + sg->shaders.data[id]; 3439 columns[0][id] = ctx->shader_names.data[s->name_id]; 3440 columns[1][id] = arena_stream_commit_and_reset(&m->scratch, &sb); 3441 } 3442 3443 meta_begin_scope(m, s8("typedef enum {")); 3444 metagen_push_table(m, m->scratch, s8("BeamformerShaderKind_"), s8(","), columns, 3445 (uz)sg->shaders.count, 2); 3446 meta_end_scope(m, s8("} BeamformerShaderKind;\n")); 3447 3448 m->scratch = ctx->scratch; 3449 3450 meta_begin_line(m, s8("#define BeamformerShaderKind_ComputeCount (")); 3451 meta_push_u64(m, (u64)sg->shaders.count); 3452 meta_end_line(m, s8(")\n")); 3453 } else { 3454 build_log_failure("failed to find Compute shader group in meta info\n"); 3455 } 3456 } 3457 3458 metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_CLibrary, meta_kind_base_c_types); 3459 3460 meta_push_line(m, s8("// END GENERATED CODE\n")); 3461 3462 meta_push(m, parameters_header, base_header); 3463 result &= meta_write_and_reset(m, out); 3464 3465 { 3466 CommandList cpp = {0}; 3467 cmd_append(&arena, &cpp, PREPROCESSOR, out, COMPILER_OUTPUT, OUTPUT("ogl_beamformer_lib_python_ffi.h")); 3468 result &= run_synchronous(arena, &cpp); 3469 } 3470 3471 return result; 3472 } 3473 3474 function MetaContext * 3475 metagen_load_context(Arena *arena, char *filename) 3476 { 3477 if (setjmp(compiler_jmp_buf)) { 3478 /* NOTE(rnp): compiler error */ 3479 return 0; 3480 } 3481 3482 MetaContext *ctx = push_struct(arena, MetaContext); 3483 ctx->scratch = sub_arena(arena, MB(1), 16); 3484 ctx->arena = arena; 3485 3486 MetaContext *result = ctx; 3487 3488 ctx->filename = c_str_to_s8(filename); 3489 ctx->directory = s8_chop(&ctx->filename, s8_scan_backwards(ctx->filename, OS_PATH_SEPARATOR_CHAR)); 3490 s8_chop(&ctx->filename, 1); 3491 if (ctx->directory.len <= 0) ctx->directory = s8("."); 3492 3493 Arena scratch = ctx->scratch; 3494 MetaEntryStack entries = meta_entry_stack_from_file(ctx->arena, filename); 3495 3496 i32 stack_items[32]; 3497 struct { i32 *data; iz capacity; iz count; } stack = {stack_items, countof(stack_items), 0}; 3498 3499 MetaShaderGroup *current_shader_group = 0; 3500 for (iz i = 0; i < entries.count; i++) { 3501 MetaEntry *e = entries.data + i; 3502 //if (e->kind == MetaEntryKind_EndScope) depth--; 3503 //meta_entry_print(e, depth, -1); 3504 //if (e->kind == MetaEntryKind_BeginScope) depth++; 3505 //continue; 3506 3507 switch (e->kind) { 3508 case MetaEntryKind_BeginScope:{ *da_push(&scratch, &stack) = (i32)(i - 1); }break; 3509 case MetaEntryKind_EndScope:{ 3510 i32 index = stack.data[--stack.count]; 3511 MetaEntry *ended = entries.data + index; 3512 switch (ended->kind) { 3513 case MetaEntryKind_ShaderGroup:{ current_shader_group = 0; }break; 3514 default:{}break; 3515 } 3516 }break; 3517 case MetaEntryKind_Emit:{ 3518 i += meta_pack_emit(ctx, scratch, e, entries.count - i); 3519 }break; 3520 case MetaEntryKind_Enumeration:{ 3521 meta_entry_argument_expected(e, s8("kind"), s8("[id ...]")); 3522 s8 kind = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 3523 MetaEntryArgument ids = meta_entry_argument_expect(e, 1, MetaEntryArgumentKind_Array); 3524 for (u32 id = 0; id < ids.count; id++) 3525 meta_commit_enumeration(ctx, kind, ids.strings[id]); 3526 }break; 3527 case MetaEntryKind_Embed:{ 3528 meta_embed(ctx, scratch, e, entries.count - i); 3529 }break; 3530 case MetaEntryKind_Expand:{ 3531 i += meta_expand(ctx, scratch, e, entries.count - i, 0); 3532 }break; 3533 case MetaEntryKind_MUnion:{ 3534 meta_pack_munion(ctx, e, entries.count - i); 3535 }break; 3536 case MetaEntryKind_ShaderGroup:{ 3537 MetaShaderGroup *sg = da_push(ctx->arena, &ctx->shader_groups); 3538 sg->name = e->name; 3539 current_shader_group = sg; 3540 }break; 3541 case MetaEntryKind_Shader:{ 3542 if (!current_shader_group) goto error; 3543 i += meta_pack_shader(ctx, current_shader_group, scratch, e, entries.count - i); 3544 }break; 3545 case MetaEntryKind_Table:{ 3546 i += meta_pack_table(ctx, e, entries.count - i); 3547 }break; 3548 3549 error: 3550 default: 3551 { 3552 meta_entry_error(e, "invalid @%s() in global scope\n", meta_entry_kind_strings[e->kind]); 3553 }break; 3554 } 3555 } 3556 3557 result->arena = 0; 3558 return result; 3559 } 3560 3561 function b32 3562 metagen_file_direct(Arena arena, char *filename) 3563 { 3564 MetaContext *ctx = metagen_load_context(&arena, filename); 3565 if (!ctx) return 0; 3566 if (ctx->shaders.count || ctx->base_shaders.count || ctx->shader_groups.count) { 3567 build_log_error("shaders not supported in file: %s\n", filename); 3568 return 0; 3569 } 3570 3571 b32 result = 1; 3572 char *out; 3573 { 3574 s8 basename; 3575 s8_split(ctx->filename, &basename, 0, '.'); 3576 3577 Stream sb = arena_stream(arena); 3578 stream_append_s8s(&sb, ctx->directory, s8(OS_PATH_SEPARATOR), s8("generated")); 3579 stream_append_byte(&sb, 0); 3580 os_make_directory((c8 *)sb.data); 3581 stream_reset(&sb, sb.widx - 1); 3582 3583 stream_append_s8s(&sb, s8(OS_PATH_SEPARATOR), basename, s8(".c")); 3584 stream_append_byte(&sb, 0); 3585 3586 out = (c8 *)arena_stream_commit(&arena, &sb).data; 3587 } 3588 3589 CommandList deps = meta_extract_emit_file_dependencies(ctx, &arena); 3590 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 3591 if (needs_rebuild_(out, deps.data, deps.count)) { 3592 build_log_generate(out); 3593 meta_push(m, c_file_header); 3594 metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_C, meta_kind_c_types); 3595 result &= meta_write_and_reset(m, out); 3596 } 3597 3598 return result; 3599 } 3600 3601 i32 3602 main(i32 argc, char *argv[]) 3603 { 3604 u64 start_time = os_timer_count(); 3605 g_argv0 = argv[0]; 3606 3607 b32 result = 1; 3608 Arena arena = os_alloc_arena(MB(8)); 3609 check_rebuild_self(arena, argc, argv); 3610 3611 os_make_directory(OUTDIR); 3612 3613 result &= metagen_file_direct(arena, "assets" OS_PATH_SEPARATOR "assets.meta"); 3614 3615 MetaContext *meta = metagen_load_context(&arena, "beamformer.meta"); 3616 if (!meta) return 1; 3617 3618 result &= metagen_emit_c_code(meta, arena); 3619 result &= metagen_emit_helper_library_header(meta, arena); 3620 result &= metagen_emit_matlab_code(meta, arena); 3621 3622 parse_config(argc, argv); 3623 3624 if (!check_build_raylib(arena)) return 1; 3625 3626 ///////////////// 3627 // lib/tests 3628 result &= build_helper_library(arena); 3629 if (config.tests) result &= build_tests(arena); 3630 3631 ////////////////// 3632 // static portion 3633 result &= build_beamformer_main(arena); 3634 3635 ///////////////////////// 3636 // hot reloadable portion 3637 // 3638 // NOTE: this is built after main because on w32 we need to export 3639 // gl function pointers for the reloadable portion to import 3640 if (config.debug) result &= build_beamformer_as_library(arena); 3641 3642 if (config.time) { 3643 f64 seconds = (f64)(os_timer_count() - start_time) / (f64)os_timer_frequency(); 3644 build_log_info("took %0.03f [s]", seconds); 3645 } 3646 3647 return result != 1; 3648 }