1147 lines
46 KiB
C
1147 lines
46 KiB
C
/* TODO: Move decls to meta.h */
|
|
|
|
#define MetaRebuildCode 1317212284
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Default base layer compiler definitions
|
|
|
|
#ifndef IsConsoleApp
|
|
# define IsConsoleApp 1
|
|
#endif
|
|
|
|
#ifndef RtcIsEnabled
|
|
# define RtcIsEnabled 1
|
|
#endif
|
|
|
|
#ifndef UnoptimizedIsEnabled
|
|
# define UnoptimizedIsEnabled 1
|
|
#endif
|
|
|
|
#ifndef AsanIsEnabled
|
|
# define AsanIsEnabled 0
|
|
#endif
|
|
|
|
#ifndef CrtlibIsEnabled
|
|
# define CrtlibIsEnabled 1
|
|
#endif
|
|
|
|
#ifndef DebinfoEnabled
|
|
# define DebinfoEnabled 1
|
|
#endif
|
|
|
|
#ifndef DeveloperIsEnabled
|
|
# define DeveloperIsEnabled 1
|
|
#endif
|
|
|
|
#ifndef ProfilingIsEnabled
|
|
# define ProfilingIsEnabled 0
|
|
#endif
|
|
|
|
#ifndef UnoptimizedIsEnabled
|
|
# define UnoptimizedIsEnabled 1
|
|
#endif
|
|
|
|
#ifndef TestsAreEnabled
|
|
# define TestsAreEnabled 0
|
|
#endif
|
|
|
|
#ifndef HotSwappingIsEnabled
|
|
# define HotSwappingIsEnabled 0
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Includes
|
|
|
|
//- Header files
|
|
#include "../prof/prof_inc.h"
|
|
#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"
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ 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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ OS command job
|
|
|
|
Struct(RunCommandResult)
|
|
{
|
|
OS_CommandResult cmd_result;
|
|
i64 elapsed_ns;
|
|
};
|
|
|
|
JobDecl(RunCommand, { String *cmds; RunCommandResult *results; });
|
|
JobDef(RunCommand, sig, id)
|
|
{
|
|
i64 start_ns = TimeNs();
|
|
Arena *arena = PermArena();
|
|
String cmd = sig->cmds[id];
|
|
RunCommandResult *result = &sig->results[id];
|
|
result->cmd_result = OS_RunCommand(arena, cmd);
|
|
result->elapsed_ns = TimeNs() - start_ns;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ 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
|
|
|
|
Enum(StepParamsFlag)
|
|
{
|
|
StepParamsFlag_None = 0,
|
|
StepParamsFlag_CompileProgram = (1 << 0),
|
|
StepParamsFlag_CompileShaders = (1 << 1),
|
|
StepParamsFlag_CompileEmbeddedDirs = (1 << 2),
|
|
StepParamsFlag_CompileArc = (1 << 3),
|
|
};
|
|
|
|
Struct(StepParams)
|
|
{
|
|
StepParamsFlag flags;
|
|
|
|
M_Layer flattened;
|
|
CompilerParams compiler_params;
|
|
|
|
String arc_store;
|
|
String arc_dir;
|
|
};
|
|
|
|
Struct(StepResult)
|
|
{
|
|
StringList obj_files;
|
|
StringList output_lines;
|
|
M_ErrorList meta_errors;
|
|
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);
|
|
}
|
|
}
|
|
|
|
JobDecl(Step, { StepParams *params; StepResult *results; });
|
|
JobDef(Step, sig, id)
|
|
{
|
|
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, Lit("pp_gen_c.c"));
|
|
{
|
|
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 prof_inc_path = F_GetFull(arena, Lit("../src/prof/prof_inc.h"));
|
|
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(prof_inc_path)));
|
|
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 = Lit("pp_gen_c.obj");
|
|
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 shaders_out_dir = shader_store_name;
|
|
OS_Mkdir(shaders_out_dir);
|
|
{
|
|
/* Remove all old shaders */
|
|
StringList files = ZI;
|
|
F_FilesFromDir(arena, &files, shaders_out_dir, 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, Lit("pp_gen_gpu.gpu"));
|
|
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;
|
|
QueuePush(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 prof_inc_path = F_GetFull(arena, Lit("../src/prof/prof_inc.h"));
|
|
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(prof_inc_path)));
|
|
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(shaders_out_dir),
|
|
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;
|
|
QueuePush(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;
|
|
QueuePush(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 (PlatformIsWindows)
|
|
{
|
|
//- 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"));
|
|
}
|
|
}
|
|
|
|
result->elapsed_ns = TimeNs() - start_ns;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Startup
|
|
|
|
JobDecl(Build, EmptySig);
|
|
JobDef(Build, _, __)
|
|
{
|
|
Arena *arena = PermArena();
|
|
M_ErrorList errors = ZI;
|
|
i32 ret = 0;
|
|
|
|
//////////////////////////////
|
|
//- Dirty check
|
|
|
|
//- Return rebuild code if metaprogram is dirty
|
|
{
|
|
/* 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/prof"), F_IterFlag_Recurse);
|
|
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);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////
|
|
//- Args
|
|
|
|
//- Unpack args
|
|
StringList args = GetCommandLineArgs();
|
|
for (StringListNode *n = args.first; n; n = n->next)
|
|
{
|
|
String arg = n->s;
|
|
}
|
|
|
|
//- Generate compiler params
|
|
CompilerParams cp = ZI;
|
|
{
|
|
//- Shared definitions
|
|
PushStringToList(arena, &cp.defs, Lit("-DIsConsoleApp=0"));
|
|
PushStringToList(arena, &cp.defs, Lit("-DRtcIsEnabled=1"));
|
|
PushStringToList(arena, &cp.defs, Lit("-DAsanIsEnabled=0"));
|
|
PushStringToList(arena, &cp.defs, Lit("-DCrtlibIsEnabled=1"));
|
|
PushStringToList(arena, &cp.defs, Lit("-DDebinfoEnabled=1"));
|
|
PushStringToList(arena, &cp.defs, Lit("-DDeveloperIsEnabled=1"));
|
|
PushStringToList(arena, &cp.defs, Lit("-DProfilingIsEnabled=0"));
|
|
PushStringToList(arena, &cp.defs, Lit("-DUnoptimizedIsEnabled=1"));
|
|
PushStringToList(arena, &cp.defs, Lit("-DTestsAreEnabled=0"));
|
|
PushStringToList(arena, &cp.defs, Lit("-DHotSwappingIsEnabled=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("-wd4127")); /* conditional expression is constant */
|
|
}
|
|
|
|
//- 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"));
|
|
}
|
|
|
|
//- Dxc
|
|
{
|
|
#if GPU_DEBUG
|
|
PushStringToList(arena, &cp.flags_dxc, Lit("-Od"));
|
|
PushStringToList(arena, &cp.flags_dxc, Lit("-Zi -Qembed_debug"));
|
|
#endif
|
|
// PushStringToList(arena, &cp.flags_dxc, Lit("-fno-caret-diagnostics"));
|
|
}
|
|
}
|
|
|
|
//////////////////////////////
|
|
//- Parse
|
|
|
|
//- 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
|
|
M_Layer flattened = ZI;
|
|
{
|
|
StringList starting_layer_names = ZI;
|
|
PushStringToList(arena, &starting_layer_names, Lit("pp"));
|
|
flattened = M_GetFlattenedEntries(arena, parsed, starting_layer_names);
|
|
}
|
|
M_AppendErrors(arena, &errors, flattened.errors);
|
|
ret = errors.count > 0;
|
|
|
|
//////////////////////////////
|
|
//- Compile & link
|
|
|
|
if (errors.count == 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 = Lit("pp.exe");
|
|
{
|
|
/* 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))));
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////
|
|
//- Echo errors
|
|
|
|
for (M_Error *e = 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);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ @hookdef Startup
|
|
|
|
void StartupLayers(void)
|
|
{
|
|
OS_Startup();
|
|
RunJob(Build, .pool = HyperPool());
|
|
}
|