#define Rtc 1 #include "buildit.h" /* ========================== * * Util * ========================== */ void Error(String msg) { SH_PrintF(Lit("ERROR: %F\n"), FmtStr(msg)); } /* ========================== * * Compile command * ========================== */ typedef struct StepListNode StepListNode; struct StepListNode { String name; String cmd; Bool silence_if_success; StepListNode *next; StepListNode *prev; }; typedef struct StepList StepList; struct StepList { StepListNode *first; StepListNode *last; Size count; }; void StepListAppend(Arena *arena, StepList *l, String name, String cmd, Bool silence_if_success) { StepListNode *n = ArenaPush(arena, StepListNode); n->name = name; n->cmd = cmd; n->silence_if_success = silence_if_success; DllPushBack(l->first, l->last, n); ++l->count; } /* ========================== * * Rc include * ========================== */ typedef struct RcIncludeListNode RcIncludeListNode; struct RcIncludeListNode { D_Tag tag; String rc_type; RcIncludeListNode *next; RcIncludeListNode *prev; }; typedef struct RcIncludeList RcIncludeList; struct RcIncludeList { RcIncludeListNode *first; RcIncludeListNode *last; Size count; }; void RcIncludeListAppend(Arena *arena, RcIncludeList *l, D_Tag tag, String rc_type) { RcIncludeListNode *n = ArenaPush(arena, RcIncludeListNode); n->tag = tag; n->rc_type = rc_type; DllPushBack(l->first, l->last, n); ++l->count; } /* ========================== * * Build * ========================== */ void OnBuild(StringList cli_args) { Arena arena = ArenaAlloc(Gigabyte(64)); /* ========================== * * Read args * ========================== */ String arg_outdir = Lit("build"); Bool arg_msvc = false; Bool arg_rtc = false; Bool arg_asan = false; Bool arg_crtlib = false; Bool arg_debinfo = false; Bool arg_developer = false; Bool arg_profiling = false; Bool arg_unoptimized = false; { typedef enum ArgState { ArgState_None, ArgState_OutputDir } ArgState; ArgState arg_state = ArgState_None; for (StringListNode *n = cli_args.first; n; n = n->next) { String arg = n->string; if (n != cli_args.first) { switch (arg_state) { case ArgState_OutputDir: { arg_outdir = arg; arg_state = ArgState_None; } break; default: { if (StringEqual(arg, Lit("-O"))) arg_state = ArgState_OutputDir; if (StringEqual(arg, Lit("-clang"))) arg_msvc = false; if (StringEqual(arg, Lit("-msvc"))) arg_msvc = true; if (StringEqual(arg, Lit("-rtc"))) arg_rtc = true; if (StringEqual(arg, Lit("-asan"))) arg_asan = true; if (StringEqual(arg, Lit("-crtlib"))) arg_crtlib = true; if (StringEqual(arg, Lit("-debinfo"))) arg_debinfo = true; if (StringEqual(arg, Lit("-developer"))) arg_developer = true; if (StringEqual(arg, Lit("-profiling"))) arg_profiling = true; if (StringEqual(arg, Lit("-unoptimized"))) arg_unoptimized = true; } break; } } } } String hist_path = OS_GetAbsPath(&arena, StringF(&arena, Lit("%F/.dephist"), FmtStr(arg_outdir))); String out_obj_dir_path = OS_GetAbsPath(&arena, StringF(&arena, Lit("%F/obj/"), FmtStr(arg_outdir))); String out_inc_dir_path = OS_GetAbsPath(&arena, StringF(&arena, Lit("%F/inc/"), FmtStr(arg_outdir))); String out_bin_dir_path = OS_GetAbsPath(&arena, StringF(&arena, Lit("%F/bin/"), FmtStr(arg_outdir))); if (!OS_DirExists(out_obj_dir_path)) { OS_CreateDirAtAbsPath(out_obj_dir_path); } if (!OS_DirExists(out_inc_dir_path)) { OS_CreateDirAtAbsPath(out_inc_dir_path); } if (!OS_DirExists(out_bin_dir_path)) { OS_CreateDirAtAbsPath(out_bin_dir_path); } SH_Print(Lit("------------------------------\n")); { String compiler = { 0 }; String compiler_loc = { 0 }; SH_CommandResult where_res = { 0 }; if (arg_msvc) { compiler = Lit("Msvc"); where_res = SH_RunCommandCaptureOutput(&arena, Lit("where cl.exe"), true); } else { compiler = Lit("Clang"); where_res = SH_RunCommandCaptureOutput(&arena, Lit("where clang.exe"), true); } compiler_loc = where_res.error ? Lit("Not found") : StringReplace(&arena, where_res.output, Lit("\n"), Lit("")); SH_PrintF(Lit("Compiler: %F (%F)\n"), FmtStr(compiler), FmtStr(compiler_loc)); } if (arg_asan) SH_Print(Lit("Asan Enabled\n")); if (arg_profiling) SH_Print(Lit("Profiling\n")); if (arg_developer) SH_Print(Lit("Developer build enabled\n")); SH_PrintF(Lit("Building to \"%F\"\n"), FmtStr(out_bin_dir_path)); SH_Print(Lit("------------------------------\n\n")); /* ========================== * * Constants * ========================== */ String dep_file_extension = Lit("d"); String obj_file_extension = Lit("obj"); Bool should_embed_res_dir = !arg_developer; Bool should_embed_in_rc = !!arg_msvc; D_Tag executable_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/PowerPlay.exe"), FmtStr(out_bin_dir_path))); D_Tag res_dir = D_DirTagFromPath(&arena, Lit("res")); D_Tag shaders_dir = D_DirTagFromPath(&arena, Lit("src/shaders")); D_Tag icon_file = D_FileTagFromPath(&arena, Lit("icon.ico")); D_Tag inc_src_file = D_FileTagFromPath(&arena, Lit("src/inc.c")); D_Tag rc_res_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/rc.res"), FmtStr(out_obj_dir_path))); #if 0 D_Tag pch_header_file = D_FileTagFromPath(&arena, Lit("src/common.h")); D_Tag pch_c_src_gen_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.c"), FmtStr(out_obj_dir_path))); D_Tag pch_cpp_src_gen_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.cpp"), FmtStr(out_obj_dir_path))); D_Tag pch_c_src_gen_obj_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.c.obj"), FmtStr(out_obj_dir_path))); D_Tag pch_cpp_src_gen_obj_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.cpp.obj"), FmtStr(out_obj_dir_path))); D_Tag pch_c_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.c.pch"), FmtStr(out_obj_dir_path))); D_Tag pch_cpp_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.cpp.pch"), FmtStr(out_obj_dir_path))); #endif /* ========================== * * Determine compiler args * ========================== */ StringList c_compile_args = { 0 }; StringList cpp_compile_args = { 0 }; StringList pch_c_compile_args = { 0 }; StringList pch_cpp_compile_args = { 0 }; StringList compile_and_link_args = { 0 }; StringList compile_args = { 0 }; StringList compile_warnings = { 0 }; StringList link_warnings = { 0 }; StringList link_args = { 0 }; StringList rc_compile_args = { 0 }; { if (arg_msvc) { /* Msvc */ StringListAppend(&arena, &c_compile_args, Lit("cl.exe /nologo /c \"%F\" /Fo\"%F\"")); StringListAppend(&arena, &cpp_compile_args, Lit("cl.exe /nologo /c \"%F\" /Fo\"%F\"")); StringListAppend(&arena, &pch_c_compile_args, Lit("cl.exe /nologo /c /Yc\"%F\" \"%F\"")); StringListAppend(&arena, &pch_cpp_compile_args, Lit("cl.exe /nologo /c /Yc\"%F\" \"%F\"")); StringListAppend(&arena, &rc_compile_args, Lit("rc /fo\"%F\" \"%F\"")); #if 0 StringListAppend(&arena, &c_compile_args, StringF(&arena, Lit("/Yu\"%F\" /FI\"%F\" /Fp\"%F\""), FmtStr(pch_header_file.full_path), FmtStr(pch_header_file.full_path), FmtStr(pch_c_file.full_path))); StringListAppend(&arena, &cpp_compile_args, StringF(&arena, Lit("/Yu\"%F\" /FI\"%F\" /Fp\"%F\""), FmtStr(pch_header_file.full_path), FmtStr(pch_header_file.full_path), FmtStr(pch_cpp_file.full_path))); StringListAppend(&arena, &pch_c_compile_args, StringF(&arena, Lit("/FI\"%F\" /Fp\"%F\" /Fo\"%F\""), FmtStr(pch_header_file.full_path), FmtStr(pch_c_file.full_path), FmtStr(pch_c_src_gen_obj_file.full_path))); StringListAppend(&arena, &pch_cpp_compile_args, StringF(&arena, Lit("/FI\"%F\" /Fp\"%F\" /Fo\"%F\""), FmtStr(pch_header_file.full_path), FmtStr(pch_cpp_file.full_path), FmtStr(pch_cpp_src_gen_obj_file.full_path))); #endif StringListAppend(&arena, &link_args, Lit("link.exe /nologo %F /OUT:\"%F\" /DEBUG:FULL /OPT:REF /OPT:ICF")); String warnings = Lit("/WX /Wall " "/options:strict " "/wd4820 /wd4201 /wd5220 /wd4514 /wd4244 /wd5045 /wd4242 /wd4061 /wd4189 /wd4723 /wd5246"); StringListAppend(&arena, &compile_warnings, warnings); StringListAppend(&arena, &link_warnings, Lit("/WX")); StringListAppend(&arena, &compile_args, StringF(&arena, Lit("/Fd\"%F\\\""), FmtStr(out_bin_dir_path))); } else { /* Clang */ StringListAppend(&arena, &c_compile_args, Lit("clang -xc -std=c99 -c %F -o %F -MD")); StringListAppend(&arena, &cpp_compile_args, Lit("clang -xc++ -std=c++20 -c %F -o %F -MD")); StringListAppend(&arena, &pch_c_compile_args, Lit("clang -xc-header -std=c99 -c %F -o %F -MD")); StringListAppend(&arena, &pch_cpp_compile_args, Lit("clang -xc++-header -std=c++20 -c %F -o %F -MD")); StringListAppend(&arena, &rc_compile_args, Lit("llvm-rc /fo\"%F\" \"%F\"")); #if 0 StringListAppend(&arena, &c_compile_args, StringF(&arena, Lit("-include-pch %F"), FmtStr(pch_c_file.full_path))); StringListAppend(&arena, &cpp_compile_args, StringF(&arena, Lit("-include-pch %F"), FmtStr(pch_cpp_file.full_path))); #endif StringListAppend(&arena, &link_args, Lit("clang %F")); StringListAppend(&arena, &link_args, StringF(&arena, Lit("-o \"%F\""), FmtStr(executable_file.full_path))); StringListAppend(&arena, &compile_and_link_args, Lit("-fuse-ld=lld-link " "-fno-strict-aliasing " "-fno-finite-loops " "-fwrapv " "-msse4.1 " "-msse4.2 ")); String warnings = Lit("-Weverything -Werror " "-Wframe-larger-than=65536 " "-Wno-unused-macros -Wno-gnu-zero-variadic-macro-arguments -Wno-documentation " "-Wno-old-style-cast -Wno-conversion -Wno-sign-conversion " "-Wno-declaration-after-statement -Wno-extra-semi -Wno-extra-semi-stmt " "-Wno-bad-function-cast -Wno-class-varargs -Wno-unreachable-code-break " "-Wno-cast-align -Wno-float-equal -Wno-zero-as-null-pointer-constant " "-Wno-cast-qual -Wno-missing-noreturn -Wno-missing-field-initializers " "-Wno-missing-braces -Wno-initializer-overrides " "-Wno-c99-extensions -Wno-c++98-compat-pedantic -Wno-c++98-compat " "-Wno-switch-enum -Wno-switch-default " "-Wno-reserved-identifier -Wno-reserved-macro-identifier " "-Wno-unsafe-buffer-usage -Wno-writable-strings " "" "-Wno-c11-extensions -Wno-gnu-anonymous-struct -Wno-nested-anon-types " "" "-Wno-double-promotion"); /* -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-parameter */ StringListAppend(&arena, &compile_warnings, warnings); StringListAppend(&arena, &link_warnings, warnings); } /* RTC */ if (arg_rtc) { if (!arg_crtlib) { Error(Lit("CRTLIB (C runtime library) Must be enabled when compiling with RTC (runtime checks)")); OS_Exit(1); } StringListAppend(&arena, &compile_args, Lit("-DRTC=1")); if (arg_msvc) { if (!arg_asan) { /* Enable /RTC option (not compatible with ASAN) */ StringListAppend(&arena, &compile_and_link_args, Lit("/RTCcsu")); } } else { /* Enable UBSan */ StringListAppend(&arena, &compile_and_link_args, Lit("-fsanitize=undefined -fsanitize-trap=all")); //StringListAppend(&compile_and_link_args, "-fsanitize=undefined"); } } /* CRTLIB */ if (arg_crtlib) { StringListAppend(&arena, &compile_args, Lit("-DCRTLIB=1")); } else { if (arg_msvc) { /* TODO */ Error(Lit("TODO\n")); OS_Exit(1); } else { StringListAppend(&arena, &compile_and_link_args, Lit("-mno-stack-arg-probe -fno-builtin -nostdlib")); } } /* Optimization */ if (arg_unoptimized) { StringListAppend(&arena, &compile_args, Lit("-DUNOPTIMIZED=1")); if (arg_msvc) { StringListAppend(&arena, &compile_args, Lit("/Od")); } else { StringListAppend(&arena, &compile_and_link_args, Lit("-O0")); } } else { if (arg_msvc) { StringListAppend(&arena, &compile_args, Lit("/O2")); StringListAppend(&arena, &link_args, Lit("/LTCG")); } else { StringListAppend(&arena, &compile_and_link_args, Lit("-O3 -flto")); } } /* Debug info */ if (arg_debinfo) { StringListAppend(&arena, &compile_args, Lit("-DDEBINFO=1")); if (arg_msvc) { StringListAppend(&arena, &compile_args, Lit("/JMC /Zi")); } else { StringListAppend(&arena, &compile_and_link_args, Lit("-g")); } } /* Address sanitizer */ if (arg_asan) { if (!arg_crtlib) { Error(Lit("CRTLIB (C runtime library) Must be enabled when compiling with asan enabled")); OS_Exit(1); } StringListAppend(&arena, &compile_args, Lit("-DASAN=1")); if (arg_msvc) { StringListAppend(&arena, &compile_args, Lit("/fsanitize=address")); } else { StringListAppend(&arena, &compile_and_link_args, Lit("-fsanitize=address -shared-libasan")); } } /* Developer mode */ if (arg_developer) { StringListAppend(&arena, &compile_args, Lit("-DDEVELOPER=1")); } /* Profiling */ if (arg_profiling) { if (!arg_crtlib) { Error(Lit("CRTLIB (C runtime library) Must be enabled when compiling with profiling enabled")); OS_Exit(1); } if (arg_msvc) { Error(Lit("MSVC not supported with profiling enabled (Profiling relies on Clang attributes)")); OS_Exit(1); } StringListAppend(&arena, &compile_args, Lit("-DPROFILING=1")); /* Tracy flags */ StringListAppend(&arena, &compile_args, Lit("-DTRACY_ENABLE=1")); StringListAppend(&arena, &compile_args, Lit("-DTRACY_CALLSTACK=5")); StringListAppend(&arena, &compile_args, Lit("-DTRACY_NO_SAMPLING -DTRACY_NO_SYSTEM_TRACING -DTRACY_NO_CALLSTACK")); /* Disable compile_warnings when compiling tracy client */ compile_warnings = (StringList) { 0 }; link_warnings = (StringList) { 0 }; } if (!arg_msvc) { String incbin_dir = StringReplace(&arena, out_inc_dir_path, Lit("\\"), Lit("/")); StringListAppend(&arena, &compile_args, StringF(&arena, Lit("-DINCBIN_DIR_RAW=\"%F\""), FmtStr(incbin_dir))); } #if 0 final_c_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), c_compile_args, compile_warnings, compile_and_link_args, compile_args); final_cpp_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), cpp_compile_args, compile_warnings, compile_and_link_args, compile_args); final_pch_c_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), pch_c_compile_args, compile_warnings, compile_and_link_args, compile_args); final_pch_cpp_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), pch_cpp_compile_args, compile_warnings, compile_and_link_args, compile_args); final_rc_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), rc_compile_args); final_link_args_fmt = StringFromStringLists(&arena, Lit(" "), link_args, link_warnings, compile_and_link_args); #endif } /* ========================== * * Read hist file * ========================== */ D_Hist hist = D_HistFromPath(&arena, hist_path); /* ========================== * * Assemble input files * ========================== */ D_TagList tar_input_dirs = { 0 }; D_Tag rc_input_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/rc.rc"), FmtStr(out_inc_dir_path)));; D_Tag pch_header_file = D_FileTagFromPath(&arena, Lit("src/common.h")); D_TagList src_input_files = { 0 }; /* Append tar input dirs */ D_TagListAppend(&arena, &tar_input_dirs, shaders_dir); if (should_embed_res_dir) { D_TagListAppend(&arena, &tar_input_dirs, res_dir); } /* Generate & append rc input file */ if (PlatformWindows) { D_AddDependency(rc_input_file, icon_file); if (should_embed_in_rc) { D_AddDependency(rc_input_file, shaders_dir); if (should_embed_res_dir) { D_AddDependency(rc_input_file, res_dir); } } if (D_IsDirty(rc_input_file, &hist)) { D_ClearWrite(rc_input_file, Lit("")); D_AppendWrite(rc_input_file, StringF(&arena, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(icon_file)), FmtStr(Lit("ICON")), FmtStr(D_GetName(icon_file)))); if (should_embed_in_rc) { D_AppendWrite(rc_input_file, StringF(&arena, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(shaders_dir)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(shaders_dir)))); if (should_embed_res_dir) { D_AppendWrite(rc_input_file, StringF(&arena, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(res_dir)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(res_dir)))); } } } } /* Append 'src' dir c & cpp files */ { D_Tag src_dir = D_DirTagFromPath(&arena, Lit("src")); D_TagList src_dir_files = D_GetDirContents(&arena, src_dir); for (D_TagListNode *n = src_dir_files.first; n; n = n->next) { Bool ignore = true; D_Tag file = n->tag; String path = file.full_path; String name = D_GetName(file); String extension = StringPathExtension(name); Bool is_dir = file.is_dir; Bool is_c = !is_dir && StringEqual(extension, Lit("c")); Bool is_cpp = !is_dir && !is_c && StringEqual(extension, Lit("cpp")); if (is_c || is_cpp) { if (StringBeginsWith(name, Lit("sys_")) || StringBeginsWith(name, Lit("renderer_")) || StringBeginsWith(name, Lit("playback_")) || StringBeginsWith(name, Lit("mp3_")) || StringBeginsWith(name, Lit("ttf_"))) { if (PlatformWindows) { ignore = !(StringEqual(name, Lit("sys_win32.c")) || StringEqual(name, Lit("renderer_d3d11.c")) || StringEqual(name, Lit("playback_wasapi.c")) || StringEqual(name, Lit("mp3_mmf.c")) || StringEqual(name, Lit("ttf_dwrite.cpp"))); } } else { ignore = false; } } if (!ignore) { D_TagListAppend(&arena, &src_input_files, file); D_Tag dep_file; { String name_no_extension = StringPathNoExtension(name); String dep_file_path = StringF(&arena, Lit("%F/%F.%F"), FmtStr(out_obj_dir_path), FmtStr(name_no_extension), FmtStr(dep_file_extension)); dep_file = D_FileTagFromPath(&arena, dep_file_path); } D_AddDependency(file, dep_file); { String dep_file_data = D_ReadAll(&arena, dep_file); StringList patterns = { 0 }; StringListAppend(&arena, &patterns, Lit("\r\n")); StringListAppend(&arena, &patterns, Lit("\n")); StringList lines = StringSplit(&arena, dep_file_data, patterns); for (StringListNode *dn = lines.first; dn; dn = dn->next) { String line = dn->string; line = StringReplace(&arena, line, Lit("\\ "), Lit(" ")); if (StringEndsWith(line, Lit(" \\"))) { line.len -= 2; } if (StringBeginsWith(line, Lit(" "))) { line.len -= 2; line.text += 2; } if (!StringEndsWith(line, Lit(":"))) { D_Tag tag = D_FileTagFromPath(&arena, line); if (!D_TagEqual(tag, file)) { D_AddDependency(file, tag); } } } } } } } /* ========================== * * Assemble build steps * ========================== */ StepList build_steps = { 0 }; D_TagList link_files = { 0 }; /* Build tar files */ for (D_TagListNode *n = tar_input_dirs.first; n; n = n->next) { D_Tag input_dir = n->tag; D_Tag tar_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(input_dir)))); D_AddDependency(tar_file, input_dir); if (should_embed_in_rc) { D_AddDependency(rc_res_file, tar_file); } else { D_AddDependency(inc_src_file, tar_file); } if (D_IsDirty(tar_file, &hist)) { String cmd = StringF(&arena, Lit("cd %F && tar cvf %F ."), FmtStr(input_dir.full_path), FmtStr(tar_file.full_path)); String step_name = StringF(&arena, Lit("%F -> %F"), FmtStr(D_GetName(input_dir)), FmtStr(D_GetName(tar_file))); StepListAppend(&arena, &build_steps, step_name, cmd, true); } } /* Build rc file */ { String rc_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), rc_compile_args); D_AddDependency(rc_res_file, rc_input_file); if (D_IsDirty(rc_res_file, &hist)) { String cmd = StringF(&arena, rc_compile_args_fmt, FmtStr(rc_res_file.full_path), FmtStr(rc_input_file.full_path)); String step_name = StringF(&arena, Lit("%F -> %F"), FmtStr(D_GetName(rc_input_file)), FmtStr(D_GetName(rc_res_file))); StepListAppend(&arena, &build_steps, step_name, cmd, true); } D_TagListAppend(&arena, &link_files, rc_res_file); } /* Build pch */ { String pch_c_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), pch_c_compile_args, compile_warnings, compile_and_link_args, compile_args); String pch_cpp_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), pch_cpp_compile_args, compile_warnings, compile_and_link_args, compile_args); D_Tag pch_c_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/%F.c_pch"), FmtStr(out_obj_dir_path), FmtStr(D_GetName(pch_header_file)))); D_Tag pch_cpp_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/%F.cpp_pch"), FmtStr(out_obj_dir_path), FmtStr(D_GetName(pch_header_file)))); D_AddDependency(pch_c_file, pch_header_file); D_AddDependency(pch_cpp_file, pch_header_file); if (arg_msvc) { #if 0 StringListAppend(&arena, &c_compile_args, StringF(&arena, Lit("/Yu\"%F\" /FI\"%F\" /Fp\"%F\""), FmtStr(pch_header_file.full_path), FmtStr(pch_header_file.full_path), FmtStr(pch_c_file.full_path))); StringListAppend(&arena, &cpp_compile_args, StringF(&arena, Lit("/Yu\"%F\" /FI\"%F\" /Fp\"%F\""), FmtStr(pch_header_file.full_path), FmtStr(pch_header_file.full_path), FmtStr(pch_cpp_file.full_path))); StringListAppend(&arena, &pch_c_compile_args, StringF(&arena, Lit("/FI\"%F\" /Fp\"%F\" /Fo\"%F\""), FmtStr(pch_header_file.full_path), FmtStr(pch_c_file.full_path), FmtStr(pch_c_src_gen_obj_file.full_path))); StringListAppend(&arena, &pch_cpp_compile_args, StringF(&arena, Lit("/FI\"%F\" /Fp\"%F\" /Fo\"%F\""), FmtStr(pch_header_file.full_path), FmtStr(pch_cpp_file.full_path), FmtStr(pch_cpp_src_gen_obj_file.full_path))); #endif } else { StringListAppend(&arena, &c_compile_args, StringF(&arena, Lit("-include-pch %F"), FmtStr(pch_c_file.full_path))); StringListAppend(&arena, &cpp_compile_args, StringF(&arena, Lit("-include-pch %F"), FmtStr(pch_cpp_file.full_path))); /* C */ { if (D_IsDirty(pch_c_file, &hist)) { String comp_cmd = StringF(&arena, pch_c_compile_args_fmt, FmtStr(pch_header_file.full_path), FmtStr(pch_c_file.full_path)); String step_name = StringF(&arena, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_c_file))); StepListAppend(&arena, &build_steps, step_name, comp_cmd, true); } } /* Cpp */ { if (D_IsDirty(pch_cpp_file, &hist)) { String comp_cmd = StringF(&arena, pch_cpp_compile_args_fmt, FmtStr(pch_header_file.full_path), FmtStr(pch_cpp_file.full_path)); String step_name = StringF(&arena, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_cpp_file))); StepListAppend(&arena, &build_steps, step_name, comp_cmd, true); } } } } /* Build src files */ { String c_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), c_compile_args, compile_warnings, compile_and_link_args, compile_args); String cpp_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), cpp_compile_args, compile_warnings, compile_and_link_args, compile_args); for (D_TagListNode *n = src_input_files.first; n; n = n->next) { D_Tag file = n->tag; String path = file.full_path; String name = D_GetName(file); String extension = StringPathExtension(name); Bool is_c = StringEqual(extension, Lit("c")); Bool is_cpp = !is_c && StringEqual(extension, Lit("cpp")); D_Tag obj_file; { String name_no_extension = StringPathNoExtension(name); String obj_file_path = StringF(&arena, Lit("%F/%F.%F"), FmtStr(out_obj_dir_path), FmtStr(name_no_extension), FmtStr(obj_file_extension)); obj_file = D_FileTagFromPath(&arena, obj_file_path); } D_AddDependency(obj_file, file); if (D_IsDirty(obj_file, &hist)) { String comp_cmd_fmt = is_c ? c_compile_args_fmt : cpp_compile_args_fmt; String comp_cmd = StringF(&arena, comp_cmd_fmt, FmtStr(file.full_path), FmtStr(obj_file.full_path)); String step_name = StringF(&arena, Lit("%F -> %F"), FmtStr(name), FmtStr(D_GetName(obj_file))); StepListAppend(&arena, &build_steps, step_name, comp_cmd, true); } D_TagListAppend(&arena, &link_files, obj_file); } } /* Link */ { String link_files_str = { 0 }; { StringList link_files_quoted_list = { 0 }; for (D_TagListNode *n = link_files.first; n; n = n->next) { D_Tag file = n->tag; String path = StringF(&arena, Lit("\"%F\""), FmtStr(file.full_path)); StringListAppend(&arena, &link_files_quoted_list, path); D_AddDependency(executable_file, file); } link_files_str = StringFromStringList(&arena, Lit(" "), link_files_quoted_list); } if (link_files_str.len > 0) { String link_args_fmt = StringFromStringLists(&arena, Lit(" "), link_args, link_warnings, compile_and_link_args); String cmd = StringF(&arena, link_args_fmt, FmtStr(link_files_str)); StepListAppend(&arena, &build_steps, Lit("Linking"), cmd, true); } } /* ========================== * * Execute build steps * * TODO: Parallelize * ========================== */ { Bool success = true; if (D_IsDirty(executable_file, &hist)) { Size step_i = 0; Size step_count = build_steps.count; for (StepListNode *n = build_steps.first; n; n = n->next) { ++step_i; String cmd = n->cmd; if (cmd.len > 0) { SH_PrintF(Lit("[%F/%F] %F\n"), FmtI64(step_i), FmtI64(step_count), FmtStr(n->name)); //SH_PrintF(Lit("%F\n"), FmtStr(cmd)); SH_CommandResult result = SH_RunCommandCaptureOutput(&arena, cmd, true); if (!n->silence_if_success || result.error != 0) { SH_PrintF(Lit("%F\n"), FmtStr(result.output)); } if (result.error != 0) { Assert(false); success = false; break; } } } } else { SH_Print(Lit("Nothing to build")); } if (!success) { Error(Lit("Build failed")); OS_Exit(1); } } /* ========================== * * Write hist file * ========================== */ D_WriteStateToHistFile(hist_path); #if 0 #if Rtc getchar(); #endif #endif }