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