power_play/build.c
2024-06-08 23:37:57 -05:00

1046 lines
42 KiB
C

#include "buildit.h"
typedef struct WorkerSharedState WorkerSharedState;
/* ========================== *
* Globals
* ========================== */
Bool force_rebuild = false;
Arena arena = { 0 };
D_Store store = { 0 };
D_Hist hist = { 0 };
WorkerSharedState *worker_shared = { 0 };
/* ========================== *
* Util
* ========================== */
void Error(String msg)
{
SH_PrintF(Lit("ERROR: %F\n"), FmtStr(msg));
}
Bool IsDirty(D_Tag tag)
{
Bool res = force_rebuild ? true : D_IsDirty(&store, &hist, tag);
if (!res) {
if (tag.kind == D_TagKind_File || tag.kind == D_TagKind_Dir) {
res = !D_Exists(tag);
}
}
return res;
}
/* ========================== *
* Step
* ========================== */
typedef struct Step Step;
typedef struct StepList StepList;
typedef void StepFunc(Step *step);
typedef enum StepStatus {
StepStatus_None,
StepStatus_Success,
StepStatus_Failure
} StepStatus;
typedef struct Step Step;
struct Step {
String name;
StepFunc *func;
void *arg;
StepList *sl;
OS_Mutex res_mutex;
OS_ConditionVariable res_cv;
StepStatus res_status;
String res_output;
Step *next;
Step *prev;
Step *next_unqueued;
Step *prev_unqueued;
};
typedef struct StepList StepList;
struct StepList {
OS_Mutex mutex;
Arena arena;
Step *first;
Step *last;
Size count;
Step *first_unqueued;
Step *last_unqueued;
StepList *next;
};
StepList StepListAlloc(void)
{
StepList sl = { 0 };
sl.mutex = OS_MutexAlloc();
sl.arena = ArenaAlloc(Gigabyte(64));
return sl;
}
Step *DequeueNextStep(StepList *sl)
{
Step *res = 0;
OS_MutexLockW(&sl->mutex);
{
res = sl->first_unqueued;
if (res) {
DllRemoveNP(sl->first_unqueued, sl->last_unqueued, res, next_unqueued, prev_unqueued);
}
}
OS_MutexUnlockW(&sl->mutex);
return res;
}
void AddStep(StepList *sl, String name, StepFunc *func, void *arg)
{
OS_MutexLockW(&sl->mutex);
{
Step *s = ArenaPush(&sl->arena, Step);
s->name = StringCopy(&sl->arena, name);
s->func = func;
s->arg = arg;
s->sl = sl;
s->res_mutex = OS_MutexAlloc();
s->res_cv = OS_ConditionVariableAlloc();
DllPushBack(sl->first, sl->last, s);
DllPushBackNP(sl->first_unqueued, sl->last_unqueued, s, next_unqueued, prev_unqueued);
++sl->count;
}
OS_MutexUnlockW(&sl->mutex);
}
/* ========================== *
* Worker
* ========================== */
typedef struct Worker Worker;
typedef struct WorkerSharedState WorkerSharedState;
struct WorkerSharedState {
OS_Mutex mutex;
OS_ConditionVariable cv;
Arena arena;
StepList *first_step_list;
StepList *last_step_list;
Bool shutdown;
Worker *first_worker;
I64 worker_count;
};
typedef struct Worker Worker;
struct Worker {
OS_Thread thread;
Worker *next;
};
OS_ThreadEntryPointFuncDef(WorkerEntryPoint, arg)
{
#if 0
I64 id = (I64)arg;
{
TempArena scratch = ScratchBeginNoConflict();
String name = StringF(scratch.arena, Lit("Builder worker %F"), FmtI64(id));
OS_SetThreadName(name);
ScratchEnd(scratch);
}
#else
OS_SetThreadName(Lit("Builder worker"));
#endif
while (!worker_shared->shutdown) {
OS_MutexLockW(&worker_shared->mutex);
{
if (!worker_shared->shutdown && !worker_shared->first_step_list) {
OS_ConditionVariableWaitW(&worker_shared->cv, &worker_shared->mutex);
}
if (!worker_shared->shutdown && worker_shared->first_step_list) {
StepList *sl = worker_shared->first_step_list;
Step *s = DequeueNextStep(sl);
if (s) {
OS_MutexUnlockW(&worker_shared->mutex);
{
s->func(s);
}
OS_MutexLockW(&worker_shared->mutex);
} else {
/* Wait for all steps in old step list to finish, then pop the list */
for (Step *check_step = sl->first; check_step; check_step = check_step->next) {
OS_MutexLockW(&check_step->res_mutex);
{
while (check_step->res_status == StepStatus_None) {
OS_ConditionVariableWaitW(&check_step->res_cv, &check_step->res_mutex);
}
}
OS_MutexUnlockW(&check_step->res_mutex);
}
SllQueuePop(worker_shared->first_step_list, worker_shared->last_step_list);
}
}
}
OS_MutexUnlockW(&worker_shared->mutex);
}
}
void StartWorkers(I64 count)
{
OS_MutexLockW(&worker_shared->mutex);
{
for (I64 i = 0; i < count; ++i) {
Worker *w = ArenaPush(&worker_shared->arena, Worker);
w->thread = OS_ThreadAlloc(&WorkerEntryPoint, (void *)worker_shared->worker_count);
SllStackPush(worker_shared->first_worker, w);
++worker_shared->worker_count;
}
}
OS_MutexUnlockW(&worker_shared->mutex);
}
void ShutdownWorkers(void)
{
OS_MutexLockW(&worker_shared->mutex);
{
worker_shared->shutdown = true;
OS_ConditionVariableBroadcast(&worker_shared->cv);
}
OS_MutexUnlockW(&worker_shared->mutex);
for (Worker *w = worker_shared->first_worker; w; w = w->next) {
OS_ThreadWaitRelease(&w->thread);
}
}
void AddStepListToWorkQueue(StepList *sl)
{
OS_MutexLockW(&worker_shared->mutex);
{
SllQueuePush(worker_shared->first_step_list, worker_shared->last_step_list, sl);
OS_ConditionVariableBroadcast(&worker_shared->cv);
}
OS_MutexUnlockW(&worker_shared->mutex);
}
/* ========================== *
* Build steps
* ========================== */
typedef struct BuildStepSimpleCommandArg BuildStepSimpleCommandArg;
struct BuildStepSimpleCommandArg {
String cmd;
};
typedef struct BuildStepMsvcCompileCommandArg BuildStepMsvcCompileCommandArg;
struct BuildStepMsvcCompileCommandArg {
String cmd;
D_Tag depfile_dependent;
D_Tag output_depfile;
D_TagList depfile_force_includes;
};
void BuildStepSimpleCommand(Step *s)
{
TempArena scratch = ScratchBeginNoConflict();
BuildStepSimpleCommandArg *arg = s->arg;
SH_CommandResult result = SH_RunCommandCaptureOutput(scratch.arena, arg->cmd, true);
OS_MutexLockW(&s->res_mutex);
{
if (result.output.len > 0) {
OS_MutexLockW(&s->sl->mutex);
{
s->res_output.text = ArenaPushArrayNoZero(&s->sl->arena, Byte, result.output.len);
}
OS_MutexUnlockW(&s->sl->mutex);
s->res_output.len = result.output.len;
MemoryCopy(s->res_output.text, result.output.text, result.output.len);
}
s->res_status = result.error ? StepStatus_Failure : StepStatus_Success;
OS_ConditionVariableBroadcast(&s->res_cv);
}
OS_MutexUnlockW(&s->res_mutex);
ScratchEnd(scratch);
}
void BuildStepMsvcCompileCommand(Step *s)
{
TempArena scratch = ScratchBeginNoConflict();
BuildStepMsvcCompileCommandArg *arg = s->arg;
SH_CommandResult result = SH_RunCommandCaptureOutput(scratch.arena, arg->cmd, true);
if (!result.error && !D_IsNil(arg->depfile_dependent) && !D_IsNil(arg->output_depfile)) {
String depfile_data = B_DepfileTextFromMsvcOutput(scratch.arena, arg->depfile_dependent, arg->depfile_force_includes, result.output);
D_ClearWrite(arg->output_depfile, depfile_data);
}
OS_MutexLockW(&s->res_mutex);
{
if (result.output.len > 0) {
OS_MutexLockW(&s->sl->mutex);
{
s->res_output.text = ArenaPushArrayNoZero(&s->sl->arena, Byte, result.output.len);
}
OS_MutexUnlockW(&s->sl->mutex);
s->res_output.len = result.output.len;
MemoryCopy(s->res_output.text, result.output.text, result.output.len);
}
s->res_status = result.error ? StepStatus_Failure : StepStatus_Success;
OS_ConditionVariableBroadcast(&s->res_cv);
}
OS_MutexUnlockW(&s->res_mutex);
ScratchEnd(scratch);
}
/* ========================== *
* Build
* ========================== */
void OnBuild(StringList cli_args)
{
OS_TimeStamp start = OS_GetTimeStamp();
arena = ArenaAlloc(Gigabyte(64));
store = D_StoreAlloc();
worker_shared = ArenaPush(&arena, WorkerSharedState);
/* ========================== *
* Setup workers
* ========================== */
worker_shared->mutex = OS_MutexAlloc();
worker_shared->arena = ArenaAlloc(Gigabyte(64));
I64 worker_count = OS_GetProcessorCount();
StartWorkers(worker_count);
/* ========================== *
* 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 build_hash_path = OS_GetAbsPath(&arena, StringF(&arena, Lit("%F/.pp_build_hash"), FmtStr(arg_outdir)));
String out_dep_dir_path = OS_GetAbsPath(&arena, StringF(&arena, Lit("%F/dep/"), 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"));
SH_Print(Lit("Power Play build\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_TagFromPath(&arena, StringF(&arena, Lit("%F/PowerPlay.exe"), FmtStr(out_bin_dir_path)), D_TagKind_File);
D_Tag res_dir = D_TagFromPath(&arena, Lit("res"), D_TagKind_Dir);
D_Tag shaders_dir = D_TagFromPath(&arena, Lit("src/shaders"), D_TagKind_Dir);
D_Tag icon_file = D_TagFromPath(&arena, Lit("icon.ico"), D_TagKind_File);
D_Tag inc_src_file = D_TagFromPath(&arena, Lit("src/inc.c"), D_TagKind_File);
D_Tag rc_res_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/rc.res"), FmtStr(out_obj_dir_path)), D_TagKind_File);
/* ========================== *
* 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\" /FS /showIncludes"));
StringListAppend(&arena, &cpp_compile_args, Lit("cl.exe /nologo /c \"%F\" /Fo\"%F\" /FS /showIncludes"));
StringListAppend(&arena, &pch_c_compile_args, Lit("cl.exe /nologo /c /Yc\"%F\" \"%F\" /FS /showIncludes"));
StringListAppend(&arena, &pch_cpp_compile_args, Lit("cl.exe /nologo /c /Yc\"%F\" \"%F\" /FS /showIncludes"));
StringListAppend(&arena, &rc_compile_args, Lit("rc /fo\"%F\" \"%F\""));
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\""));
StringListAppend(&arena, &link_args, Lit("clang %F -o \"%F\""));
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(&arena, &compile_and_link_args, Lit("-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)));
}
}
/* ========================== *
* Generate root dependencies
* ========================== */
{
D_Tag build_hash_file = D_TagFromPath(&arena, build_hash_path, D_TagKind_File);
U64 build_hash = D_HashString64Basis;
{
String args_str = StringFromStringList(&arena, Lit(" "), cli_args);
String compile_time_str = Lit(__DATE__ __TIME__);
build_hash = D_HashString64(build_hash, args_str);
build_hash = D_HashString64(build_hash, compile_time_str);
}
U64 old_build_hash = 0;
{
String build_hash_file_data = D_ReadAll(&arena, build_hash_file);
if (build_hash_file_data.len >= 8) {
MemoryCopy((Byte *)&old_build_hash, build_hash_file_data.text, 8);
}
}
if (build_hash != old_build_hash) {
SH_Print(Lit("Builder exe or build args have changed, rebuilding all.\n"));
force_rebuild = true;
String data = StringFromStruct(&build_hash);
D_ClearWrite(build_hash_file, data);
}
}
/* ========================== *
* Build
* ========================== */
hist = D_HistFromPath(&arena, hist_path);
StepList tar_build_steps = StepListAlloc();
StepList rc_and_pch_build_steps = StepListAlloc();
StepList comp_build_steps = StepListAlloc();
StepList link_build_steps = StepListAlloc();
D_TagList link_files = { 0 };
/* ========================== *
* Build step: Tar archives
* ========================== */
{
D_TagList tar_input_dirs = { 0 };
{
D_TagListAppend(&arena, &tar_input_dirs, shaders_dir);
if (should_embed_res_dir) {
D_TagListAppend(&arena, &tar_input_dirs, res_dir);
}
}
for (D_TagListNode *n = tar_input_dirs.first; n; n = n->next) {
D_Tag input_dir = n->tag;
D_Tag tar_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(input_dir))), D_TagKind_File);
D_AddDependency(&store, tar_file, input_dir);
if (should_embed_in_rc) {
D_AddDependency(&store, rc_res_file, tar_file);
} else {
D_AddDependency(&store, inc_src_file, tar_file);
}
if (IsDirty(tar_file)) {
BuildStepSimpleCommandArg *bs_arg = ArenaPush(&arena, BuildStepSimpleCommandArg);
bs_arg->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)));
AddStep(&tar_build_steps, step_name, &BuildStepSimpleCommand, bs_arg);
}
}
}
/* ========================== *
* Build step: Compile RC files
* ========================== */
if (PlatformWindows) {
D_Tag rc_input_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/rc.rc"), FmtStr(out_inc_dir_path)), D_TagKind_File);
{
D_Tag shaders_tar_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(shaders_dir))), D_TagKind_File);
D_Tag res_tar_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(res_dir))), D_TagKind_File);
D_AddDependency(&store, rc_input_file, icon_file);
if (should_embed_in_rc) {
D_AddDependency(&store, rc_input_file, shaders_tar_file);
if (should_embed_res_dir) {
D_AddDependency(&store, rc_input_file, res_tar_file);
}
}
if (IsDirty(rc_input_file)) {
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_tar_file)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(shaders_tar_file))));
if (should_embed_res_dir) {
D_AppendWrite(rc_input_file, StringF(&arena, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(res_tar_file)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(res_tar_file))));
}
}
}
}
{
String rc_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), rc_compile_args);
D_AddDependency(&store, rc_res_file, rc_input_file);
if (IsDirty(rc_res_file)) {
BuildStepSimpleCommandArg *bs_arg = ArenaPush(&arena, BuildStepSimpleCommandArg);
bs_arg->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)));
AddStep(&rc_and_pch_build_steps, step_name, &BuildStepSimpleCommand, bs_arg);
}
D_TagListAppend(&arena, &link_files, rc_res_file);
}
}
/* ========================== *
* Build step: Compile pch files
* ========================== */
D_TagList depfile_force_includes = { 0 };
{
D_Tag pch_header_file = D_TagFromPath(&arena, Lit("src/common.h"), D_TagKind_File);
D_Tag dep_file;
{
String name = D_GetName(pch_header_file);
String dep_file_path = StringF(&arena, Lit("%F/%F.%F"), FmtStr(out_obj_dir_path), FmtStr(name), FmtStr(dep_file_extension));
dep_file = D_TagFromPath(&arena, dep_file_path, D_TagKind_DepFile);
}
D_Tag pch_c_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/%F.c_pch"), FmtStr(out_obj_dir_path), FmtStr(D_GetName(pch_header_file))), D_TagKind_File);
D_AddDependency(&store, pch_c_file, pch_header_file);
D_AddDependency(&store, pch_c_file, dep_file);
D_Tag pch_cpp_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/%F.cpp_pch"), FmtStr(out_obj_dir_path), FmtStr(D_GetName(pch_header_file))), D_TagKind_File);
D_AddDependency(&store, pch_cpp_file, pch_header_file);
D_AddDependency(&store, pch_cpp_file, dep_file);
if (arg_msvc) {
D_TagListAppend(&arena, &depfile_force_includes, pch_header_file);
D_Tag pch_c_src_gen_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/common.c"), FmtStr(out_obj_dir_path)), D_TagKind_File);
D_Tag pch_c_src_gen_obj_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/common.c.obj"), FmtStr(out_obj_dir_path)), D_TagKind_File);
D_Tag pch_cpp_src_gen_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/common.cpp"), FmtStr(out_obj_dir_path)), D_TagKind_File);
D_Tag pch_cpp_src_gen_obj_file = D_TagFromPath(&arena, StringF(&arena, Lit("%F/common.cpp.obj"), FmtStr(out_obj_dir_path)), D_TagKind_File);
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)));
D_TagListAppend(&arena, &link_files, pch_c_src_gen_obj_file);
D_TagListAppend(&arena, &link_files, pch_cpp_src_gen_obj_file);
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);
/* C */
D_AddDependency(&store, pch_c_src_gen_obj_file, pch_c_src_gen_file);
D_AddDependency(&store, pch_c_file, pch_c_src_gen_obj_file);
if (IsDirty(pch_c_file)) {
D_ClearWrite(pch_c_src_gen_file, Lit(""));
BuildStepSimpleCommandArg *bs_arg = ArenaPush(&arena, BuildStepSimpleCommandArg);
bs_arg->cmd = StringF(&arena, pch_c_compile_args_fmt, FmtStr(pch_header_file.full_path), FmtStr(pch_c_src_gen_file.full_path));
String step_name = StringF(&arena, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_c_file)));
AddStep(&rc_and_pch_build_steps, step_name, &BuildStepSimpleCommand, bs_arg);
}
/* Cpp */
D_AddDependency(&store, pch_cpp_src_gen_obj_file, pch_cpp_src_gen_file);
D_AddDependency(&store, pch_cpp_file, pch_cpp_src_gen_obj_file);
if (IsDirty(pch_cpp_file)) {
D_ClearWrite(pch_cpp_src_gen_file, Lit(""));
BuildStepSimpleCommandArg *bs_arg = ArenaPush(&arena, BuildStepSimpleCommandArg);
bs_arg->cmd = StringF(&arena, pch_cpp_compile_args_fmt, FmtStr(pch_header_file.full_path), FmtStr(pch_cpp_src_gen_file.full_path));
String step_name = StringF(&arena, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_cpp_file)));
AddStep(&rc_and_pch_build_steps, step_name, &BuildStepSimpleCommand, bs_arg);
}
} 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)));
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);
/* C */
if (IsDirty(pch_c_file)) {
String step_name = StringF(&arena, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_c_file)));
BuildStepMsvcCompileCommandArg *bs_arg = ArenaPush(&arena, BuildStepMsvcCompileCommandArg);
bs_arg->cmd = StringF(&arena, pch_c_compile_args_fmt, FmtStr(pch_header_file.full_path), FmtStr(pch_c_file.full_path));
bs_arg->depfile_dependent = pch_header_file;
bs_arg->output_depfile = dep_file;
AddStep(&comp_build_steps, step_name, &BuildStepMsvcCompileCommand, bs_arg);
}
/* Cpp */
if (IsDirty(pch_cpp_file)) {
String step_name = StringF(&arena, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_cpp_file)));
BuildStepMsvcCompileCommandArg *bs_arg = ArenaPush(&arena, BuildStepMsvcCompileCommandArg);
bs_arg->cmd = StringF(&arena, pch_cpp_compile_args_fmt, FmtStr(pch_header_file.full_path), FmtStr(pch_cpp_file.full_path));
bs_arg->depfile_dependent = pch_header_file;
bs_arg->output_depfile = dep_file;
AddStep(&comp_build_steps, step_name, &BuildStepMsvcCompileCommand, bs_arg);
}
}
}
/* ========================== *
* Build step: Compile src files
* ========================== */
{
D_TagList src_input_files = { 0 };
{
D_Tag src_dir = D_TagFromPath(&arena, Lit("src"), D_TagKind_Dir);
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.kind == D_TagKind_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);
}
}
}
{
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 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_TagFromPath(&arena, dep_file_path, D_TagKind_DepFile);
}
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_TagFromPath(&arena, obj_file_path, D_TagKind_File);
}
D_AddDependency(&store, obj_file, file);
D_AddDependency(&store, obj_file, dep_file);
if (IsDirty(obj_file)) {
String comp_cmd_fmt = is_c ? c_compile_args_fmt : cpp_compile_args_fmt;
String step_name = StringF(&arena, Lit("%F -> %F"), FmtStr(name), FmtStr(D_GetName(obj_file)));
if (arg_msvc) {
BuildStepMsvcCompileCommandArg *bs_arg = ArenaPush(&arena, BuildStepMsvcCompileCommandArg);
bs_arg->cmd = StringF(&arena, comp_cmd_fmt, FmtStr(file.full_path), FmtStr(obj_file.full_path));
bs_arg->depfile_dependent = obj_file;
bs_arg->output_depfile = dep_file;
bs_arg->depfile_force_includes = depfile_force_includes;
AddStep(&comp_build_steps, step_name, &BuildStepMsvcCompileCommand, bs_arg);
} else {
BuildStepSimpleCommandArg *bs_arg = ArenaPush(&arena, BuildStepSimpleCommandArg);
bs_arg->cmd = StringF(&arena, comp_cmd_fmt, FmtStr(file.full_path), FmtStr(obj_file.full_path));
AddStep(&comp_build_steps, step_name, &BuildStepSimpleCommand, bs_arg);
}
}
D_TagListAppend(&arena, &link_files, obj_file);
}
}
}
/* ========================== *
* Build step: 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(&store, 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);
BuildStepSimpleCommandArg *bs_arg = ArenaPush(&arena, BuildStepSimpleCommandArg);
bs_arg->cmd = StringF(&arena, link_args_fmt, FmtStr(link_files_str), FmtStr(executable_file.full_path));
String step_name = Lit("Linking");
AddStep(&link_build_steps, step_name, &BuildStepSimpleCommand, bs_arg);
}
}
/* ========================== *
* Execute build steps
* ========================== */
/* TODO: Cleanup (testing parallel stuff) */
Bool success = true;
if (IsDirty(executable_file)) {
AddStepListToWorkQueue(&tar_build_steps);
AddStepListToWorkQueue(&rc_and_pch_build_steps);
AddStepListToWorkQueue(&comp_build_steps);
AddStepListToWorkQueue(&link_build_steps);
I64 step_count = 0;
step_count += tar_build_steps.count;
step_count += rc_and_pch_build_steps.count;
step_count += comp_build_steps.count;
step_count += link_build_steps.count;
Step *s = tar_build_steps.first;
{
if (!s) {
s = rc_and_pch_build_steps.first;
if (!s) {
s = comp_build_steps.first;
if (!s) {
s = link_build_steps.first;
}
}
}
}
I64 step_i = 0;
while (s) {
++step_i;
SH_PrintF(Lit("[%F/%F] %F\n"), FmtI64(step_i), FmtI64(step_count), FmtStr(s->name));
OS_MutexLockR(&s->res_mutex);
{
while (s->res_status == StepStatus_None) {
OS_ConditionVariableWaitR(&s->res_cv, &s->res_mutex);
}
}
OS_MutexUnlockR(&s->res_mutex);
String output = s->res_output;
if (s->res_status != StepStatus_Success) {
Assert(false);
success = false;
SH_PrintF(Lit("%F\n"), FmtStr(output));
break;
}
{
if (s == tar_build_steps.last) {
s = rc_and_pch_build_steps.first;
if (s) goto skip_cont;
}
if (s == rc_and_pch_build_steps.last) {
s = comp_build_steps.first;
if (s) goto skip_cont;
}
if (s == comp_build_steps.last) {
s = link_build_steps.first;
if (s) goto skip_cont;
}
}
s = s->next;
skip_cont:
continue;
}
} else {
SH_Print(Lit("No work to do\n"));
}
if (!success) {
Error(Lit("Build failed\n"));
OS_Exit(1);
}
/* ========================== *
* Write hist file
* ========================== */
D_WriteStoreToHistFile(&store, hist_path);
OS_TimeStamp end = OS_GetTimeStamp();
F64 seconds = OS_SecondsFromTimeStamp(end - start);
SH_PrintF(Lit("Finished in %F seconds\n"), FmtF64P(seconds, 5));
ShutdownWorkers();
#if 0
#if Rtc
getchar();
#endif
#endif
}