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