/* 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(""); 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(""); 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(); }