// #include #include #define DQN_IMPLEMENTATION #include "External/tely/External/dqn/dqn.h" struct Dqn_OSExecuteAsyncHandle { void *process; }; struct Dqn_OSExecuteResult { uint32_t os_error_code; uint32_t exit_code; }; struct Dqn_CPPBuildCompileFile { Dqn_Slice flags; Dqn_String8 path; }; struct Dqn_CPPBuildContext { Dqn_Slice compile_files; Dqn_Slice compile_flags; Dqn_Slice include_dirs; Dqn_Slice link_flags; Dqn_String8 build_dir; }; enum Dqn_CPPBuildStatus { Dqn_CPPBuildStatus_Ok, Dqn_CPPBuildStatus_BuildDirectoryFailedToBeMade, }; struct Dqn_CPPBuildAsyncResult { Dqn_CPPBuildStatus status; Dqn_OSExecuteAsyncHandle async_handle; }; Dqn_OSExecuteResult Dqn_OS_ExecuteWait(Dqn_OSExecuteAsyncHandle handle) { Dqn_OSExecuteResult result = {}; if (!handle.process) return result; DWORD exec_result = WaitForSingleObject(handle.process, INFINITE); if (exec_result == WAIT_FAILED) { result.os_error_code = GetLastError(); return result; } DWORD exit_status; if (!GetExitCodeProcess(handle.process, &exit_status)) { result.os_error_code = GetLastError(); return result; } result.exit_code = exit_status; CloseHandle(handle.process); return result; } Dqn_OSExecuteAsyncHandle Dqn_OS_ExecuteAsync(Dqn_String8 cmd, Dqn_String8 working_dir) { Dqn_OSExecuteAsyncHandle result = {}; #if defined(DQN_OS_WIN32) Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_String16 cmd16 = Dqn_Win_String8ToString16(scratch.arena, cmd); Dqn_String16 working_dir16 = Dqn_Win_String8ToString16(scratch.arena, working_dir); PROCESS_INFORMATION proc_info = {}; STARTUPINFOW startup_info = {}; startup_info.cb = sizeof(STARTUPINFOW); startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); startup_info.dwFlags |= STARTF_USESTDHANDLES; BOOL create_result = CreateProcessW(nullptr, cmd16.data, nullptr, nullptr, true, 0, nullptr, working_dir16.data, &startup_info, &proc_info); if (!create_result) return result; CloseHandle(proc_info.hThread); result.process = proc_info.hProcess; #else pid_t child_pid = fork(); if (child_pid < 0) { result.os_error_code = errno; return result; } if (child_pid == 0) { if (working_dir.size) { if (chdir(working_dir.data) == -1) { result.os_error_code = errno; return result; } } if (execvp(cmd.items[0], (char * const*) cmd_null.items) < 0) { result.os_error_code = errno; return result; } DQN_INVALID_CODE_PATH; } result.process = DQN_CAST(void *)child_pid; #endif return result; } Dqn_OSExecuteResult Dqn_OS_Execute(Dqn_String8 cmd, Dqn_String8 working_dir) { Dqn_OSExecuteAsyncHandle async_handle = Dqn_OS_ExecuteAsync(cmd, working_dir); Dqn_OSExecuteResult result = Dqn_OS_ExecuteWait(async_handle); return result; } Dqn_String8 Dqn_CPPBuildContext_ToCommandLine(Dqn_CPPBuildContext build_context, Dqn_Allocator allocator) { // NOTE: Check if object files are newer than the source files ================================= Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); Dqn_Slice dirtied_compile_files = Dqn_Slice_Alloc(scratch.arena, build_context.compile_files.size, Dqn_ZeroMem_Yes); dirtied_compile_files.size = 0; DQN_FOR_UINDEX (index, build_context.compile_files.size) { Dqn_CPPBuildCompileFile file = build_context.compile_files.data[index]; Dqn_String8 file_stem = Dqn_String8_FileNameNoExtension(file.path); Dqn_String8 obj_file = {}; if (build_context.build_dir.size) obj_file = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.obj", DQN_STRING_FMT(build_context.build_dir), DQN_STRING_FMT(file_stem)); else obj_file = Dqn_FsPath_ConvertF(scratch.arena, "%.*s.obj", DQN_STRING_FMT(file_stem)); Dqn_FsInfo file_info = Dqn_Fs_GetInfo(file.path); Dqn_FsInfo obj_file_info = Dqn_Fs_GetInfo(obj_file); if (obj_file_info.last_write_time_in_s >= file_info.last_write_time_in_s) continue; dirtied_compile_files.data[dirtied_compile_files.size++] = file; } Dqn_String8 result = {}; if (dirtied_compile_files.size <= 0) return result; // NOTE: Build the command line invocation ===================================================== Dqn_String8Builder builder = {}; builder.allocator = allocator; DQN_FOR_UINDEX (index, build_context.compile_flags.size) { Dqn_String8 flag = build_context.compile_flags.data[index]; if (index) Dqn_String8Builder_AppendF(&builder, " "); Dqn_String8Builder_AppendRef(&builder, flag); } DQN_FOR_UINDEX (index, build_context.include_dirs.size) { Dqn_String8 include_dir = build_context.include_dirs.data[index]; if (builder.count) Dqn_String8Builder_AppendF(&builder, " "); Dqn_String8Builder_AppendF(&builder, "/I %.*s", DQN_STRING_FMT(include_dir)); } DQN_FOR_UINDEX (index, dirtied_compile_files.size) { Dqn_CPPBuildCompileFile file = dirtied_compile_files.data[index]; Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); Dqn_String8 obj_file = {}; if (builder.count) Dqn_String8Builder_AppendF(&builder, " "); DQN_FOR_UINDEX (flag_index, file.flags.size) { Dqn_String8 flag = file.flags.data[flag_index]; Dqn_String8Builder_AppendF(&builder, "%s%.*s", flag_index ? " " : "", DQN_STRING_FMT(flag)); } if (file.flags.size) Dqn_String8Builder_AppendF(&builder, " "); Dqn_String8Builder_AppendRef(&builder, file.path); } DQN_FOR_UINDEX (index, build_context.link_flags.size) { Dqn_String8 file = build_context.link_flags.data[index]; if (builder.count) Dqn_String8Builder_AppendF(&builder, " "); Dqn_String8Builder_AppendRef(&builder, file); } result = Dqn_String8Builder_Build(&builder, allocator); return result; } Dqn_CPPBuildAsyncResult Dqn_CPPBuild_Async(Dqn_CPPBuildContext build_context) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_String8 cmd = Dqn_CPPBuildContext_ToCommandLine(build_context, scratch.allocator); Dqn_CPPBuildAsyncResult result = {}; if (!cmd.size) return result; if (!Dqn_Fs_MakeDir(build_context.build_dir)) { result.status = Dqn_CPPBuildStatus_BuildDirectoryFailedToBeMade; return result; } result.async_handle = Dqn_OS_ExecuteAsync(cmd, build_context.build_dir); return result; } void Dqn_CPPBuild_ExecuteOrAbort(Dqn_CPPBuildContext build_context) { Dqn_CPPBuildAsyncResult build_result = Dqn_CPPBuild_Async(build_context); if (build_result.status == Dqn_CPPBuildStatus_BuildDirectoryFailedToBeMade) { Dqn_Log_ErrorF("Failed to make build dir '%.*s'", DQN_STRING_FMT(build_context.build_dir)); exit(-1); } Dqn_OSExecuteResult exec_result = Dqn_OS_ExecuteWait(build_result.async_handle); if (exec_result.os_error_code) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_Log_ErrorF("Build command could not be executed, error returned was (%d): %.*s", error.code, DQN_STRING_FMT(error.msg)); exit(-1); } if (exec_result.exit_code) exit(exec_result.exit_code); } int main() { Dqn_Library_Init(); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_String8 const exe_dir = Dqn_OS_EXEDir(scratch.arena); Dqn_String8 const build_dir = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Build", DQN_STRING_FMT(exe_dir)); Dqn_String8 const tely_dir = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/External/tely", DQN_STRING_FMT(exe_dir)); Dqn_Slice common_compile_flags = Dqn_Slice_InitCArrayCopy(scratch.arena, { DQN_STRING8("cl"), DQN_STRING8("/W4"), DQN_STRING8("/Z7"), DQN_STRING8("/MT"), DQN_STRING8("/EHsc"), DQN_STRING8("/nologo"), }); Dqn_Slice common_link_flags = Dqn_Slice_InitCArrayCopy(scratch.arena, { DQN_STRING8("/link"), DQN_STRING8("/incremental:no"), }); // NOTE: Assets ================================================================================ uint64_t robocopy_timings[2] = {}; { robocopy_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { robocopy_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_String8 robocopy_cmd = Dqn_String8_InitF(scratch.allocator, "robocopy /MIR /NJH /NJS /NDL /NP %.*s\\Data %.*s\\Data", DQN_STRING_FMT(exe_dir), DQN_STRING_FMT(build_dir)); Dqn_OS_Execute(robocopy_cmd, /*working_dir*/ {}); } // NOTE: Raylib ================================================================================ Dqn_CPPBuildContext raylib_build_context = {}; Dqn_String8 const raylib_dir = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/External/tely/external/raylib", DQN_STRING_FMT(exe_dir)); uint64_t raylib_timings[2] = {}; { raylib_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { raylib_timings[1] = Dqn_OS_PerfCounterNow(); }; raylib_build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rcore.c", DQN_STRING_FMT(raylib_dir))}, Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/utils.c", DQN_STRING_FMT(raylib_dir))}, Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/raudio.c", DQN_STRING_FMT(raylib_dir))}, Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rmodels.c", DQN_STRING_FMT(raylib_dir))}, Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rtext.c", DQN_STRING_FMT(raylib_dir))}, Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rtextures.c", DQN_STRING_FMT(raylib_dir))}, Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rshapes.c", DQN_STRING_FMT(raylib_dir))}, Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rglfw.c", DQN_STRING_FMT(raylib_dir))}, }); raylib_build_context.include_dirs = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_FsPath_ConvertF(scratch.arena, "%.*s", DQN_STRING_FMT(raylib_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/external/glfw/include", DQN_STRING_FMT(raylib_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/glfw/deps/mingw", DQN_STRING_FMT(raylib_dir)), }); raylib_build_context.compile_flags = Dqn_Slice_InitCArrayCopy(scratch.arena, { DQN_STRING8("cl"), DQN_STRING8("/w"), DQN_STRING8("/c"), DQN_STRING8("/D _DEFAULT_SOURCE"), DQN_STRING8("/D PLATFORM_DESKTOP"), DQN_STRING8("/W4"), DQN_STRING8("/Z7"), DQN_STRING8("/MT"), DQN_STRING8("/EHsc"), DQN_STRING8("/nologo"), }); raylib_build_context.link_flags = common_link_flags; raylib_build_context.build_dir = build_dir; Dqn_CPPBuild_ExecuteOrAbort(raylib_build_context); } // NOTE: Feely Pona Sprite Packer ============================================================== uint64_t feely_pona_sprite_packer_timings[2] = {}; { feely_pona_sprite_packer_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { feely_pona_sprite_packer_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_String8 code_dir = exe_dir; Dqn_CPPBuildContext build_context = {}; build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_sprite_packer.h", DQN_STRING_FMT(code_dir)) }, }); build_context.compile_flags = common_compile_flags; build_context.link_flags = common_link_flags; build_context.build_dir = build_dir; Dqn_CPPBuild_ExecuteOrAbort(build_context); } // NOTE: Feely Pona No DLL ===================================================================== uint64_t feely_pona_no_dll_timings[2] = {}; Dqn_CPPBuildContext feely_pona_no_dll_build_context = {}; { feely_pona_no_dll_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { feely_pona_no_dll_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_String8 code_dir = exe_dir; feely_pona_no_dll_build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_CPPBuildCompileFile{ {Dqn_Slice_InitCArrayCopy(scratch.arena, {DQN_STRING8("/Tp")})}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_nodll.h", DQN_STRING_FMT(code_dir)) }, }); feely_pona_no_dll_build_context.include_dirs = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_FsPath_ConvertF(scratch.arena, "%.*s", DQN_STRING_FMT(raylib_dir)), }); feely_pona_no_dll_build_context.compile_flags = common_compile_flags; // NOTE: Link to raylib object files and windows libs ====================================== Dqn_List link_flags = Dqn_List_InitSliceCopy(scratch.arena, 128, common_link_flags); { for (Dqn_CPPBuildCompileFile file : raylib_build_context.compile_files) { Dqn_String8 file_stem = Dqn_String8_FileNameNoExtension(file.path); Dqn_String8 obj_file = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.obj", DQN_STRING_FMT(raylib_build_context.build_dir), DQN_STRING_FMT(file_stem)); Dqn_List_Add(&link_flags, obj_file); } Dqn_List_Add(&link_flags, DQN_STRING8("gdi32.lib")); Dqn_List_Add(&link_flags, DQN_STRING8("opengl32.lib")); Dqn_List_Add(&link_flags, DQN_STRING8("winmm.lib")); Dqn_List_Add(&link_flags, DQN_STRING8("user32.lib")); Dqn_List_Add(&link_flags, DQN_STRING8("shell32.lib")); } feely_pona_no_dll_build_context.link_flags = Dqn_List_ToSliceCopy(&link_flags, scratch.arena); feely_pona_no_dll_build_context.build_dir = build_dir; Dqn_CPPBuild_ExecuteOrAbort(feely_pona_no_dll_build_context); } // NOTE: Feely Pona DLL ======================================================================== uint64_t feely_pona_dll_timings[2] = {}; { feely_pona_dll_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { feely_pona_dll_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_String8 code_dir = exe_dir; Dqn_CPPBuildContext build_context = {}; build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_CPPBuildCompileFile{ {Dqn_Slice_InitCArrayCopy(scratch.arena, {DQN_STRING8("/Tp")})}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_unity.h", DQN_STRING_FMT(code_dir)) }, }); Dqn_List compile_flags = Dqn_List_InitSliceCopy(scratch.arena, 128, common_compile_flags); Dqn_List_Add(&compile_flags, DQN_STRING8("/LD")); Dqn_List_Add(&compile_flags, DQN_STRING8("/Fotely_dll_msvc")); Dqn_List_Add(&compile_flags, DQN_STRING8("/Fetely_dll_msvc")); build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena); build_context.link_flags = feely_pona_no_dll_build_context.link_flags; build_context.build_dir = build_dir; Dqn_CPPBuild_ExecuteOrAbort(build_context); } // NOTE: Feely Pona platform =================================================================== uint64_t feely_pona_platform_timings[2] = {}; { feely_pona_platform_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { feely_pona_platform_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_String8 code_dir = exe_dir; Dqn_CPPBuildContext build_context = {}; build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_CPPBuildCompileFile{ {Dqn_Slice_InitCArrayCopy(scratch.arena, {DQN_STRING8("/Tp")})}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/tely_platform_raylib_unity.h", DQN_STRING_FMT(tely_dir)) }, }); build_context.include_dirs = Dqn_Slice_InitCArrayCopy(scratch.arena, { raylib_dir, }); Dqn_List compile_flags = Dqn_List_InitSliceCopy(scratch.arena, 128, common_compile_flags); Dqn_List_Add(&compile_flags, DQN_STRING8("/Fofeely_pona_msvc")); Dqn_List_Add(&compile_flags, DQN_STRING8("/Fefeely_pona_msvc")); build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena); build_context.link_flags = feely_pona_no_dll_build_context.link_flags; build_context.build_dir = build_dir; Dqn_CPPBuild_ExecuteOrAbort(build_context); } Dqn_Print_StdLnF(Dqn_PrintStd_Out, ""); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "robocopy: %.2fms", Dqn_OS_PerfCounterMs(robocopy_timings[0], robocopy_timings[1])); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "raylib: %.2fms", Dqn_OS_PerfCounterMs(raylib_timings[0], raylib_timings[1])); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "feely pona sprite packer: %.2fms", Dqn_OS_PerfCounterMs(feely_pona_sprite_packer_timings[0], feely_pona_sprite_packer_timings[1])); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "feely pona (no dll): %.2fms", Dqn_OS_PerfCounterMs(feely_pona_no_dll_timings[0], feely_pona_no_dll_timings[1])); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "feely pona (dll): %.2fms", Dqn_OS_PerfCounterMs(feely_pona_dll_timings[0], feely_pona_dll_timings[1])); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "feely pona (platform): %.2fms", Dqn_OS_PerfCounterMs(feely_pona_platform_timings[0], feely_pona_platform_timings[1])); return 0; }