1140 lines
42 KiB
C
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();
|
|
}
|