/* TODO: Move decls to meta.h */ #define MetaRebuildCode 1317212284 //////////////////////////////////////////////////////////// //~ Default base layer compiler definitions #ifndef IsConsoleApp # define IsConsoleApp 1 #endif #ifndef IsRtcEnabled # define IsRtcEnabled 1 #endif #ifndef IsUnoptimized # define IsUnoptimized 1 #endif #ifndef IsAsanEnabled # define IsAsanEnabled 0 #endif #ifndef IsCrtlibEnabled # define IsCrtlibEnabled 1 #endif #ifndef IsDebinfoEnabled # define IsDebinfoEnabled 1 #endif #ifndef IsDeveloperModeEnabled # define IsDeveloperModeEnabled 1 #endif #ifndef IsUnoptimized # define IsUnoptimized 1 #endif #ifndef IsTestingEnabled # define IsTestingEnabled 0 #endif #ifndef IsHotSwappingEnabled # define IsHotSwappingEnabled 0 #endif //////////////////////////////////////////////////////////// //~ Includes //- Header files #include "../base/base_inc.h" #include "meta_os/meta_os_inc.h" #include "meta_file/meta_file_inc.h" #include "meta_lay.h" #include "meta.h" //- Source files #include "meta_lay.c" //////////////////////////////////////////////////////////// //~ Global command line args Struct(CmdLineArgs) { String leaf_layer_name; } cmdline = ZI; //////////////////////////////////////////////////////////// //~ Util void EchoLine(String msg) { Echo(msg); Echo(Lit("\n")); } Struct(LineCol) { i64 line; i64 col; }; LineCol LineColFromPos(String data, i64 pos) { TempArena scratch = BeginScratchNoConflict(); LineCol result = ZI; for (u64 cur = 0; cur < data.len && cur <= pos; ++cur) { u8 c = data.text[cur]; if (c == '\n') { ++result.line; result.col = 0; } else if (c != '\r') { ++result.col; } } result.line += 1; EndScratch(scratch); return result; } //////////////////////////////////////////////////////////// //~ Compiler params Struct(CompilerParams) { StringList defs; StringList warnings_msvc; StringList warnings_clang; StringList warnings_dxc; StringList flags_msvc; StringList flags_clang; StringList flags_dxc; StringList compiler_only_flags_msvc; StringList compiler_only_flags_clang; StringList linker_only_flags_msvc; StringList linker_only_flags_clang; }; //////////////////////////////////////////////////////////// //~ Build step job #if 0 Enum(StepParamsFlag) { StepParamsFlag_None = 0, StepParamsFlag_CompileProgram = (1 << 0), StepParamsFlag_CompileShaders = (1 << 1), StepParamsFlag_CompileEmbeddedDirs = (1 << 2), StepParamsFlag_CompileArc = (1 << 3), StepParamsFlag_RunCommand = (1 << 4), }; Struct(StepParams) { StepParamsFlag flags; M_Layer flattened; CompilerParams compiler_params; String arc_store; String arc_dir; String cmd; }; Struct(StepResult) { StringList obj_files; StringList output_lines; M_ErrorList meta_errors; OS_CommandResult cmd_result; i32 return_code; i64 elapsed_ns; }; void InheritStepResults(Arena *arena, StepResult *dst, u64 srcs_count, StepResult *srcs) { for (i32 i = 0; i < srcs_count; ++i) { StepResult *src = &srcs[i]; if (dst->return_code == 0) { dst->return_code = src->return_code; } for (StringListNode *n = src->output_lines.first; n; n = n->next) { PushStringToList(arena, &dst->output_lines, n->s); } for (StringListNode *n = src->obj_files.first; n; n = n->next) { PushStringToList(arena, &dst->obj_files, n->s); } M_AppendErrors(arena, &dst->meta_errors, src->meta_errors); } } void Step(void) { StepParams *params = &sig->params[id]; StepParamsFlag flags = params->flags; CompilerParams cp = params->compiler_params; Arena *arena = PermArena(); StepResult *result = &sig->results[id]; M_ErrorList *errors = &result->meta_errors; StringList *output = &result->output_lines; i64 start_ns = TimeNs(); String shader_store_name = Lit("ShadersStore"); ////////////////////////////// //- Build C if (flags & StepParamsFlag_CompileProgram) { //- Generate C file String c_out_file = F_GetFull(arena, StringF(arena, "%F_gen_c.c", FmtString(cmdline.leaf_layer_name))); { StringList c_store_lines = ZI; StringList c_shader_lines = ZI; StringList c_include_lines = ZI; StringList c_startup_lines = ZI; { for (M_Entry *entry = params->flattened.first; entry->valid; entry = entry->next) { M_EntryKind kind = entry->kind; M_Token *entry_tok = entry->name_token; M_Token *arg0_tok = entry->arg_tokens[0]; M_Token *arg1_tok = entry->arg_tokens[1]; switch (kind) { default: break; case M_EntryKind_EmbedDir: { if (arg0_tok->valid && arg1_tok->valid) { String store_name = arg0_tok->s; String token_file = arg1_tok->file->name; String token_parent_dir = F_GetParentDir(token_file); String arg_dir = arg1_tok->s; String full = F_GetFullCrossPlatform(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_dir))); if (F_IsDir(full)) { u64 hash = HashFnv64(Fnv64Basis, StringF(arena, "%F/", FmtString(store_name))); String line = StringF(arena, "ResourceStore %F = { 0x%F };", FmtString(store_name), FmtHex(hash)); PushStringToList(arena, &c_store_lines, line); } else { String err = StringF(arena, "Directory '%F' not found", FmtString(full)); M_PushError(arena, errors, arg1_tok, err); } } else { M_PushError(arena, errors, entry_tok, Lit("Expected resource store & directory name")); } } break; case M_EntryKind_VertexShader: case M_EntryKind_PixelShader: case M_EntryKind_ComputeShader: { if (arg0_tok->valid) { String shader_type = kind == M_EntryKind_VertexShader ? Lit("VertexShader") : kind == M_EntryKind_PixelShader ? Lit("PixelShader") : kind == M_EntryKind_ComputeShader ? Lit("ComputeShader") : Lit(""); String shader_name = arg0_tok->s; u64 hash = HashFnv64(Fnv64Basis, StringF(arena, "%F/%F", FmtString(shader_store_name), FmtString(shader_name))); String line = StringF(arena, "%F %F = { 0x%F };", FmtString(shader_type), FmtString(shader_name), FmtHex(hash)); PushStringToList(arena, &c_shader_lines, line); } else { M_PushError(arena, errors, entry_tok, Lit("Expected shader name")); } } break; case M_EntryKind_IncludeC: { if (arg0_tok->valid) { String token_file = arg0_tok->file->name; String token_parent_dir = F_GetParentDir(token_file); String arg_file = arg0_tok->s; String full = F_GetFull(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_file))); if (F_IsFile(full)) { String line = StringF(arena, "#include \"%F\"", FmtString(full)); PushStringToList(arena, &c_include_lines, line); } else { String err = StringF(arena, "File '%F' not found", FmtString(full)); M_PushError(arena, errors, arg0_tok, err); } } else { M_PushError(arena, errors, entry_tok, Lit("Expected file name")); } } break; case M_EntryKind_Startup: { if (arg0_tok->valid) { String startup = arg0_tok->s; String line = StringF(arena, " %F();", FmtString(startup)); PushStringToList(arena, &c_startup_lines, line); } else { M_PushError(arena, errors, entry_tok, Lit("Expected startup function name")); } } break; } } } if (errors->count == 0) { StringList c_out_lines = ZI; PushStringToList(arena, &c_out_lines, Lit("// Auto generated file")); /* Include base layer */ { String base_inc_path = F_GetFull(arena, Lit("../src/base/base_inc.h")); PushStringToList(arena, &c_out_lines, Lit("")); PushStringToList(arena, &c_out_lines, Lit("//- Base layer includes")); PushStringToList(arena, &c_out_lines, StringF(arena, "#include \"%F\"", FmtString(base_inc_path))); } /* Define resource stores */ if (c_store_lines.count > 0) { PushStringToList(arena, &c_out_lines, Lit("")); PushStringToList(arena, &c_out_lines, Lit("//- Resource stores")); for (StringListNode *n = c_store_lines.first; n; n = n->next) { PushStringToList(arena, &c_out_lines, n->s); } } /* Define shaders */ if (c_shader_lines.count > 0) { PushStringToList(arena, &c_out_lines, Lit("")); PushStringToList(arena, &c_out_lines, Lit("//- Shaders")); for (StringListNode *n = c_shader_lines.first; n; n = n->next) { PushStringToList(arena, &c_out_lines, n->s); } } /* Include dependency layers */ if (c_include_lines.count > 0) { PushStringToList(arena, &c_out_lines, Lit("")); PushStringToList(arena, &c_out_lines, Lit("//- Dependency graph includes")); for (StringListNode *n = c_include_lines.first; n; n = n->next) { PushStringToList(arena, &c_out_lines, n->s); } } /* Define StartupLayers */ { PushStringToList(arena, &c_out_lines, Lit("")); PushStringToList(arena, &c_out_lines, Lit("//- Startup")); PushStringToList(arena, &c_out_lines, Lit("void StartupLayers(void)")); PushStringToList(arena, &c_out_lines, Lit("{")); for (StringListNode *n = c_startup_lines.first; n; n = n->next) { PushStringToList(arena, &c_out_lines, n->s); } PushStringToList(arena, &c_out_lines, Lit("}")); } /* Write to file */ PushStringToList(arena, &c_out_lines, Lit("")); String c_out = StringFromList(arena, c_out_lines, Lit("\n")); F_ClearWrite(c_out_file, c_out); } } //- Compile C String c_out_obj_file = StringF(arena, "%F_gen_c.obj", FmtString(cmdline.leaf_layer_name)); PushStringToList(arena, &result->obj_files, c_out_obj_file); if (errors->count == 0) { String cmd = StringF(arena, "cmd /c cl.exe /c %F -Fo:%F %F %F %F %F", FmtString(c_out_file), FmtString(c_out_obj_file), FmtString(StringFromList(arena, cp.flags_msvc, Lit(" "))), FmtString(StringFromList(arena, cp.compiler_only_flags_msvc, Lit(" "))), FmtString(StringFromList(arena, cp.warnings_msvc, Lit(" "))), FmtString(StringFromList(arena, cp.defs, Lit(" ")))); OS_CommandResult cmd_result = OS_RunCommand(arena, cmd); String cmd_output = TrimWhitespace(cmd_result.output); if (cmd_output.len > 0) { PushStringToList(arena, output, cmd_output); } result->return_code = cmd_result.code; } } ////////////////////////////// //- Build shaders if (flags & StepParamsFlag_CompileShaders) { String shader_store_name = shader_store_name; OS_Mkdir(shader_store_name); { /* Remove all old shaders */ StringList files = ZI; F_FilesFromDir(arena, &files, shader_store_name, F_IterFlag_None); for (StringListNode *n = files.first; n; n = n->next) { String file = n->s; /* Safety check to prevent non-shader files from being removed */ if (StringEndsWith(file, Lit("VS")) || StringEndsWith(file, Lit("CS")) || StringEndsWith(file, Lit("PS"))) { OS_Rm(n->s); } } } //- Generate GPU file & shader entries String gpu_out_file = F_GetFull(arena, StringF(arena, "%F_gen_gpu.hlsl", FmtString(cmdline.leaf_layer_name))); Enum(ShaderEntryKind) { ShaderEntryKind_VS, ShaderEntryKind_PS, ShaderEntryKind_CS, }; Struct(ShaderEntry) { ShaderEntry *next; ShaderEntryKind kind; String name; }; ShaderEntry *first_shader_entry = 0; ShaderEntry *last_shader_entry = 0; u64 shader_entries_count = 0; { StringList gpu_include_lines = ZI; { for (M_Entry *entry = params->flattened.first; entry->valid; entry = entry->next) { M_EntryKind kind = entry->kind; M_Token *entry_tok = entry->name_token; M_Token *arg0_tok = entry->arg_tokens[0]; M_Token *arg1_tok = entry->arg_tokens[1]; switch (kind) { default: break; case M_EntryKind_IncludeGpu: { if (arg0_tok->valid) { String token_file = arg0_tok->file->name; String token_parent_dir = F_GetParentDir(token_file); String arg_file = arg0_tok->s; String full = F_GetFull(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_file))); if (F_IsFile(full)) { String line = StringF(arena, "#include \"%F\"", FmtString(full)); PushStringToList(arena, &gpu_include_lines, line); } else { String err = StringF(arena, "File '%F' not found", FmtString(full)); M_PushError(arena, errors, arg0_tok, err); } } else { M_PushError(arena, errors, entry_tok, Lit("Expected file name")); } } break; case M_EntryKind_VertexShader: case M_EntryKind_PixelShader: case M_EntryKind_ComputeShader: { if (arg0_tok->valid) { ShaderEntryKind shader_kind = kind == M_EntryKind_VertexShader ? ShaderEntryKind_VS : kind == M_EntryKind_PixelShader ? ShaderEntryKind_PS : kind == M_EntryKind_ComputeShader ? ShaderEntryKind_CS : ShaderEntryKind_VS; String shader_name = arg0_tok->s; ShaderEntry *e = PushStruct(arena, ShaderEntry); e->kind = shader_kind; e->name = shader_name; SllQueuePush(first_shader_entry, last_shader_entry, e); ++shader_entries_count; } else { M_PushError(arena, errors, entry_tok, Lit("Expected shader name")); } } break; } } } if (errors->count == 0) { StringList gpu_out_lines = ZI; PushStringToList(arena, &gpu_out_lines, Lit("// Auto generated file")); /* Include base layer */ { String base_inc_path = F_GetFull(arena, Lit("../src/base/base_inc.h")); PushStringToList(arena, &gpu_out_lines, Lit("")); PushStringToList(arena, &gpu_out_lines, Lit("//- Base layer includes")); PushStringToList(arena, &gpu_out_lines, StringF(arena, "#include \"%F\"", FmtString(base_inc_path))); } /* Include dependency layers */ if (gpu_out_lines.count > 0) { PushStringToList(arena, &gpu_out_lines, Lit("")); PushStringToList(arena, &gpu_out_lines, Lit("//- Dependency graph includes")); for (StringListNode *n = gpu_include_lines.first; n; n = n->next) { PushStringToList(arena, &gpu_out_lines, n->s); } } /* Write to file */ PushStringToList(arena, &gpu_out_lines, Lit("")); String c_out = StringFromList(arena, gpu_out_lines, Lit("\n")); F_ClearWrite(gpu_out_file, c_out); } } //- Compile shaders String *compile_cmds = PushStructs(arena, String, shader_entries_count); RunCommandResult *compile_results = PushStructs(arena, RunCommandResult, shader_entries_count); { i32 i = 0; for (ShaderEntry *e = first_shader_entry; e; e = e->next) { String target = e->kind == ShaderEntryKind_VS ? Lit("vs_6_6") : e->kind == ShaderEntryKind_PS ? Lit("ps_6_6") : e->kind == ShaderEntryKind_CS ? Lit("cs_6_6") : Lit("vs_6_6"); String *compile_cmd = &compile_cmds[i]; *compile_cmd = StringF(arena, "cmd /c dxc.exe -T %F -E %F -Fo %F/%F %F %F %F", FmtString(target), FmtString(e->name), FmtString(shader_store_name), FmtString(e->name), FmtString(gpu_out_file), FmtString(StringFromList(arena, cp.defs, Lit(" "))), FmtString(StringFromList(arena, cp.flags_dxc, Lit(" ")))); ++i; } } u32 job_count = 0; Fence job_fence = ZI; job_count += RunJob(RunCommand, .count = shader_entries_count, .fence = &job_fence, .sig = { .cmds = compile_cmds, .results = compile_results }); YieldOnFence(&job_fence, job_count); //- Process shader compilation results { i32 i = 0; for (ShaderEntry *e = first_shader_entry; e; e = e->next) { RunCommandResult compile_result = compile_results[i]; OS_CommandResult cmd_result = compile_result.cmd_result; if (result->return_code == 0) { PushStringToList(arena, output, StringF(arena, "%F:%F %Fs", FmtString(F_GetFileName(gpu_out_file)), FmtString(e->name), FmtFloat(SecondsFromNs(compile_result.elapsed_ns)))); if (cmd_result.output.len > 0) { PushStringToList(arena, output, cmd_result.output); } result->return_code = cmd_result.code; } ++i; } } //- Build embedded shader archive if (result->return_code == 0) { StepParams arc_params = *params; StepResult arc_results = ZI; arc_params.flags = StepParamsFlag_CompileArc; arc_params.arc_store = shader_store_name; arc_params.arc_dir = shader_store_name; u32 job_count = 0; Fence job_fence = ZI; job_count += RunJob(Step, .fence = &job_fence, .sig.params = &arc_params, .sig.results = &arc_results); YieldOnFence(&job_fence, job_count); InheritStepResults(arena, result, 1, &arc_results); } } ////////////////////////////// //- Build embedded dirs if (flags & StepParamsFlag_CompileEmbeddedDirs) { //- Assemble embed dirs String arc_out_file = F_GetFull(arena, Lit("resources.arc")); { Struct(EmbeddedDir) { EmbeddedDir *next; String store_name; String dir_name; }; EmbeddedDir *first_dir_embed = 0; EmbeddedDir *last_dir_embed = 0; u64 dir_embeds_count = 0; { for (M_Entry *entry = params->flattened.first; entry->valid; entry = entry->next) { M_EntryKind kind = entry->kind; M_Token *entry_tok = entry->name_token; M_Token *arg0_tok = entry->arg_tokens[0]; M_Token *arg1_tok = entry->arg_tokens[1]; switch (kind) { default: break; case M_EntryKind_EmbedDir: { if (arg0_tok->valid && arg1_tok->valid) { String store_name = arg0_tok->s; String token_file = arg1_tok->file->name; String token_parent_dir = F_GetParentDir(token_file); String arg_dir = arg1_tok->s; String full = F_GetFullCrossPlatform(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_dir))); if (F_IsDir(full)) { EmbeddedDir *ed = PushStruct(arena, EmbeddedDir); ed->store_name = store_name; ed->dir_name = full; SllQueuePush(first_dir_embed, last_dir_embed, ed); ++dir_embeds_count; } else { String err = StringF(arena, "Directory '%F' not found", FmtString(full)); M_PushError(arena, errors, arg1_tok, err); } } else { M_PushError(arena, errors, entry_tok, Lit("Expected resource store & directory name")); } } break; } } } if (errors->count == 0) { StepParams *arc_params_array = PushStructs(arena, StepParams, dir_embeds_count); StepResult *arc_results_array = PushStructs(arena, StepResult, dir_embeds_count); { i32 i = 0; for (EmbeddedDir *ed = first_dir_embed; ed; ed = ed->next) { StepParams *arc_params = &arc_params_array[i]; *arc_params = *params; arc_params->flags = StepParamsFlag_CompileArc; arc_params->arc_store = ed->store_name; arc_params->arc_dir = ed->dir_name; ++i; } } u32 job_count = 0; Fence job_fence = ZI; job_count += RunJob(Step, .fence = &job_fence, .count = dir_embeds_count, .sig.params = arc_params_array, .sig.results = arc_results_array); YieldOnFence(&job_fence, job_count); InheritStepResults(arena, result, dir_embeds_count, arc_results_array); } } } ////////////////////////////// //- Build arc file if (flags & StepParamsFlag_CompileArc) { String store = params->arc_store; String dir_path = params->arc_dir; Struct(EntryNode) { EntryNode *next; String entry_name; String file_name; }; EntryNode *first_entry = 0; EntryNode *last_entry = 0; u64 entries_count = 0; StringList files = ZI; F_FilesFromDir(arena, &files, dir_path, F_IterFlag_Recurse); for (StringListNode *file_node = files.first; file_node; file_node = file_node->next) { String file_name = file_node->s; if (F_IsFile(file_name)) { String entry_name = file_name; if (entry_name.len > (dir_path.len + 1)) { entry_name.len -= dir_path.len + 1; entry_name.text += dir_path.len + 1; } entry_name = StringF(arena, "%F/%F", FmtString(store), FmtString(entry_name)); for (u64 i = 0; i < entry_name.len; ++i) { if (entry_name.text[i] == '\\') { entry_name.text[i] = '/'; } } EntryNode *en = PushStruct(arena, EntryNode); en->entry_name = entry_name; en->file_name = file_name; SllQueuePush(first_entry, last_entry, en); ++entries_count; } } String arc_out_file = StringF(arena, "%F.arc", FmtString(store)); String arc_contents = ZI; { BB_Buff bb = BB_AcquireBuff(Gibi(2)); BB_Writer bw = BB_WriterFromBuff(&bb); /* Write magic */ BB_WriteUBits(&bw, ResourceEmbeddedMagic, 64); /* Write header */ BB_WriteUBits(&bw, entries_count, 64); /* Reserve entries space */ u64 entry_size = 8 /* Name start */ + 8 /* Name end */ + 8 /* Data start */ + 8; /* Data end */ u8 *entries_start = BB_GetWrittenRaw(&bw) + BB_GetNumBytesWritten(&bw); u64 entries_size = entry_size * entries_count; String entries_str = STRING(entries_size, entries_start); BB_WriteSeekBytes(&bw, entries_size); BB_Buff entries_bb = BB_BuffFromString(entries_str); BB_Writer entries_bw = BB_WriterFromBuff(&entries_bb); /* Write entries */ for (EntryNode *en = first_entry; en; en = en->next) { /* TODO: Copy file data directly into archive file */ String file_data = F_DataFromFile(arena, en->file_name); /* Write name */ BB_WriteAlignBytes(&bw, 64); u64 name_start = BB_GetNumBytesWritten(&bw) + 1; BB_WriteString(&bw, en->entry_name); u64 name_len = BB_GetNumBytesWritten(&bw) - name_start; /* Write data */ BB_WriteAlignBytes(&bw, 64); /* FIXME: Why no +1 here? */ u64 data_start = BB_GetNumBytesWritten(&bw); BB_WriteBytes(&bw, file_data); u64 data_len = BB_GetNumBytesWritten(&bw) - data_start; /* Write entry */ BB_WriteUBits(&entries_bw, name_start, 64); BB_WriteUBits(&entries_bw, name_len, 64); BB_WriteUBits(&entries_bw, data_start, 64); BB_WriteUBits(&entries_bw, data_len, 64); } arc_contents.len = BB_GetNumBytesWritten(&bw); arc_contents.text = BB_GetWrittenRaw(&bw); } /* Write arc file */ F_ClearWrite(arc_out_file, arc_contents); PushStringToList(arena, output, StringF(arena, "%F (%F mb)", FmtString(F_GetFileName(arc_out_file)), FmtFloatP((f32)arc_contents.len / 1024 / 1024, 3))); if (IsPlatformWindows) { //- Generate rc file String rc_out_file = StringF(arena, "%F.rc", FmtString(store)); { RandState rs = ZI; StringList rc_out_lines = ZI; String arc_file_cp = F_GetFullCrossPlatform(arena, arc_out_file); String line = StringF(arena, "%F_%F RCDATA \"%F\"", FmtString(Lit(Stringize(W32_EmbeddedDataPrefix))), FmtHex(RandU64FromState(&rs)), FmtString(arc_file_cp)); PushStringToList(arena, &rc_out_lines, line); /* Write to file */ String rc_out = StringFromList(arena, rc_out_lines, Lit("\n")); F_ClearWrite(rc_out_file, rc_out); } //- Compile RC String res_out_file = StringF(arena, "%F.res", FmtString(store)); PushStringToList(arena, &result->obj_files, res_out_file); if (errors->count == 0) { String cmd = StringF(arena, "cmd /c rc.exe -nologo -fo %F %F", FmtString(res_out_file), FmtString(F_GetFull(arena, rc_out_file))); OS_CommandResult cmd_result = OS_RunCommand(arena, cmd); String cmd_output = TrimWhitespace(cmd_result.output); PushStringToList(arena, output, rc_out_file); if (cmd_output.len > 0) { PushStringToList(arena, output, cmd_output); } result->return_code = cmd_result.code; } } else { /* TODO: Compile object files using .incbin on non-windows platforms */ Panic(Lit("Non-windows embedded format not implemented")); } } ////////////////////////////// //- Run command if (flags & StepParamsFlag_RunCommand) { result->cmd_result = OS_RunCommand(arena, cmd); } result->elapsed_ns = TimeNs() - start_ns; } #endif //////////////////////////////////////////////////////////// //~ Stub funcs /* FIXME: Move and implement these */ Struct(CpuTopologyInfo) { i32 num_logical_cores; }; CpuTopologyInfo GetCpuTopologyInfo(void) { /* FIXME: Implement this */ CpuTopologyInfo res = ZI; res.num_logical_cores = 8; return res; } //////////////////////////////////////////////////////////// //~ Startup void BuildEntryPoint(WaveLaneCtx *lane, void *udata) { Arena *arena = PermArena(); M_ErrorList meta_parse_errors = ZI; i32 ret = 0; ////////////////////////////// //- Dirty check /* Return rebuild code if metaprogram is dirty */ if (lane->idx == 0) { /* Read old metahash */ u64 old_metahash = 0; if (F_IsFile(Lit("metahash.dat"))) { String hashes_str = F_DataFromFile(arena, Lit("metahash.dat")); if (hashes_str.len == sizeof(old_metahash)) { CopyBytes(&old_metahash, hashes_str.text, sizeof(old_metahash)); } OS_Rm(Lit("metahash.dat")); } /* Calculate new metahash */ u64 new_metahash = 0; { StringList check_files = ZI; F_FilesFromDir(arena, &check_files, Lit("../src/base"), F_IterFlag_Recurse); F_FilesFromDir(arena, &check_files, Lit("../src/meta"), F_IterFlag_Recurse); PushStringToList(arena, &check_files, Lit("../src/config.h")); for (StringListNode *n = check_files.first; n; n = n->next) { String file = n->s; new_metahash = RandU64FromSeeds(HashFnv64(new_metahash, file), OS_LastWriteTimestampFromPath(file)); } } /* Exit if metaprogram needs recompilation */ if (old_metahash == 0 || old_metahash == new_metahash) { F_ClearWrite(Lit("metahash.dat"), StringFromStruct(&new_metahash)); } else { EchoLine(Lit("Metaprogram is dirty")); ExitNow(MetaRebuildCode); } } ////////////////////////////// //- Command line if (lane->idx == 0) { String layer_name = ZI; CommandlineArg arg = CommandlineArgFromName(Lit("layer")); if (arg.name.len != 0) { layer_name = arg.value; } else { layer_name = StringFromCommandlineIdx(1); } if (layer_name.len == 0) { EchoLine(Lit("No layer supplied, assuming \"pp\" build")); layer_name = Lit("pp"); } EchoLine(StringF(arena, "Building \"%F\"", FmtString(layer_name))); cmdline.leaf_layer_name = layer_name; } ////////////////////////////// //- Args //- Generate compiler params CompilerParams cp = ZI; { //- Common { PushStringToList(arena, &cp.defs, Lit("-DIsConsoleApp=0")); PushStringToList(arena, &cp.defs, Lit("-DIsRtcEnabled=1")); PushStringToList(arena, &cp.defs, Lit("-DIsAsanEnabled=0")); PushStringToList(arena, &cp.defs, Lit("-DIsCrtlibEnabled=1")); PushStringToList(arena, &cp.defs, Lit("-DIsDebinfoEnabled=1")); PushStringToList(arena, &cp.defs, Lit("-DIsDeveloperModeEnabled=1")); PushStringToList(arena, &cp.defs, Lit("-DIsUnoptimized=1")); PushStringToList(arena, &cp.defs, Lit("-DIsTestingEnabled=0")); PushStringToList(arena, &cp.defs, Lit("-DIsHotSwappingEnabled=1")); } //- Msvc { PushStringToList(arena, &cp.compiler_only_flags_msvc, Lit("-diagnostics:column")); PushStringToList(arena, &cp.flags_msvc, Lit("-INCREMENTAL:NO")); PushStringToList(arena, &cp.flags_msvc, Lit("-nologo")); /* Optimization */ PushStringToList(arena, &cp.compiler_only_flags_msvc, Lit("-Od")); /* Debug info */ PushStringToList(arena, &cp.flags_msvc, Lit("-DEBUG:FULL")); PushStringToList(arena, &cp.compiler_only_flags_msvc, Lit("-Z7")); /* Enable warnings */ PushStringToList(arena, &cp.warnings_msvc, Lit("-W4")); PushStringToList(arena, &cp.warnings_msvc, Lit("-WX")); // PushStringToList(arena, &cp.warnings_msvc, Lit("-we4013")); /* function undefined; assuming extern returning int */ /* Disable warnings */ PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4244")); /* 'function': conversion from 'int' to 'f32', possible loss of data */ PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4201")); /* nonstandard extension used: nameless struct/union */ PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4324")); /* structure was padded due to alignment specifier */ PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4100")); /* unreferenced parameter */ PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4101")); /* unreferenced local variable */ PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4189")); /* local variable is initialized but not referenced */ PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4200")); /* nonstandard extension used: zero-sized array in struct/union */ PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4702")); /* unreachable code */ PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4305")); /* 'initializing': truncation from 'double' to 'f32' */ // PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4127")); /* conditional expression is constant */ // PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4820")); /* bytes padding added after data member */ // PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4464")); /* relative include path contains '..' */ // PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4061")); /* enumerator is not explicitly handled by a case label */ // PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4242")); /* conversion from X to Y, possible loss of data */ // PushStringToList(arena, &cp.warnings_msvc, Lit("-wd4388")); /* signed/unsigned mismatch */ // PushStringToList(arena, &cp.warnings_msvc, Lit("-wd5045")); /* Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified */ } //- Clang { PushStringToList(arena, &cp.flags_clang, Lit("-std=c99")); PushStringToList(arena, &cp.flags_clang, Lit("-fno-finite-loops")); PushStringToList(arena, &cp.flags_clang, Lit("-fno-strict-aliasing")); PushStringToList(arena, &cp.flags_clang, Lit("-g -gcodeview")); PushStringToList(arena, &cp.flags_clang, Lit("-O0")); PushStringToList(arena, &cp.flags_clang, Lit("-msse4.2")); /* Enable warnings */ PushStringToList(arena, &cp.warnings_clang, Lit("-Wall")); PushStringToList(arena, &cp.warnings_clang, Lit("-Werror")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wframe-larger-than=65536")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wmissing-prototypes")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wmissing-declarations")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wunused-variable")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wunused-but-set-variable")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wunused-parameter")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wimplicit-fallthrough")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wswitch")); /* Disable warnings */ PushStringToList(arena, &cp.warnings_clang, Lit("-Wno-initializer-overrides")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wno-microsoft-enum-forward-reference")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wno-unused-variable")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wno-unused-parameter")); PushStringToList(arena, &cp.warnings_clang, Lit("-Wno-incompatible-function-pointer-types")); } //- Dxc { PushStringToList(arena, &cp.flags_dxc, Lit("-Od")); PushStringToList(arena, &cp.flags_dxc, Lit("-Zi -Qembed_debug")); } } ////////////////////////////// //- Parse M_Layer flattened = ZI; if (lane->idx == 0) { //- Lex layers M_TokenFileList lexed = ZI; { StringList src_dirs = ZI; PushStringToList(arena, &src_dirs, Lit("../src")); lexed = M_TokensFromSrcDirs(arena, src_dirs); } //- Parse layers M_LayerList parsed = ZI; { parsed = M_LayersFromTokenFiles(arena, lexed); } //- Flatten layers { StringList starting_layer_names = ZI; PushStringToList(arena, &starting_layer_names, cmdline.leaf_layer_name); flattened = M_GetFlattenedEntries(arena, parsed, starting_layer_names); } M_AppendErrors(arena, &meta_parse_errors, flattened.errors); } ////////////////////////////// //- Prep /* * ## Phase 1 * * * | * ------------------------------------------------------------- * * ## Phase 2 * * | | * ------------------------------------------------------------- * * ## Phase 3 * * ------------------------------------------------------------- * * ## Phase 4 * * */ String shader_store_name = Lit("ShadersStore"); String c_out_file = F_GetFull(arena, StringF(arena, "%F_gen_c.c", FmtString(cmdline.leaf_layer_name))); String gpu_out_file = F_GetFull(arena, StringF(arena, "%F_gen_gpu.hlsl", FmtString(cmdline.leaf_layer_name))); ////////////////////////////// //- Generate final C file Struct(CGenData) { M_ErrorList errors; }; CGenData cgen = ZI; { String c_out_file = F_GetFull(arena, StringF(arena, "%F_gen_c.c", FmtString(cmdline.leaf_layer_name))); StringList c_store_lines = ZI; StringList c_shader_lines = ZI; StringList c_include_lines = ZI; StringList c_startup_lines = ZI; { for (M_Entry *entry = flattened.first; entry->valid; entry = entry->next) { M_EntryKind kind = entry->kind; M_Token *entry_tok = entry->name_token; M_Token *arg0_tok = entry->arg_tokens[0]; M_Token *arg1_tok = entry->arg_tokens[1]; switch (kind) { default: break; case M_EntryKind_EmbedDir: { if (arg0_tok->valid && arg1_tok->valid) { String store_name = arg0_tok->s; String token_file = arg1_tok->file->name; String token_parent_dir = F_GetParentDir(token_file); String arg_dir = arg1_tok->s; String full = F_GetFullCrossPlatform(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_dir))); if (F_IsDir(full)) { u64 hash = HashFnv64(Fnv64Basis, StringF(arena, "%F/", FmtString(store_name))); String line = StringF(arena, "ResourceStore %F = { 0x%F };", FmtString(store_name), FmtHex(hash)); PushStringToList(arena, &c_store_lines, line); } else { String err = StringF(arena, "Directory '%F' not found", FmtString(full)); M_PushError(arena, &cgen.errors, arg1_tok, err); } } else { M_PushError(arena, &cgen.errors, entry_tok, Lit("Expected resource store & directory name")); } } break; case M_EntryKind_VertexShader: case M_EntryKind_PixelShader: case M_EntryKind_ComputeShader: { if (arg0_tok->valid) { String shader_type = kind == M_EntryKind_VertexShader ? Lit("VertexShader") : kind == M_EntryKind_PixelShader ? Lit("PixelShader") : kind == M_EntryKind_ComputeShader ? Lit("ComputeShader") : Lit(""); String shader_name = arg0_tok->s; u64 hash = HashFnv64(Fnv64Basis, StringF(arena, "%F/%F", FmtString(shader_store_name), FmtString(shader_name))); String line = StringF(arena, "%F %F = { 0x%F };", FmtString(shader_type), FmtString(shader_name), FmtHex(hash)); PushStringToList(arena, &c_shader_lines, line); } else { M_PushError(arena, &cgen.errors, entry_tok, Lit("Expected shader name")); } } break; case M_EntryKind_IncludeC: { if (arg0_tok->valid) { String token_file = arg0_tok->file->name; String token_parent_dir = F_GetParentDir(token_file); String arg_file = arg0_tok->s; String full = F_GetFull(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_file))); if (F_IsFile(full)) { String line = StringF(arena, "#include \"%F\"", FmtString(full)); PushStringToList(arena, &c_include_lines, line); } else { String err = StringF(arena, "File '%F' not found", FmtString(full)); M_PushError(arena, &cgen.errors, arg0_tok, err); } } else { M_PushError(arena, &cgen.errors, entry_tok, Lit("Expected file name")); } } break; case M_EntryKind_Startup: { if (arg0_tok->valid) { String startup = arg0_tok->s; String line = StringF(arena, " %F();", FmtString(startup)); PushStringToList(arena, &c_startup_lines, line); } else { M_PushError(arena, &cgen.errors, entry_tok, Lit("Expected startup function name")); } } break; } } } if (cgen.errors.count == 0) { StringList c_out_lines = ZI; PushStringToList(arena, &c_out_lines, Lit("// Auto generated file")); /* Include base layer */ { String base_inc_path = F_GetFull(arena, Lit("../src/base/base_inc.h")); PushStringToList(arena, &c_out_lines, Lit("")); PushStringToList(arena, &c_out_lines, Lit("//- Base layer includes")); PushStringToList(arena, &c_out_lines, StringF(arena, "#include \"%F\"", FmtString(base_inc_path))); } /* Define resource stores */ if (c_store_lines.count > 0) { PushStringToList(arena, &c_out_lines, Lit("")); PushStringToList(arena, &c_out_lines, Lit("//- Resource stores")); for (StringListNode *n = c_store_lines.first; n; n = n->next) { PushStringToList(arena, &c_out_lines, n->s); } } /* Define shaders */ if (c_shader_lines.count > 0) { PushStringToList(arena, &c_out_lines, Lit("")); PushStringToList(arena, &c_out_lines, Lit("//- Shaders")); for (StringListNode *n = c_shader_lines.first; n; n = n->next) { PushStringToList(arena, &c_out_lines, n->s); } } /* Include dependency layers */ if (c_include_lines.count > 0) { PushStringToList(arena, &c_out_lines, Lit("")); PushStringToList(arena, &c_out_lines, Lit("//- Dependency graph includes")); for (StringListNode *n = c_include_lines.first; n; n = n->next) { PushStringToList(arena, &c_out_lines, n->s); } } /* Define StartupLayers */ { PushStringToList(arena, &c_out_lines, Lit("")); PushStringToList(arena, &c_out_lines, Lit("//- Startup")); PushStringToList(arena, &c_out_lines, Lit("void StartupLayers(void)")); PushStringToList(arena, &c_out_lines, Lit("{")); for (StringListNode *n = c_startup_lines.first; n; n = n->next) { PushStringToList(arena, &c_out_lines, n->s); } PushStringToList(arena, &c_out_lines, Lit("}")); } /* Write to file */ PushStringToList(arena, &c_out_lines, Lit("")); String c_out = StringFromList(arena, c_out_lines, Lit("\n")); F_ClearWrite(c_out_file, c_out); } } ////////////////////////////// //- Generate final HLSL file Enum(ShaderEntryKind) { ShaderEntryKind_VS, ShaderEntryKind_PS, ShaderEntryKind_CS, }; Struct(ShaderEntry) { ShaderEntry *next; ShaderEntryKind kind; String name; }; Struct(GpuGenData) { M_ErrorList errors; ShaderEntry *first_shader_entry; ShaderEntry *last_shader_entry; u64 shader_entries_count; }; GpuGenData gpugen = ZI; { /* Clear shader store */ OS_Mkdir(shader_store_name); { /* Remove all old shaders */ StringList files = ZI; F_FilesFromDir(arena, &files, shader_store_name, F_IterFlag_None); for (StringListNode *n = files.first; n; n = n->next) { String file = n->s; /* Safety check to prevent non-shader files from being removed */ if (StringEndsWith(file, Lit("VS")) || StringEndsWith(file, Lit("CS")) || StringEndsWith(file, Lit("PS"))) { OS_Rm(n->s); } else { /* Unexpected file in shader store */ Assert(0); } } } /* Generate GPU file & shader entries */ { StringList gpu_include_lines = ZI; { for (M_Entry *entry = flattened.first; entry->valid; entry = entry->next) { M_EntryKind kind = entry->kind; M_Token *entry_tok = entry->name_token; M_Token *arg0_tok = entry->arg_tokens[0]; M_Token *arg1_tok = entry->arg_tokens[1]; switch (kind) { default: break; case M_EntryKind_IncludeGpu: { if (arg0_tok->valid) { String token_file = arg0_tok->file->name; String token_parent_dir = F_GetParentDir(token_file); String arg_file = arg0_tok->s; String full = F_GetFull(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_file))); if (F_IsFile(full)) { String line = StringF(arena, "#include \"%F\"", FmtString(full)); PushStringToList(arena, &gpu_include_lines, line); } else { String err = StringF(arena, "File '%F' not found", FmtString(full)); M_PushError(arena, &gpugen.errors, arg0_tok, err); } } else { M_PushError(arena, &gpugen.errors, entry_tok, Lit("Expected file name")); } } break; case M_EntryKind_VertexShader: case M_EntryKind_PixelShader: case M_EntryKind_ComputeShader: { if (arg0_tok->valid) { ShaderEntryKind shader_kind = kind == M_EntryKind_VertexShader ? ShaderEntryKind_VS : kind == M_EntryKind_PixelShader ? ShaderEntryKind_PS : kind == M_EntryKind_ComputeShader ? ShaderEntryKind_CS : ShaderEntryKind_VS; String shader_name = arg0_tok->s; ShaderEntry *e = PushStruct(arena, ShaderEntry); e->kind = shader_kind; e->name = shader_name; SllQueuePush(gpugen.first_shader_entry, gpugen.last_shader_entry, e); ++gpugen.shader_entries_count; } else { M_PushError(arena, &gpugen.errors, entry_tok, Lit("Expected shader name")); } } break; } } } if (gpugen.errors.count == 0) { StringList gpu_out_lines = ZI; PushStringToList(arena, &gpu_out_lines, Lit("// Auto generated file")); /* Include base layer */ { String base_inc_path = F_GetFull(arena, Lit("../src/base/base_inc.h")); PushStringToList(arena, &gpu_out_lines, Lit("")); PushStringToList(arena, &gpu_out_lines, Lit("//- Base layer includes")); PushStringToList(arena, &gpu_out_lines, StringF(arena, "#include \"%F\"", FmtString(base_inc_path))); } /* Include dependency layers */ if (gpu_out_lines.count > 0) { PushStringToList(arena, &gpu_out_lines, Lit("")); PushStringToList(arena, &gpu_out_lines, Lit("//- Dependency graph includes")); for (StringListNode *n = gpu_include_lines.first; n; n = n->next) { PushStringToList(arena, &gpu_out_lines, n->s); } } /* Write to file */ PushStringToList(arena, &gpu_out_lines, Lit("")); String c_out = StringFromList(arena, gpu_out_lines, Lit("\n")); F_ClearWrite(gpu_out_file, c_out); } } } ////////////////////////////// //- Compile C Struct(CComp) { M_ErrorList errors; String obj_file; StringList output; i32 return_code; }; CComp ccomp = ZI; { ccomp.obj_file = StringF(arena, "%F_gen_c.obj", FmtString(cmdline.leaf_layer_name)); if (ccomp.errors.count == 0) { String cmd = StringF(arena, "cmd /c cl.exe /c %F -Fo:%F %F %F %F %F", FmtString(c_out_file), FmtString(ccomp.obj_file), FmtString(StringFromList(arena, cp.flags_msvc, Lit(" "))), FmtString(StringFromList(arena, cp.compiler_only_flags_msvc, Lit(" "))), FmtString(StringFromList(arena, cp.warnings_msvc, Lit(" "))), FmtString(StringFromList(arena, cp.defs, Lit(" ")))); OS_CommandResult cmd_result = OS_RunCommand(arena, cmd); String cmd_output = TrimWhitespace(cmd_result.output); if (cmd_output.len > 0) { PushStringToList(arena, &ccomp.output, cmd_output); } ccomp.return_code = cmd_result.code; } } ////////////////////////////// //- Compile shaders Struct(GpuComp) { StringList output; }; GpuComp gpucomp = ZI; for (ShaderEntry *e = gpugen.first_shader_entry; e; e = e->next) { String out_file = StringF(arena, "%F/%F", FmtString(shader_store_name), FmtString(e->name)); String target = e->kind == ShaderEntryKind_VS ? Lit("vs_6_6") : e->kind == ShaderEntryKind_PS ? Lit("ps_6_6") : e->kind == ShaderEntryKind_CS ? Lit("cs_6_6") : Lit("vs_6_6"); String compile_cmd = StringF(arena, "cmd /c dxc.exe -T %F -E %F -Fo %F %F %F %F", FmtString(target), FmtString(e->name), FmtString(out_file), FmtString(gpu_out_file), FmtString(StringFromList(arena, cp.defs, Lit(" "))), FmtString(StringFromList(arena, cp.flags_dxc, Lit(" ")))); OS_CommandResult cmd_result = OS_RunCommand(arena, compile_cmd); if (cmd_result.code == 0) { // f64 elapsed = SecondsFromNs(cmd_result.elapsed_ns); f64 elapsed = 0; PushStringToList(arena, &gpucomp.output, StringF(arena, "%F:%F %Fs", FmtString(F_GetFileName(gpu_out_file)), FmtString(e->name), FmtFloat(elapsed))); if (cmd_result.output.len > 0) { PushStringToList(arena, &gpucomp.output, cmd_result.output); } cmd_result.code = cmd_result.code; } } ////////////////////////////// //- Gather resource archive info Struct(ArcInfoEntry) { ArcInfoEntry *next; String dir_path; String store_name; String out_path; }; Struct(ArcInfoGen) { M_ErrorList errors; ArcInfoEntry *first_arc_entry; ArcInfoEntry *last_arc_entry; u64 arc_entries_count; }; ArcInfoGen arcinfogen = ZI; { /* Gather archives from embedded dirs */ for (M_Entry *entry = flattened.first; entry->valid; entry = entry->next) { M_EntryKind kind = entry->kind; M_Token *entry_tok = entry->name_token; M_Token *arg0_tok = entry->arg_tokens[0]; M_Token *arg1_tok = entry->arg_tokens[1]; switch (kind) { default: break; case M_EntryKind_EmbedDir: { if (arg0_tok->valid && arg1_tok->valid) { String store_name = arg0_tok->s; String token_file = arg1_tok->file->name; String token_parent_dir = F_GetParentDir(token_file); String arg_dir = arg1_tok->s; String full = F_GetFullCrossPlatform(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_dir))); if (F_IsDir(full)) { ArcInfoEntry *arc = PushStruct(arena, ArcInfoEntry); arc->store_name = store_name; arc->dir_path = full; arc->out_path = StringF(arena, "%F.arc", FmtString(store_name)); SllQueuePush(arcinfogen.first_arc_entry, arcinfogen.last_arc_entry, arc); ++arcinfogen.arc_entries_count; } else { String err = StringF(arena, "Directory '%F' not found", FmtString(full)); M_PushError(arena, &arcinfogen.errors, arg1_tok, err); } } else { M_PushError(arena, &arcinfogen.errors, entry_tok, Lit("Expected resource store & directory name")); } } break; } } /* Gather shader archive */ { ArcInfoEntry *arc = PushStruct(arena, ArcInfoEntry); arc->store_name = shader_store_name; arc->dir_path = F_GetFullCrossPlatform(arena, shader_store_name); arc->out_path = StringF(arena, "%F.arc", FmtString(shader_store_name)); SllQueuePush(arcinfogen.first_arc_entry, arcinfogen.last_arc_entry, arc); ++arcinfogen.arc_entries_count; } } ////////////////////////////// //- Generate resource archives for (ArcInfoEntry *entry = arcinfogen.first_arc_entry; entry; entry = entry->next) { String dir_path = entry->dir_path; String store_name = entry->store_name; String arc_path = entry->out_path; Struct(EntryNode) { EntryNode *next; String entry_name; String file_name; }; EntryNode *first_entry = 0; EntryNode *last_entry = 0; u64 entries_count = 0; StringList files = ZI; F_FilesFromDir(arena, &files, dir_path, F_IterFlag_Recurse); for (StringListNode *file_node = files.first; file_node; file_node = file_node->next) { String file_name = file_node->s; if (F_IsFile(file_name)) { String entry_name = file_name; if (entry_name.len > (dir_path.len + 1)) { entry_name.len -= dir_path.len + 1; entry_name.text += dir_path.len + 1; } entry_name = StringF(arena, "%F/%F", FmtString(store_name), FmtString(entry_name)); for (u64 i = 0; i < entry_name.len; ++i) { if (entry_name.text[i] == '\\') { entry_name.text[i] = '/'; } } EntryNode *en = PushStruct(arena, EntryNode); en->entry_name = entry_name; en->file_name = file_name; SllQueuePush(first_entry, last_entry, en); ++entries_count; } } String arc_contents = ZI; { BB_Buff bb = BB_AcquireBuff(Gibi(2)); BB_Writer bw = BB_WriterFromBuff(&bb); /* Write magic */ BB_WriteUBits(&bw, ResourceEmbeddedMagic, 64); /* Write header */ BB_WriteUBits(&bw, entries_count, 64); /* Reserve entries space */ u64 entry_size = 8 /* Name start */ + 8 /* Name end */ + 8 /* Data start */ + 8; /* Data end */ u8 *entries_start = BB_GetWrittenRaw(&bw) + BB_GetNumBytesWritten(&bw); u64 entries_size = entry_size * entries_count; String entries_str = STRING(entries_size, entries_start); BB_WriteSeekBytes(&bw, entries_size); BB_Buff entries_bb = BB_BuffFromString(entries_str); BB_Writer entries_bw = BB_WriterFromBuff(&entries_bb); /* Write entries */ for (EntryNode *en = first_entry; en; en = en->next) { /* TODO: Copy file data directly into archive file */ String file_data = F_DataFromFile(arena, en->file_name); /* Write name */ BB_WriteAlignBytes(&bw, 64); u64 name_start = BB_GetNumBytesWritten(&bw) + 1; BB_WriteString(&bw, en->entry_name); u64 name_len = BB_GetNumBytesWritten(&bw) - name_start; /* Write data */ BB_WriteAlignBytes(&bw, 64); /* FIXME: Why no +1 here? */ u64 data_start = BB_GetNumBytesWritten(&bw); BB_WriteBytes(&bw, file_data); u64 data_len = BB_GetNumBytesWritten(&bw) - data_start; /* Write entry */ BB_WriteUBits(&entries_bw, name_start, 64); BB_WriteUBits(&entries_bw, name_len, 64); BB_WriteUBits(&entries_bw, data_start, 64); BB_WriteUBits(&entries_bw, data_len, 64); } arc_contents.len = BB_GetNumBytesWritten(&bw); arc_contents.text = BB_GetWrittenRaw(&bw); } /* Write arc file */ F_ClearWrite(arc_path, arc_contents); // PushStringToList(arena, output, StringF(arena, "%F (%F mb)", FmtString(F_GetFileName(arc_path)), FmtFloatP((f32)arc_contents.len / 1024 / 1024, 3))); // if (IsPlatformWindows) // { // //- Generate rc file // String rc_out_file = StringF(arena, "%F.rc", FmtString(store_name)); // { // RandState rs = ZI; // StringList rc_out_lines = ZI; // String arc_file_cp = F_GetFullCrossPlatform(arena, arc_path); // String line = StringF(arena, "%F_%F RCDATA \"%F\"", FmtString(Lit(Stringize(W32_EmbeddedDataPrefix))), FmtHex(RandU64FromState(&rs)), FmtString(arc_file_cp)); // PushStringToList(arena, &rc_out_lines, line); // /* Write to file */ // String rc_out = StringFromList(arena, rc_out_lines, Lit("\n")); // F_ClearWrite(rc_out_file, rc_out); // } // //- Compile RC // String res_out_file = StringF(arena, "%F.res", FmtString(store_name)); // PushStringToList(arena, &result->obj_files, res_out_file); // if (errors->count == 0) // { // String cmd = StringF(arena, "cmd /c rc.exe -nologo -fo %F %F", FmtString(res_out_file), FmtString(F_GetFull(arena, rc_out_file))); // OS_CommandResult cmd_result = OS_RunCommand(arena, cmd); // String cmd_output = TrimWhitespace(cmd_result.output); // PushStringToList(arena, output, rc_out_file); // if (cmd_output.len > 0) // { // PushStringToList(arena, output, cmd_output); // } // result->return_code = cmd_result.code; // } // } // else // { // /* TODO: Compile object files using .incbin on non-windows platforms */ // Panic(Lit("Non-windows embedded format not implemented")); // } } ////////////////////////////// //- Compile resource archives Struct(ResComp) { String obj_file; StringList output; i32 return_code; }; ResComp *rescomps = PushStructs(arena, ResComp, arcinfogen.arc_entries_count); { if (IsPlatformWindows) { i32 rescomp_idx = 0; for (ArcInfoEntry *entry = arcinfogen.first_arc_entry; entry; entry = entry->next) { ResComp *rescomp = &rescomps[rescomp_idx]; String arc_path = entry->out_path; /* Generate RC file */ String rc_out_file = StringF(arena, "%F.rc", FmtString(entry->store_name)); { RandState rs = ZI; StringList rc_out_lines = ZI; String arc_file_cp = F_GetFullCrossPlatform(arena, arc_path); String line = StringF(arena, "%F_%F RCDATA \"%F\"", FmtString(Lit(Stringize(W32_EmbeddedDataPrefix))), FmtHex(RandU64FromState(&rs)), FmtString(arc_file_cp)); PushStringToList(arena, &rc_out_lines, line); /* Write to file */ String rc_out = StringFromList(arena, rc_out_lines, Lit("\n")); F_ClearWrite(rc_out_file, rc_out); } /* Compile RC file */ rescomp->obj_file = StringF(arena, "%F.res", FmtString(entry->store_name)); { String cmd = StringF(arena, "cmd /c rc.exe -nologo -fo %F %F", FmtString(rescomp->obj_file), FmtString(F_GetFull(arena, rc_out_file))); OS_CommandResult cmd_result = OS_RunCommand(arena, cmd); String cmd_output = TrimWhitespace(cmd_result.output); PushStringToList(arena, &rescomp->output, rc_out_file); if (cmd_output.len > 0) { PushStringToList(arena, &rescomp->output, cmd_output); } rescomp->return_code = cmd_result.code; } ++rescomp_idx; } } else { /* TODO: Compile object files using .incbin on non-windows platforms */ Panic(Lit("Non-windows embedded format not implemented")); } } ////////////////////////////// //- Link { String exe_file = StringF(arena, "%F.exe", FmtString(cmdline.leaf_layer_name)); { /* Wait for exe to become writable (wait for program to close) */ f32 timeout = 1.0; OS_File file = OS_OpenFile(exe_file, OS_FileFlag_Write, NsFromSeconds(timeout)); OS_CloseFile(file); } i64 link_elapsed_ns = 0; if (ret == 0) { i64 start_ns = TimeNs(); String obj_files_str = ZI; { StringList obj_files = ZI; PushStringToList(arena, &obj_files, ccomp.obj_file); for (u64 rescomp_idx = 0; rescomp_idx < arcinfogen.arc_entries_count; ++rescomp_idx) { ResComp *rescomp = &rescomps[rescomp_idx]; PushStringToList(arena, &obj_files, rescomp->obj_file); } obj_files_str = StringFromList(arena, obj_files, Lit(" ")); } String cmd = StringF(arena, "cmd /c link.exe %F /OUT:%F %F %F", FmtString(obj_files_str), FmtString(exe_file), FmtString(StringFromList(arena, cp.flags_msvc, Lit(" "))), FmtString(StringFromList(arena, cp.linker_only_flags_msvc, Lit(" ")))); OS_CommandResult result = OS_RunCommand(arena, cmd); String output = TrimWhitespace(result.output); if (output.len > 0) { EchoLine(output); } ret = result.code; link_elapsed_ns = TimeNs() - start_ns; } if (ret == 0) { EchoLine(StringF(arena, ">>>>> Linked in %Fs", FmtFloat(SecondsFromNs(link_elapsed_ns)))); } } ////////////////////////////// //- Compile & link #if 0 ret = meta_parse_errors.count > 0; if (ret == 0) { //- Compile StepParams params_array[3] = ZI; StepResult results_array[3] = ZI; StepParams *program_params = ¶ms_array[0]; StepResult *program_result = &results_array[0]; program_params->flags |= StepParamsFlag_CompileProgram; program_params->compiler_params = cp; program_params->flattened = flattened; StepParams *shader_params = ¶ms_array[1]; StepResult *shader_result = &results_array[1]; shader_params->flags |= StepParamsFlag_CompileShaders; shader_params->compiler_params = cp; shader_params->flattened = flattened; StepParams *resource_params = ¶ms_array[2]; StepResult *resource_result = &results_array[2]; resource_params->flags |= StepParamsFlag_CompileEmbeddedDirs; resource_params->compiler_params = cp; resource_params->flattened = flattened; u32 job_count = 0; Fence job_fence = ZI; job_count += RunJob(Step, .count = countof(params_array), .fence = &job_fence, .sig.params = params_array, .sig.results = results_array); YieldOnFence(&job_fence, job_count); //- Process compile step results /* Copy meta errors */ for (M_Error *e = program_result->meta_errors.first; e; e = e->next) M_PushError(arena, &errors, e->token, e->msg); for (M_Error *e = shader_result->meta_errors.first; e; e = e->next) M_PushError(arena, &errors, e->token, e->msg); for (M_Error *e = resource_result->meta_errors.first; e; e = e->next) M_PushError(arena, &errors, e->token, e->msg); ret = (ret != 0) ? ret : program_result->return_code; ret = (ret != 0) ? ret : shader_result->return_code; ret = (ret != 0) ? ret : resource_result->return_code; ret = (ret != 0) ? ret : errors.count > 0; { String program_output = StringFromList(arena, program_result->output_lines, Lit("\n")); String shader_output = StringFromList(arena, shader_result->output_lines, Lit("\n")); String resource_output = StringFromList(arena, resource_result->output_lines, Lit("\n")); EchoLine(StringF(arena, ">>>>> Built C in %Fs", FmtFloat(SecondsFromNs(program_result->elapsed_ns)))); if (program_output.len > 0) EchoLine(program_output); EchoLine(StringF(arena, ">>>>> Built shaders in %Fs", FmtFloat(SecondsFromNs(shader_result->elapsed_ns)))); if (shader_output.len > 0) EchoLine(shader_output); EchoLine(StringF(arena, ">>>>> Built resources in %Fs", FmtFloat(SecondsFromNs(resource_result->elapsed_ns)))); if (resource_output.len > 0) EchoLine(resource_output); } //- Link String exe_file = StringF(arena, "%F.exe", FmtString(cmdline.leaf_layer_name)); { /* Wait for exe to become writable (wait for program to close) */ f32 timeout = 1.0; OS_File file = OS_OpenFile(exe_file, OS_FileFlag_Write, NsFromSeconds(timeout)); OS_CloseFile(file); } i64 link_elapsed_ns = 0; if (ret == 0) { i64 start_ns = TimeNs(); String program_obj_files_str = StringFromList(arena, program_result->obj_files, Lit(" ")); String shader_obj_files_str = StringFromList(arena, shader_result->obj_files, Lit(" ")); String resource_obj_files_str = StringFromList(arena, resource_result->obj_files, Lit(" ")); String cmd = StringF(arena, "cmd /c link.exe %F %F %F /OUT:%F %F %F", FmtString(program_obj_files_str), FmtString(shader_obj_files_str), FmtString(resource_obj_files_str), FmtString(exe_file), FmtString(StringFromList(arena, cp.flags_msvc, Lit(" "))), FmtString(StringFromList(arena, cp.linker_only_flags_msvc, Lit(" ")))); OS_CommandResult result = OS_RunCommand(arena, cmd); String output = TrimWhitespace(result.output); if (output.len > 0) { EchoLine(output); } ret = result.code; link_elapsed_ns = TimeNs() - start_ns; } if (ret == 0) { EchoLine(StringF(arena, ">>>>> Linked in %Fs", FmtFloat(SecondsFromNs(link_elapsed_ns)))); } } #endif ////////////////////////////// //- Echo meta errors for (M_Error *e = meta_parse_errors.first; e; e = e->next) { String msg = ZI; M_Token *token = e->token; String token_file = token->file->name; String token_file_data = token->file->data; if (token_file.len > 0) { i64 token_pos = -1; if (token->s.len > 0 && token_file_data.len > 0 && token->s.text > token_file_data.text && token->s.text < (token_file_data.text + token_file_data.len)) { token_pos = token->s.text - token_file_data.text; } LineCol line_col = ZI; if (token_pos >= 0) { line_col = LineColFromPos(token_file_data, token_pos); } msg = StringF(arena, "%F:%F:%F: error: %F", FmtString(token_file), FmtSint(line_col.line), FmtSint(line_col.col), FmtString(e->msg)); } else { msg = StringF(arena, "error: %F", FmtString(e->msg)); } EchoLine(msg); } EchoLine(StringF(arena, "Runtime: %Fs", FmtFloat(SecondsFromNs(TimeNs())))); ExitNow(ret); } //////////////////////////////////////////////////////////// //~ @hookimpl Startup void StartupLayers(void) { OS_Startup(); CpuTopologyInfo cpu_info = GetCpuTopologyInfo(); // DispatchWave(cpu_info.num_logical_cores - 1, BuildEntryPoint, 0); DispatchWave(1, BuildEntryPoint, 0); }