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