power_play/build.c

641 lines
28 KiB
C

#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;
String link_file_path;
Bool silence_output_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, String link_file_path, Bool silence_output_if_success)
{
StepListNode *n = ArenaPush(arena, StepListNode);
n->name = name;
n->cmd = cmd;
n->link_file_path = link_file_path;
n->silence_output_if_success = silence_output_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 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"));
if (arg_msvc) {
SH_Print(Lit("* Compiler: Msvc *\n"));
} else {
SH_Print(Lit("* Compiler: Clang *\n"));
}
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
* ========================== */
StepList compile_command_list = { 0 };
String obj_file_extension = Lit("obj");
D_Tag executable = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/PowerPlay.exe"), FmtStr(out_bin_dir_path)));
D_Tag pdb = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/PowerPlay.pdb"), FmtStr(out_bin_dir_path)));
D_Tag pch_header = D_FileTagFromPath(&arena, Lit("src/common.h"));
D_Tag pch_c_output = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.c.pch"), FmtStr(out_obj_dir_path)));
D_Tag pch_c_obj_output = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.c.obj"), FmtStr(out_obj_dir_path)));
D_Tag pch_cpp_output = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.cpp.pch"), FmtStr(out_obj_dir_path)));
D_Tag pch_cpp_obj_output = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.cpp.obj"), FmtStr(out_obj_dir_path)));
#if 0
#if 1
D_Tag pch_h_input = D_FileTagFromPath(&arena, Lit("src/common.h"));
D_Tag pch_c_src_gen = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.c"), FmtStr(out_obj_dir_path)));
D_Tag pch_cpp_src_gen = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.cpp"), FmtStr(out_obj_dir_path)));
D_Tag pch_c_output = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.c.pch"), FmtStr(out_obj_dir_path)));
D_Tag pch_cpp_output = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common.cpp.pch"), FmtStr(out_obj_dir_path)));
#else
D_Tag pch_input = D_FileTagFromPath(&arena, Lit("src/common.h"));
D_Tag pch_c_output = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common_c.pch"), FmtStr(out_obj_dir_path)));
D_Tag pch_cpp_output = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/common_cpp.pch"), FmtStr(out_obj_dir_path)));
#endif
#endif
/* ========================== *
* Determine compiler args
* ========================== */
String final_c_compile_args_fmt = { 0 };
String final_cpp_compile_args_fmt = { 0 };
String final_pch_c_compile_args_fmt = { 0 };
String final_pch_cpp_compile_args_fmt = { 0 };
String final_link_args_fmt = { 0 };
{
StringList compile_warnings = { 0 };
StringList compile_and_link_args = { 0 };
StringList compile_args = { 0 };
StringList c_compile_args = { 0 };
StringList cpp_compile_args = { 0 };
StringList pch_c_compile_args = { 0 };
StringList pch_cpp_compile_args = { 0 };
StringList link_warnings = { 0 };
StringList link_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, &c_compile_args, StringF(&arena, Lit("/Yu\"%F\" /FI\"%F\" /Fp\"%F\""), FmtStr(pch_header.full_path), FmtStr(pch_header.full_path), FmtStr(pch_c_output.full_path)));
StringListAppend(&arena, &cpp_compile_args, StringF(&arena, Lit("/Yu\"%F\" /FI\"%F\" /Fp\"%F\""), FmtStr(pch_header.full_path), FmtStr(pch_header.full_path), FmtStr(pch_cpp_output.full_path)));
StringListAppend(&arena, &pch_c_compile_args, StringF(&arena, Lit("/FI\"%F\" /Fp\"%F\" /Fo\"%F\""), FmtStr(pch_header.full_path), FmtStr(pch_c_output.full_path), FmtStr(pch_c_obj_output.full_path)));
StringListAppend(&arena, &pch_cpp_compile_args, StringF(&arena, Lit("/FI\"%F\" /Fp\"%F\" /Fo\"%F\""), FmtStr(pch_header.full_path), FmtStr(pch_cpp_output.full_path), FmtStr(pch_cpp_obj_output.full_path)));
StringListAppend(&arena, &link_args, Lit("link.exe /nologo %F"));
StringListAppend(&arena, &link_args, StringF(&arena, Lit("/OUT:\"%F\" /PDB:\"%F\" /DEBUG:FULL /OPT:REF /OPT:ICF /NODEFAULTLIB:LIBCMT"), FmtStr(executable.full_path), FmtStr(pdb.full_path)));
String warnings = Lit("/WX /Wall "
"/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"));
StringListAppend(&arena, &cpp_compile_args, Lit("clang -xc++ -std=c++20 -c %F -o %F"));
StringListAppend(&arena, &pch_c_compile_args, Lit("clang -xc-header -std=c99 -c %F -o %F"));
StringListAppend(&arena, &pch_cpp_compile_args, Lit("clang -xc++-header -std=c++20 -c %F -o %F"));
StringListAppend(&arena, &c_compile_args, StringF(&arena, Lit("-include-pch %F"), FmtStr(pch_c_output.full_path)));
StringListAppend(&arena, &cpp_compile_args, StringF(&arena, Lit("-include-pch %F"), FmtStr(pch_cpp_output.full_path)));
StringListAppend(&arena, &link_args, Lit("clang %F"));
StringListAppend(&arena, &link_args, StringF(&arena, Lit("-o \"%F\""), FmtStr(executable.full_path)));
StringListAppend(&arena,
&compile_and_link_args,
Lit("-fuse-ld=lld-link "
"-nostdlib "
"-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"));
String crt_libs = { 0 };
if (arg_msvc) {
crt_libs = arg_rtc ?
Lit("msvcrtd.lib ucrtd.lib msvcprtd.lib vcruntimed.lib") :
Lit("msvcrt.lib ucrt.lib msvcprt.lib vcruntime.lib");
} else {
crt_libs = arg_rtc ?
Lit("-lmsvcrtd -lucrtd -lmsvcprtd -lvcruntimed") :
Lit("-lmsvcrt -lucrt -lmsvcprt -lvcruntime");
}
StringListAppend(&arena, &link_args, crt_libs);
} 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"));
}
}
/* 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("/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)));
}
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_link_args_fmt = StringFromStringLists(&arena, Lit(" "), link_args, link_warnings, compile_and_link_args);
}
/* ========================== *
* Generate embeddable tar files
* ========================== */
Bool embed_in_rc = !!arg_msvc;
D_Tag res_dir = D_DirTagFromPath(&arena, Lit("res"));
D_Tag shaders_dir = D_DirTagFromPath(&arena, Lit("src/shaders"));
D_Tag inc_file = D_FileTagFromPath(&arena, Lit("src/inc.c"));
RcIncludeList rc_includes = { 0 };
/* Generate shaders tar */
D_Tag shaders_tar = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/shaders.tar"), FmtStr(out_inc_dir_path)));
D_AddDependency(shaders_tar, shaders_dir);
if (embed_in_rc) {
RcIncludeListAppend(&arena, &rc_includes, shaders_tar, Lit("RCDATA"));
} else {
D_AddDependency(inc_file, shaders_tar);
}
if (D_IsDirty(shaders_tar)) {
String tar_cmd = StringF(&arena, Lit("cd %F && tar cvf %F ."), FmtStr(shaders_dir.full_path), FmtStr(shaders_tar.full_path));
String step_name = StringF(&arena, Lit("Generating archive: %F"), FmtStr(D_GetName(shaders_tar)));
StepListAppend(&arena, &compile_command_list, step_name, tar_cmd, (String) { 0 }, true);
}
/* Generate res tar */
if (!arg_developer) {
D_Tag res_tar = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/res.tar"), FmtStr(out_inc_dir_path)));
D_AddDependency(res_tar, res_dir);
if (embed_in_rc) {
RcIncludeListAppend(&arena, &rc_includes, res_tar, Lit("RCDATA"));
} else {
D_AddDependency(inc_file, res_tar);
}
if (D_IsDirty(res_tar)) {
String tar_cmd = StringF(&arena, Lit("cd %F && tar cvf %F ."), FmtStr(res_dir.full_path), FmtStr(res_tar.full_path));
String step_name = StringF(&arena, Lit("Archiving %F"), FmtStr(D_GetName(res_tar)));
StepListAppend(&arena, &compile_command_list, step_name, tar_cmd, (String) { 0 }, true);
}
}
/* ========================== *
* RC file (windows)
* ========================== */
if (PlatformWindows) {
D_Tag rc_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/rc.rc"), FmtStr(out_inc_dir_path)));;
/* Add icon file to rc list */
{
D_Tag icon_file = D_FileTagFromPath(&arena, Lit("icon.ico"));
RcIncludeListAppend(&arena, &rc_includes, icon_file, Lit("ICON"));
}
/* Add rc dependencies */
for (RcIncludeListNode *rin = rc_includes.first; rin; rin = rin->next) {
D_AddDependency(rc_file, rin->tag);
}
if (D_IsDirty(rc_file)) {
/* Generate rc file */
D_ClearWrite(rc_file, Lit(""));
for (RcIncludeListNode *rin = rc_includes.first; rin; rin = rin->next) {
String name = D_GetName(rin->tag);
String line = StringF(&arena, Lit("%F %F DISCARDABLE %F\n"), FmtStr(name), FmtStr(rin->rc_type), FmtStr(name));
D_AppendWrite(rc_file, line);
}
/* Append rc -> res compile command */
D_Tag rc_res_file = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F/rc.res"), FmtStr(out_obj_dir_path)));
String rc_compile_cmd = { 0 };
if (arg_msvc) {
rc_compile_cmd = StringF(&arena, Lit("rc /fo\"%F\" \"%F\""), FmtStr(rc_res_file.full_path), FmtStr(rc_file.full_path));
} else {
rc_compile_cmd = StringF(&arena, Lit("llvm-rc /fo\"%F\" \"%F\""), FmtStr(rc_res_file.full_path), FmtStr(rc_file.full_path));
}
StepListAppend(&arena, &compile_command_list, D_GetName(rc_file), rc_compile_cmd, rc_res_file.full_path, true);
}
}
/* ========================== *
* PCH compile commands
* ========================== */
if (arg_msvc) {
/* C */
{
D_Tag c_src = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F.c"), FmtStr(pch_c_output.full_path)));
D_ClearWrite(c_src, Lit(""));
String comp_cmd = StringF(&arena, final_pch_c_compile_args_fmt, FmtStr(pch_header.full_path), FmtStr(c_src.full_path));
String link_file = pch_c_obj_output.full_path;
String step_name = StringF(&arena, Lit("%F (c pch)"), FmtStr(D_GetName(pch_header)));
StepListAppend(&arena, &compile_command_list, step_name, comp_cmd, link_file, true);
}
/* Cpp */
{
D_Tag cpp_src = D_FileTagFromPath(&arena, StringF(&arena, Lit("%F.cpp"), FmtStr(pch_cpp_output.full_path)));
D_ClearWrite(cpp_src, Lit(""));
String comp_cmd = StringF(&arena, final_pch_cpp_compile_args_fmt, FmtStr(pch_header.full_path), FmtStr(cpp_src.full_path));
String link_file = pch_cpp_obj_output.full_path;
String step_name = StringF(&arena, Lit("%F (cpp pch)"), FmtStr(D_GetName(pch_header)));
StepListAppend(&arena, &compile_command_list, step_name, comp_cmd, link_file, true);
}
} else {
/* C */
{
String comp_cmd = StringF(&arena, final_pch_c_compile_args_fmt, FmtStr(pch_header.full_path), FmtStr(pch_c_output.full_path));
String step_name = StringF(&arena, Lit("%F (c pch)"), FmtStr(D_GetName(pch_header)));
StepListAppend(&arena, &compile_command_list, step_name, comp_cmd, (String) { 0 }, true);
}
/* Cpp */
{
String comp_cmd = StringF(&arena, final_pch_cpp_compile_args_fmt, FmtStr(pch_header.full_path), FmtStr(pch_cpp_output.full_path));
String step_name = StringF(&arena, Lit("%F (cpp pch)"), FmtStr(D_GetName(pch_header)));
StepListAppend(&arena, &compile_command_list, step_name, comp_cmd, (String) { 0 }, true);
}
}
/* ========================== *
* Add src file compile commands
* ========================== */
D_Tag src_dir = D_DirTagFromPath(&arena, Lit("src"));
D_TagList src_files = D_GetDirContents(&arena, src_dir);
for (D_TagListNode *n = src_files.first; n; n = n->next) {
D_Tag file = n->tag;
Bool include = !file.is_dir;
if (!include) continue;
String name = D_GetName(file);
String extension = D_GetExtension(file);
Bool is_c = StringEqual(extension, Lit("c"));
Bool is_cpp = !is_c && StringEqual(extension, Lit("cpp"));
include = (is_c || is_cpp) && D_IsDirty(file);
if (!include) continue;
/* Determine platform specific source files */
{
if (StringBeginsWith(name, Lit("sys_")) ||
StringBeginsWith(name, Lit("renderer_")) ||
StringBeginsWith(name, Lit("playback_")) ||
StringBeginsWith(name, Lit("mp3_")) ||
StringBeginsWith(name, Lit("ttf_"))) {
include = false;
if (PlatformWindows) {
include = 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"));
}
}
}
if (!include) continue;
String obj_file_path = { 0 };
{
String name_no_extension = StringPathNoExtension(name);
obj_file_path = StringF(&arena, Lit("%F/%F.%F"), FmtStr(out_obj_dir_path), FmtStr(name_no_extension), FmtStr(obj_file_extension));
obj_file_path = OS_GetAbsPath(&arena, obj_file_path);
}
String comp_cmd_fmt = is_c ? final_c_compile_args_fmt : final_cpp_compile_args_fmt;
String comp_cmd = StringF(&arena, comp_cmd_fmt, FmtStr(file.full_path), FmtStr(obj_file_path));
StepListAppend(&arena, &compile_command_list, name, comp_cmd, obj_file_path, true);
}
/* ========================== *
* Compile / link
* ========================== */
if (compile_command_list.first) {
Bool success = true;
StringList link_files = { 0 };
/* Compile */
Size comp_i = 0;
Size comp_count = compile_command_list.count;
for (StepListNode *n = compile_command_list.first; n; n = n->next) {
++comp_i;
String comp_cmd = n->cmd;
if (comp_cmd.len > 0 ) {
SH_PrintF(Lit("[%F/%F] %F\n"), FmtI64(comp_i), FmtI64(comp_count), FmtStr(n->name));
//SH_PrintF(Lit("%F\n"), FmtStr(comp_cmd));
SH_CommandResult result = SH_RunCommandCaptureOutput(&arena, comp_cmd, true);
if (!n->silence_output_if_success || result.error != 0) {
SH_PrintF(Lit("%F\n"), FmtStr(result.output));
}
if (result.error != 0) {
Assert(false);
success = false;
break;
}
}
String link_file_path = n->link_file_path;
if (link_file_path.len > 0) {
StringListAppend(&arena, &link_files, link_file_path);
}
}
/* Link */
if (success) {
String link_files_str = { 0 };
{
StringList link_files_quoted_list = { 0 };
for (StringListNode *n = link_files.first; n; n = n->next) {
String path = StringF(&arena, Lit("\"%F\""), FmtStr(n->string));
StringListAppend(&arena, &link_files_quoted_list, path);
}
link_files_str = StringFromStringList(&arena, Lit(" "), link_files_quoted_list);
}
String link_cmd = StringF(&arena, final_link_args_fmt, FmtStr(link_files_str));
//SH_PrintF(Lit("[Link] %F\n"), FmtStr(link_cmd));
SH_Print(Lit("Linking...\n"));
SH_CommandResult result = SH_RunCommandCaptureOutput(&arena, link_cmd, false);
if (result.error != 0) {
Assert(false);
success = false;
}
D_SetDirty(executable);
}
if (!success) {
Error(Lit("Build failed"));
OS_Exit(1);
}
} else {
/* Nothing to build */
SH_Print(Lit("Nothing to build"));
}
#if 0
#if Rtc
getchar();
#endif
#endif
}