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