power_play/src/meta/meta.c
2025-08-27 04:25:44 -05:00

1140 lines
42 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
////////////////////////////////
//~ Includes
#include "../base/base_inc.h"
#include "meta_os/meta_os_inc.h"
#include "meta_file/meta_file_inc.h"
#include "meta.h"
////////////////////////////////
//~ Util
void Echo(String msg)
{
HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (console_handle != INVALID_HANDLE_VALUE)
{
WriteFile(console_handle, msg.text, msg.len, 0, 0);
WriteFile(console_handle, "\n", 1, 0, 0);
}
}
Struct(LineCol)
{
i64 line;
i64 col;
};
LineCol LineColFromFilePos(String file, i64 pos)
{
TempArena scratch = BeginScratchNoConflict();
LineCol result = ZI;
String data = F_DataFromFile(scratch.arena, file);
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;
}
////////////////////////////////
//~ Lay tokenizer
Struct(L_Token)
{
b32 bof;
b32 eof;
i64 pos;
String s;
L_Token *prev;
L_Token *next;
};
Struct(L_TokenList)
{
L_Token *first;
L_Token *last;
};
Enum(L_TokenizerMode)
{
L_TokenizerMode_None,
L_TokenizerMode_SingleLineComment,
L_TokenizerMode_MultiLineComment
};
/* FIXME: Ignore comments */
L_TokenList L_TokensFromString(Arena *arena, String s)
{
L_TokenList result = ZI;
/* Push start nil token */
{
L_Token *beginning = PushStruct(arena, L_Token);
beginning->prev = beginning; /* Self link */
beginning->pos = -1;
result.first = beginning;
result.last = beginning;
beginning->bof = 1;
}
/* Tokenize */
u64 pos = 0;
String cur_text = ZI;
cur_text.text = s.text;
L_TokenizerMode mode = L_TokenizerMode_None;
while (pos < s.len)
{
u8 c = s.text[pos];
switch (mode)
{
case L_TokenizerMode_None:
{
if (c == ' ' || c == '\n' || c == 0 || pos == s.len - 1)
{ /* Whitespace */
if (cur_text.len > 0)
{
L_Token *t = PushStruct(arena, L_Token);
t->s = PushString(arena, cur_text);
t->pos = (cur_text.text - s.text);
if (result.last)
{
result.last->next = t;
}
else
{
result.first = t;
}
t->prev = result.last;
result.last = t;
cur_text.len = 0;
}
pos += 1;
}
else if (c == '/' && (pos + 1) < s.len && s.text[pos + 1] == '/')
{ /* Start single line comment */
mode = L_TokenizerMode_SingleLineComment;
pos += 2;
}
else if (c == '/' && (pos + 1) < s.len && s.text[pos + 1] == '*')
{ /* Start multi line comment */
mode = L_TokenizerMode_MultiLineComment;
pos += 2;
}
else
{
if (cur_text.len == 0)
{
cur_text.text = &s.text[pos];
}
++cur_text.len;
pos += 1;
}
} break;
case L_TokenizerMode_SingleLineComment:
{
if (c == '\n')
{ /* End single line comment */
mode = L_TokenizerMode_None;
pos += 1;
}
else
{
pos += 1;
}
} break;
case L_TokenizerMode_MultiLineComment:
{
if (c == '*' && (pos + 1) < s.len && s.text[pos + 1] == '/')
{ /* End multi line comment */
mode = L_TokenizerMode_None;
pos += 2;
}
else
{
pos += 1;
}
} break;
};
}
/* Push end nil token */
{
L_Token *ending = PushStruct(arena, L_Token);
ending->next = ending; /* Self link */
ending->prev = result.last;
ending->pos = s.len;
result.last->next = ending;
result.last = ending;
ending->eof = 1;
}
return result;
}
////////////////////////////////
//~ Blob
Enum(L_ParseMode)
{
L_ParseMode_None,
L_ParseMode_LayerName,
L_ParseMode_Dep,
L_ParseMode_IncludeC,
L_ParseMode_IncludeGpu,
L_ParseMode_ShaderVS,
L_ParseMode_ShaderPS,
L_ParseMode_ShaderCS,
L_ParseMode_Startup,
L_ParseMode_EmbedDir_StoreName,
L_ParseMode_EmbedDir_DirName,
L_ParseMode_DefaultWindowsImpl,
};
Enum(L_BlobTopoColor)
{
L_BlobTopoColor_None,
L_BlobTopoColor_Queued,
L_BlobTopoColor_Visiting,
L_BlobTopoColor_Finished,
};
Struct(L_BlobItem)
{
L_BlobItem *next;
String s;
String store_name;
String token_file;
i64 token_pos;
};
Struct(L_BlobItemList)
{
L_BlobItem *first;
L_BlobItem *last;
i64 count;
};
Struct(L_Blob)
{
i64 id;
L_Blob *next;
L_BlobItemList names;
L_BlobItemList deps;
L_BlobItemList c_includes;
L_BlobItemList gpu_includes;
L_BlobItemList vertex_shaders;
L_BlobItemList pixel_shaders;
L_BlobItemList compute_shaders;
L_BlobItemList startup_functions;
L_BlobItemList embed_dirs;
L_BlobItemList default_windows_impls;
L_BlobItemList errors;
};
Struct(L_ParseResult)
{
L_Blob *first_blob;
L_Blob *last_blob;
Dict *blobs_dict;
u64 blobs_count;
};
L_BlobItem *L_PushBlobItem(Arena *arena, L_BlobItemList *list, String token_file, i64 token_pos, String item_string)
{
L_BlobItem *item = PushStruct(arena, L_BlobItem);
item->s = item_string;
item->token_file = token_file;
item->token_pos = token_pos;
SllQueuePush(list->first, list->last, item);
++list->count;
return item;
}
L_ParseResult L_ParseBlobsFromPaths(Arena *arena, StringList paths)
{
TempArena scratch = BeginScratch(arena);
L_ParseResult result = ZI;
result.blobs_dict = InitDict(arena, 1024);
for (StringListNode *dir_node = paths.first; dir_node; dir_node = dir_node->next)
{
String dir = dir_node->s;
StringList files = ZI;
F_FilesFromDir(scratch.arena, &files, dir, F_IterFlag_Recurse);
for (StringListNode *file_node = files.first; file_node; file_node = file_node->next)
{
if (EqString(F_ExtensionFromFile(file_node->s), Lit("lay")))
{
String file = PushString(arena, file_node->s);
String data = F_DataFromFile(scratch.arena, file);
L_TokenList tokens = L_TokensFromString(scratch.arena, data);
L_ParseMode mode = L_ParseMode_None;
L_Blob *blob = PushStruct(arena, L_Blob);
SllQueuePush(result.first_blob, result.last_blob, blob);
blob->id = result.blobs_count++;
String store_name = Lit("<None>");
for (L_Token *token = tokens.first->next; token; token = token->next)
{
String s = token->s;
if (token->eof)
{
if (mode != L_ParseMode_None)
{
L_PushBlobItem(arena, &blob->errors, file, token->pos, Lit("Unexpected end of file"));
}
break;
}
if (mode == L_ParseMode_None)
{
if (EqString(s, Lit("@Layer"))) mode = L_ParseMode_LayerName;
else if (EqString(s, Lit("@Dep"))) mode = L_ParseMode_Dep;
else if (EqString(s, Lit("@IncludeC"))) mode = L_ParseMode_IncludeC;
else if (EqString(s, Lit("@IncludeGpu"))) mode = L_ParseMode_IncludeGpu;
else if (EqString(s, Lit("@ShaderVS"))) mode = L_ParseMode_ShaderVS;
else if (EqString(s, Lit("@ShaderPS"))) mode = L_ParseMode_ShaderPS;
else if (EqString(s, Lit("@ShaderCS"))) mode = L_ParseMode_ShaderCS;
else if (EqString(s, Lit("@Startup"))) mode = L_ParseMode_Startup;
else if (EqString(s, Lit("@EmbedDir"))) mode = L_ParseMode_EmbedDir_StoreName;
else if (EqString(s, Lit("@DefaultWindowsImpl"))) mode = L_ParseMode_DefaultWindowsImpl;
else
{
/* Encountered unexpected token, stop parsing */
L_PushBlobItem(arena, &blob->errors, file, token->pos, StringF(arena, "Unexpected token '%F'", FmtString(s)));
break;
}
}
else
{
switch (mode)
{
case L_ParseMode_None:
{
if (EqString(s, Lit("@Layer"))) mode = L_ParseMode_LayerName;
else if (EqString(s, Lit("@Dep"))) mode = L_ParseMode_Dep;
else
{
L_PushBlobItem(arena, &blob->names, file, token->pos, s);
}
} break;
case L_ParseMode_LayerName:
{
L_PushBlobItem(arena, &blob->names, file, token->pos, s);
mode = L_ParseMode_None;
} break;
case L_ParseMode_Dep:
{
L_PushBlobItem(arena, &blob->deps, file, token->pos, s);
mode = L_ParseMode_None;
} break;
case L_ParseMode_IncludeC:
{
L_PushBlobItem(arena, &blob->c_includes, file, token->pos, s);
mode = L_ParseMode_None;
} break;
case L_ParseMode_IncludeGpu:
{
L_PushBlobItem(arena, &blob->gpu_includes, file, token->pos, s);
mode = L_ParseMode_None;
} break;
case L_ParseMode_ShaderVS:
{
L_PushBlobItem(arena, &blob->vertex_shaders, file, token->pos, s);
mode = L_ParseMode_None;
} break;
case L_ParseMode_ShaderPS:
{
L_PushBlobItem(arena, &blob->pixel_shaders, file, token->pos, s);
mode = L_ParseMode_None;
} break;
case L_ParseMode_ShaderCS:
{
L_PushBlobItem(arena, &blob->compute_shaders, file, token->pos, s);
mode = L_ParseMode_None;
} break;
case L_ParseMode_Startup:
{
L_PushBlobItem(arena, &blob->startup_functions, file, token->pos, s);
mode = L_ParseMode_None;
} break;
case L_ParseMode_EmbedDir_StoreName:
{
store_name = s;
mode = L_ParseMode_EmbedDir_DirName;
} break;
case L_ParseMode_EmbedDir_DirName:
{
String embed_dir_name = s;
L_BlobItem *item = L_PushBlobItem(arena, &blob->embed_dirs, file, token->pos, embed_dir_name);
item->store_name = PushString(arena, store_name);
store_name = Lit("<None>");
mode = L_ParseMode_None;
} break;
case L_ParseMode_DefaultWindowsImpl:
{
L_PushBlobItem(arena, &blob->default_windows_impls, file, token->pos, s);
mode = L_ParseMode_None;
} break;
}
}
}
if (mode != L_ParseMode_None)
{
L_PushBlobItem(arena, &blob->errors, file, tokens.last->pos, Lit("Unexpected end of file"));
}
if (blob->names.count == 1)
{
String name = blob->names.first->s;
u64 hash = HashFnv64(Fnv64Basis, name);
DictEntry *entry = EnsureDictEntry(arena, result.blobs_dict, hash);
if (entry->value == 0)
{
entry->value = (u64)blob;
}
else
{
L_PushBlobItem(arena, &blob->errors, file, blob->names.first->token_pos, StringF(arena, "Layer '%F' already exists", FmtString(name)));
}
}
else if (blob->names.count == 0)
{
L_PushBlobItem(arena, &blob->errors, file, tokens.first->pos, Lit("Mising layer name"));
}
else
{
for (L_BlobItem *item = blob->names.first; item; item = item->next)
{
L_PushBlobItem(arena, &blob->errors, item->token_file, item->token_pos, Lit("Layer contains multiple names"));
}
}
}
}
}
EndScratch(scratch);
return result;
}
////////////////////////////////
//~ Topo
Struct(L_TopoItem)
{
L_TopoItem *next;
String s;
String store_name;
String token_file;
i64 token_pos;
};
Struct(L_TopoItemList)
{
L_TopoItem *first;
L_TopoItem *last;
u64 count;
};
Struct(L_Topo)
{
L_TopoItemList c_includes;
L_TopoItemList startup_functions;
L_TopoItemList embed_dirs;
L_TopoItemList errors;
};
L_TopoItem *L_PushTopoItem(Arena *arena, L_TopoItemList *list, String token_file, i64 token_pos, String item_string)
{
L_TopoItem *item = PushStruct(arena, L_TopoItem);
item->s = item_string;
item->token_file = token_file;
item->token_pos = token_pos;
SllQueuePush(list->first, list->last, item);
++list->count;
return item;
}
L_Topo L_TopoFromLayerName(Arena *arena, StringList starting_layer_names, StringList paths)
{
TempArena scratch = BeginScratch(arena);
L_Topo result = ZI;
/* Parse blobs */
L_ParseResult parsed = L_ParseBlobsFromPaths(scratch.arena, paths);
Struct(Node)
{
Node *next;
L_Blob *blob;
i32 state; /* 0 = enter, 1 = exit */
};
/* Topological sort via post-order DFS */
Node *first_stack = 0;
Node *first_post = 0;
Node *last_post = 0;
i8 *seen_blobs = PushStructs(scratch.arena, i8, parsed.blobs_count);
i8 *entered_blobs = PushStructs(scratch.arena, i8, parsed.blobs_count);
{
for (StringListNode *n = starting_layer_names.first; n; n = n->next)
{
String s = n->s;
L_Blob *starting_blob = (L_Blob *)DictValueFromHash(parsed.blobs_dict, HashFnv64(Fnv64Basis, s));
if (starting_blob)
{
Node *n = PushStruct(scratch.arena, Node);
n->blob = starting_blob;
n->state = 0;
SllStackPush(first_stack, n);
}
else
{
L_PushTopoItem(arena, &result.errors, Lit(""), 0, StringF(arena, "Starting layer not found with name '%F'", FmtString(s)));
}
}
while (first_stack)
{
L_Blob *blob = 0;
Node *stack_node = first_stack;
blob = stack_node->blob;
SllStackPop(first_stack);
if (stack_node->state == 0)
{ /* Enter */
if (!seen_blobs[blob->id])
{
if (entered_blobs[blob->id])
{ /* Cycle detected */
Node *first_cycle = 0;
{
i8 *cycle_blobs_seen = PushStructs(scratch.arena, i8, parsed.blobs_count);
{
Node *cycle_node = PushStruct(scratch.arena, Node);
cycle_node->blob = blob;
SllStackPush(first_cycle, cycle_node);
}
for (Node *n = first_stack; n; n = n->next)
{
if (entered_blobs[n->blob->id])
{
if (!cycle_blobs_seen[n->blob->id])
{
Node *cycle_node = PushStruct(scratch.arena, Node);
cycle_node->blob = n->blob;
SllStackPush(first_cycle, cycle_node);
cycle_blobs_seen[n->blob->id] = 1;
}
}
if (n->blob == blob) break;
}
}
StringList cycle_names_list = ZI;
for (Node *cycle = first_cycle; cycle; cycle = cycle->next)
{
String name = ZI;
if (cycle->blob->names.count > 0) name = cycle->blob->names.first->s;
PushStringToList(scratch.arena, &cycle_names_list, name);
}
String cycle_names_str = StringFromList(scratch.arena, cycle_names_list, Lit(" -> "));
String name = ZI;
String token_file = ZI;
i64 token_pos = 0;
if (blob->names.count > 0)
{
name = blob->names.first->s;
token_file = PushString(arena, blob->names.first->token_file);
token_pos = blob->names.first->token_pos;
}
if (cycle_names_list.count > 1)
{
L_PushTopoItem(arena, &result.errors, token_file, token_pos, StringF(arena,
"Cyclic dependency detected while processing dependencies for layer '%F' (%F)",
FmtString(name),
FmtString(cycle_names_str)));
}
else
{
L_PushTopoItem(arena, &result.errors, token_file, token_pos, StringF(arena,
"Cyclic dependency detected while processing dependencies for layer '%F'",
FmtString(name)));
}
}
else
{
/* Push platform impls */
{
L_BlobItemList platform_impls = ZI;
if (PlatformIsWindows)
{
platform_impls = blob->default_windows_impls;
}
for (L_BlobItem *impl_item = platform_impls.first; impl_item; impl_item = impl_item->next)
{
String impl_name = impl_item->s;
u64 impl_hash = HashFnv64(Fnv64Basis, impl_name);
L_Blob *impl = (L_Blob *)DictValueFromHash(parsed.blobs_dict, impl_hash);
if (impl)
{
if (!seen_blobs[impl->id])
{
Node *n = PushStruct(scratch.arena, Node);
n->blob = impl;
n->state = 0;
SllStackPush(first_stack, n);
}
}
else
{
String file = PushString(arena, impl_item->token_file);
L_PushTopoItem(arena, &result.errors, file, impl_item->token_pos, StringF(arena, "Layer '%F' not found", FmtString(impl_name)));
}
}
}
/* Push blob exit */
{
entered_blobs[blob->id] = 1;
Node *n = PushStruct(scratch.arena, Node);
n->blob = blob;
n->state = 1;
SllStackPush(first_stack, n);
}
/* Push blob deps */
for (L_BlobItem *dep_item = blob->deps.first; dep_item; dep_item = dep_item->next)
{
String dep_name = dep_item->s;
u64 dep_hash = HashFnv64(Fnv64Basis, dep_name);
L_Blob *dep = (L_Blob *)DictValueFromHash(parsed.blobs_dict, dep_hash);
if (dep)
{
if (!seen_blobs[dep->id])
{
Node *n = PushStruct(scratch.arena, Node);
n->blob = dep;
n->state = 0;
SllStackPush(first_stack, n);
}
}
else
{
String file = PushString(arena, dep_item->token_file);
L_PushTopoItem(arena, &result.errors, file, dep_item->token_pos, StringF(arena, "Layer '%F' not found", FmtString(dep_name)));
}
}
}
}
}
else
{ /* Exit */
entered_blobs[blob->id] = 0;
seen_blobs[blob->id] = 1;
SllQueuePush(first_post, last_post, stack_node);
}
}
}
/* Process post blobs into result */
for (Node *n = first_post; n; n = n->next)
{
L_Blob *blob = n->blob;
/* Errors */
for (L_BlobItem *bitem = blob->errors.first; bitem; bitem = bitem->next)
{
String file = PushString(arena, bitem->token_file);
String s = PushString(arena, bitem->s);
L_PushTopoItem(arena, &result.errors, file, bitem->token_pos, s);
}
/* C includes */
for (L_BlobItem *bitem = blob->c_includes.first; bitem; bitem = bitem->next)
{
String file = PushString(arena, bitem->token_file);
String s = PushString(arena, bitem->s);
L_PushTopoItem(arena, &result.c_includes, file, bitem->token_pos, s);
}
/* Startup funcs */
for (L_BlobItem *bitem = blob->startup_functions.first; bitem; bitem = bitem->next)
{
String file = PushString(arena, bitem->token_file);
String s = PushString(arena, bitem->s);
L_PushTopoItem(arena, &result.startup_functions, file, bitem->token_pos, s);
}
/* Embed dirs */
for (L_BlobItem *bitem = blob->embed_dirs.first; bitem; bitem = bitem->next)
{
String file = PushString(arena, bitem->token_file);
String s = PushString(arena, bitem->s);
L_TopoItem *titem = L_PushTopoItem(arena, &result.embed_dirs, file, bitem->token_pos, s);
titem->store_name = PushString(arena, bitem->store_name);
}
}
/* Process errors of untouched blobs */
for (L_Blob *blob = parsed.first_blob; blob; blob = blob->next)
{
if (!seen_blobs[blob->id] && blob->errors.count > 0)
{
for (L_BlobItem *bitem = blob->errors.first; bitem; bitem = bitem->next)
{
String file = PushString(arena, bitem->token_file);
String s = PushString(arena, bitem->s);
L_PushTopoItem(arena, &result.errors, file, bitem->token_pos, s);
}
}
}
EndScratch(scratch);
return result;
}
////////////////////////////////
//~ Error
Struct(Error)
{
String s;
String file;
i64 pos;
Error *next;
};
Struct(ErrorList)
{
Error *first;
Error *last;
i64 count;
};
Error *PushError(Arena *arena, ErrorList *list, String file, i64 pos, String s)
{
Error *e = PushStruct(arena, Error);
e->s = s;
e->file = file;
e->pos = pos;
SllQueuePush(list->first, list->last, e);
++list->count;
return e;
}
////////////////////////////////
//~ Task
JobDecl(TaskJob, {i32 _;});
JobDef(TaskJob, sig, id)
{
}
////////////////////////////////
//~ Build
void StartupMeta(void)
{
Arena *arena = AcquireArena(Gibi(64));
ErrorList errors = ZI;
//- Unpack args
StringList args = GetCommandLineArgs();
for (StringListNode *n = args.first; n; n = n->next)
{
String arg = n->s;
}
//- Return 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
{
Echo(Lit("Metaprogram is dirty"));
ExitNow(MetaRebuildCode);
}
}
//- Extract layer topography
StringList src_dirs = ZI;
StringList starting_layer_names = ZI;
PushStringToList(arena, &src_dirs, Lit("../src"));
PushStringToList(arena, &starting_layer_names, Lit("pp"));
L_Topo topo = L_TopoFromLayerName(arena, starting_layer_names, src_dirs);
//- Process topo errors
if (topo.errors.count > 0)
{
StringList topo_errors = ZI;
u64 max_topo_errors = 50;
for (L_TopoItem *item = topo.errors.first; item && topo_errors.count < max_topo_errors; item = item->next)
{
PushError(arena, &errors, item->token_file, item->token_pos, item->s);
}
}
//- Generate C file
String c_out_file = F_GetFull(arena, Lit("pp_gen.c"));
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("//- Include base layer"));
String line = StringF(arena, "#include \"%F\"", FmtString(base_inc_path));
PushStringToList(arena, &c_out_lines, line);
}
/* Include dependency layers */
{
PushStringToList(arena, &c_out_lines, Lit(""));
PushStringToList(arena, &c_out_lines, Lit("//- Include dependencies"));
for (L_TopoItem *item = topo.c_includes.first; item; item = item->next)
{
String parent = F_GetParentDir(item->token_file);
String file = StringF(arena, "%F%F", FmtString(parent), FmtString(item->s));
if (F_IsFile(file))
{
String full = F_GetFull(arena, file);
String line = StringF(arena, "#include \"%F\"", FmtString(full));
PushStringToList(arena, &c_out_lines, line);
}
else
{
String e = StringF(arena, "File '%F' not found", FmtString(file));
PushError(arena, &errors, item->token_file, item->token_pos, e);
}
}
}
/* 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 (L_TopoItem *item = topo.startup_functions.first; item; item = item->next)
{
String line = StringF(arena, " %F();", FmtString(item->s));
PushStringToList(arena, &c_out_lines, line);
}
PushStringToList(arena, &c_out_lines, Lit("}"));
PushStringToList(arena, &c_out_lines, Lit(""));
}
/* Write to file */
String c_out = StringFromList(arena, c_out_lines, Lit("\n"));
F_ClearWrite(c_out_file, c_out);
}
//- Generate archive file
String archive_out_file = F_GetFull(arena, Lit("embeds.arc"));
{
for (L_TopoItem *item = topo.embed_dirs.first; item; item = item->next)
{
String parent = F_GetParentDir(item->token_file);
String dir = StringF(arena, "%F%F", FmtString(parent), FmtString(item->s));
if (F_IsDir(dir))
{
String full = F_GetFull(arena, dir);
String line = StringF(arena, "#include \"%F\"", FmtString(full));
// PushStringToList(arena, &c_out_lines, line);
}
else
{
String e = StringF(arena, "Dir '%F' not found", FmtString(dir));
PushError(arena, &errors, item->token_file, item->token_pos, e);
}
}
}
//- Echo meta errors
i32 ret = errors.count > 0;
for (Error *e = errors.first; e; e = e->next)
{
String s = e->s;
String file = e->file;
i64 pos = e->pos;
String error = ZI;
if (file.len > 0)
{
LineCol line_col = LineColFromFilePos(file, pos);
error = StringF(arena,
"%F:%F:%F: error: %F",
FmtString(file),
FmtSint(line_col.line),
FmtSint(line_col.col),
FmtString(s));
}
else
{
error = StringF(arena, "error: %F", FmtString(s));
}
Echo(error);
}
//- Generate compiler flags
StringList compiler_defs = ZI;
StringList warning_flags_msvc = ZI;
StringList warning_flags_clang = ZI;
StringList flags_msvc = ZI;
StringList flags_clang = ZI;
StringList compiler_flags_msvc = ZI;
StringList compiler_flags_clang = ZI;
StringList linker_flags_msvc = ZI;
StringList linker_flags_clang = ZI;
{
//- Shared definitions
PushStringToList(arena, &compiler_defs, Lit("-DIsConsoleApp=0"));
PushStringToList(arena, &compiler_defs, Lit("-DRtcIsEnabled=1"));
PushStringToList(arena, &compiler_defs, Lit("-DAsanIsEnabled=0"));
PushStringToList(arena, &compiler_defs, Lit("-DCrtlibIsEnabled=1"));
PushStringToList(arena, &compiler_defs, Lit("-DDebinfoEnabled=1"));
PushStringToList(arena, &compiler_defs, Lit("-DDeveloperIsEnabled=1"));
PushStringToList(arena, &compiler_defs, Lit("-DProfilingIsEnabled=0"));
PushStringToList(arena, &compiler_defs, Lit("-DUnoptimizedIsEnabled=1"));
PushStringToList(arena, &compiler_defs, Lit("-DTestsAreEnabled=0"));
//- Msvc
{
PushStringToList(arena, &flags_msvc, Lit("-nologo"));
PushStringToList(arena, &compiler_flags_msvc, Lit("-diagnostics:column"));
/* Debug info */
PushStringToList(arena, &flags_msvc, Lit("-DEBUG:FULL"));
PushStringToList(arena, &compiler_flags_msvc, Lit("-Z7"));
/* Enable warnings */
PushStringToList(arena, &warning_flags_msvc, Lit("-W4"));
PushStringToList(arena, &warning_flags_msvc, Lit("-WX"));
// PushStringToList(arena, &warning_flags_msvc, Lit("-we4013")); /* function undefined; assuming extern returning int */
/* Disable warnings */
PushStringToList(arena, &warning_flags_msvc, Lit("-wd4244")); /* 'function': conversion from 'int' to 'f32', possible loss of data */
PushStringToList(arena, &warning_flags_msvc, Lit("-wd4201")); /* nonstandard extension used: nameless struct/union */
PushStringToList(arena, &warning_flags_msvc, Lit("-wd4324")); /* structure was padded due to alignment specifier */
PushStringToList(arena, &warning_flags_msvc, Lit("-wd4100")); /* unreferenced parameter */
PushStringToList(arena, &warning_flags_msvc, Lit("-wd4101")); /* unreferenced local variable */
PushStringToList(arena, &warning_flags_msvc, Lit("-wd4189")); /* local variable is initialized but not referenced */
PushStringToList(arena, &warning_flags_msvc, Lit("-wd4200")); /* nonstandard extension used: zero-sized array in struct/union */
}
//- Clang
{
PushStringToList(arena, &flags_clang, Lit("-std=c99"));
PushStringToList(arena, &flags_clang, Lit("-fno-finite-loops"));
PushStringToList(arena, &flags_clang, Lit("-fno-strict-aliasing"));
PushStringToList(arena, &flags_clang, Lit("-g -gcodeview"));
PushStringToList(arena, &flags_clang, Lit("-O0"));
PushStringToList(arena, &flags_clang, Lit("-msse4.2"));
/* Enable warnings */
PushStringToList(arena, &warning_flags_clang, Lit("-Wall"));
PushStringToList(arena, &warning_flags_clang, Lit("-Werror"));
PushStringToList(arena, &warning_flags_clang, Lit("-Wframe-larger-than=65536"));
PushStringToList(arena, &warning_flags_clang, Lit("-Wmissing-prototypes"));
PushStringToList(arena, &warning_flags_clang, Lit("-Wmissing-declarations"));
PushStringToList(arena, &warning_flags_clang, Lit("-Wunused-variable"));
PushStringToList(arena, &warning_flags_clang, Lit("-Wunused-but-set-variable"));
PushStringToList(arena, &warning_flags_clang, Lit("-Wunused-parameter"));
PushStringToList(arena, &warning_flags_clang, Lit("-Wimplicit-fallthrough"));
PushStringToList(arena, &warning_flags_clang, Lit("-Wswitch"));
/* Disable warnings */
PushStringToList(arena, &warning_flags_clang, Lit("-Wno-initializer-overrides"));
PushStringToList(arena, &warning_flags_clang, Lit("-Wno-microsoft-enum-forward-reference"));
}
}
//- Compile C
String c_out_obj_file = Lit("pp_gen.obj");
if (ret == 0)
{
Echo(Lit("[Compiling]"));
String cmd = StringF(arena,
"cl /c %F -Fo:%F %F %F %F %F",
FmtString(c_out_file),
FmtString(c_out_obj_file),
FmtString(StringFromList(arena, flags_msvc, Lit(" "))),
FmtString(StringFromList(arena, compiler_flags_msvc, Lit(" "))),
FmtString(StringFromList(arena, warning_flags_msvc, Lit(" "))),
FmtString(StringFromList(arena, compiler_defs, Lit(" "))));
OS_CommandResult result = OS_RunCommand(arena, cmd);
String output = TrimWhitespace(result.output);
Echo(output);
ret = result.code;
}
//- Link
String exe_file = Lit("pp.exe");
if (ret == 0)
{
Echo(Lit("[Linking]"));
String cmd = StringF(arena,
"link %F /OUT:%F %F %F",
FmtString(c_out_obj_file),
FmtString(exe_file),
FmtString(StringFromList(arena, flags_msvc, Lit(" "))),
FmtString(StringFromList(arena, linker_flags_msvc, Lit(" "))));
OS_CommandResult result = OS_RunCommand(arena, cmd);
String output = TrimWhitespace(result.output);
Echo(output);
ret = result.code;
}
#if 0
//- Generate compiler cmds
String msvc_cmd_str = ZI;
String clang_cmd_str = ZI;
/* Msvc */
{
String compiler_str = StringF(arena,
"%F %F %F %F",
FmtString(StringFromList(arena, flags_msvc, Lit(" "))),
FmtString(StringFromList(arena, compiler_flags_msvc, Lit(" "))),
FmtString(StringFromList(arena, warning_flags_msvc, Lit(" "))),
FmtString(StringFromList(arena, compiler_defs, Lit(" "))));
String linker_str = StringF(arena,
"%F %F",
FmtString(StringFromList(arena, flags_msvc, Lit(" "))),
FmtString(StringFromList(arena, linker_flags_msvc, Lit(" "))));
msvc_cmd_str = StringF(arena, "\"cl\" %F %F -link %F", FmtString(c_out_file), FmtString(compiler_str), FmtString(linker_str));
}
/* Clang */
{
String compiler_str = StringF(arena,
"%F %F %F %F",
FmtString(StringFromList(arena, flags_clang, Lit(" "))),
FmtString(StringFromList(arena, compiler_flags_clang, Lit(" "))),
FmtString(StringFromList(arena, warning_flags_clang, Lit(" "))),
FmtString(StringFromList(arena, compiler_defs, Lit(" "))));
clang_cmd_str = StringF(arena, "\"clang\" %F %F", FmtString(c_out_file), FmtString(compiler_str));
}
//- Compile C
if (ret == 0) {
Echo(Lit("Compiling"));
String cmd_str = msvc_cmd_str;
// String cmd_str = clang_cmd_str;
OS_CommandResult result = OS_RunCommand(arena, cmd_str);
String output = result.output;
output = Trim(output, Lit("\n"));
output = Trim(output, Lit("\r"));
output = Trim(output, Lit("\n"));
Echo(output);
if (result.code == 0)
{
Echo(Lit("Compilation succeeded"));
}
else
{
Echo(Lit("Compilation failed"));
}
ret = result.code;
}
#endif
ExitNow(ret);
}
////////////////////////////////
//~ @hookdef Startup
void StartupLayers(void)
{
OS_Startup();
StartupMeta();
}