// #include #include #define DQN_IMPLEMENTATION #include "External/tely/External/dqn/dqn.h" #define DQN_CPP_BUILD_IMPLEMENTATION #include "External/tely/External/dqn/dqn_cppbuild.h" #if 0 void RebuildProgramIfRequired(int argc, char const **argv) { Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Str8 const exe_dir = Dqn_OS_EXEDir(scratch.arena); Dqn_Str8 build_program_path = Dqn_OS_EXEPath(scratch.arena); Dqn_FsInfo build_program_info = Dqn_Fs_GetInfo(build_program_path); Dqn_FsInfo source_path = Dqn_Fs_GetInfo(Dqn_Str8_InitCStr8(__FILE__)); if (!build_program_info.exists) { Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_Log_WarningF("Failed to get the last write time of the build program '%.*s', skipping rebuild (%d): %.*s", DQN_STR_FMT(build_program_path), error.code, DQN_STR_FMT(error.msg)); return; } if (!source_path.exists) { Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_Log_WarningF( "Failed to get the last write time of the build program's source code '%s', skipping rebuild (%d): %.*s", __FILE__, error.code, DQN_STR_FMT(error.msg)); return; } // NOTE: The build program is newer than the source path, no rebuild required if (source_path.last_write_time_in_s < build_program_info.last_write_time_in_s) { return; } Dqn_Log_InfoF("Build program source code has changed, rebuilding the program (timestamps changed source was %I64u, build program was %I64u)", source_path.last_write_time_in_s, build_program_info.last_write_time_in_s); Dqn_Str8 temp_build_program_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s.old", DQN_STR_FMT(build_program_path)); if (!Dqn_Fs_Move(build_program_path, temp_build_program_path, true)) { Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_Log_WarningF("Failed to backup the build program for rebuilding, skipping rebuild (%d): %.*s", error.code, DQN_STR_FMT(error.msg)); return; } // NOTE: Rebuild the build program because a change was detected =============== Dqn_Str8 rebuild_cmd = Dqn_Str8_InitF(scratch.allocator, "cl -Z7 -W4 -nologo %s /incremental:no /link", __FILE__); Dqn_OSExecResult rebuild_result = Dqn_OS_Exec(rebuild_cmd, exe_dir /*working_dir*/); if (rebuild_result.os_error_code) { Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_Log_ErrorF("Detected change in the build program's source code '%s' but the OS failed to rebuild the program, skipping rebuild (%d): %.*s", __FILE__, error.code, DQN_STR_FMT(error.msg)); return; } if (rebuild_result.exit_code) { Dqn_Fs_Move(temp_build_program_path, build_program_path, true); exit(rebuild_result.exit_code); } Dqn_Str8Builder builder = {}; builder.allocator = scratch.allocator; DQN_FOR_UINDEX (arg_index, argc) Dqn_Str8Builder_AppendF(&builder, "%s%s", arg_index ? " " : "", argv[arg_index]); Dqn_Str8 rebootstrap_cmd = Dqn_Str8Builder_Build(&builder, scratch.allocator); Dqn_OSExecResult exec_result = Dqn_OS_Exec(rebootstrap_cmd, exe_dir /*working_dir*/); if (exec_result.os_error_code) { Dqn_WinError error = Dqn_Win_LastError(scratch.arena); Dqn_Log_ErrorF("Detected change in the build program's source code '%s' but the OS failed to rebuild the program, skipping rebuild (%d): %.*s", __FILE__, error.code, DQN_STR_FMT(error.msg)); Dqn_Fs_Move(temp_build_program_path, build_program_path, true); return; } exit(exec_result.exit_code); } #endif #define PRINT_HELP Dqn_Print_StdLnF(Dqn_PrintStd_Out, "USAGE: feely_pona_build [--help|--dry-run|--web|--fast-dev-build|--msvc|--gcc]") int main(int argc, char const **argv) { Dqn_Library_Init(Dqn_LibraryOnInit_Nil); bool dry_run = false; bool target_web = false; bool dev_fast_build = false; bool msvc_build = false; bool gcc_build = false; for (Dqn_isize arg_index = 1; arg_index < argc; arg_index++) { Dqn_Str8 arg = Dqn_Str8_InitCStr8(argv[arg_index]); if (arg == DQN_STR8("--help")) { PRINT_HELP; return 0; } else if (arg == DQN_STR8("--dry-run")) { dry_run = true; } else if (arg == DQN_STR8("--web")) { target_web = true; } else if (arg == DQN_STR8("--fast-dev-build")) { dev_fast_build = true; } else if (arg == DQN_STR8("--gcc")) { gcc_build = true; } else if (arg == DQN_STR8("--msvc")) { msvc_build = true; } else { PRINT_HELP; return 0; } } #if 0 RebuildProgramIfRequired(argc, argv); #else Dqn_Print_StdLnF(Dqn_PrintStd_Out, "-- Dqn_CPPBuild v0"); #endif if (msvc_build && gcc_build) { Dqn_Print_StdLnF(Dqn_PrintStd_Out, "Both '--msvc' and '--gcc' were specified but only one is supported at a time for this build program."); return -1; } if (!msvc_build && !gcc_build) { #if defined(DQN_OS_WIN32) msvc_build = true; #else gcc_build = true; #endif } Dqn_Print_StdLnF(Dqn_PrintStd_Out, "Building for %s", msvc_build ? "Windows" : "Linux"); uint64_t build_timings[2] = {}; build_timings[0] = Dqn_OS_PerfCounterNow(); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Str8 const exe_dir = Dqn_OS_EXEDir(scratch.arena); Dqn_Str8 const code_dir = exe_dir; Dqn_Str8 const build_dir = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Build", DQN_STR_FMT(exe_dir)); Dqn_Str8 const tely_dir = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/External/tely", DQN_STR_FMT(exe_dir)); Dqn_CPPBuildFlagsStyle flags_style = msvc_build ? Dqn_CPPBuildFlagsStyle_MSVC : Dqn_CPPBuildFlagsStyle_GCC; Dqn_FArray32 common_compile_flags = {}; Dqn_FArray32 common_link_flags = {}; if (msvc_build) { Dqn_FArray_AddAssert(&common_compile_flags, DQN_STR8("cl")); Dqn_FArray_AddAssert(&common_compile_flags, DQN_STR8("-W4")); Dqn_FArray_AddAssert(&common_compile_flags, DQN_STR8("-Z7")); Dqn_FArray_AddAssert(&common_compile_flags, DQN_STR8("-MT")); Dqn_FArray_AddAssert(&common_compile_flags, DQN_STR8("-EHsc")); Dqn_FArray_AddAssert(&common_compile_flags, DQN_STR8("-nologo")); Dqn_FArray_AddAssert(&common_link_flags, DQN_STR8("-link")); Dqn_FArray_AddAssert(&common_link_flags, DQN_STR8("-incremental:no")); } else { Dqn_FArray_AddAssert(&common_compile_flags, DQN_STR8("g++")); Dqn_FArray_AddAssert(&common_compile_flags, DQN_STR8("-Wall")); Dqn_FArray_AddAssert(&common_compile_flags, DQN_STR8("-g")); Dqn_FArray_AddAssert(&common_link_flags, DQN_STR8("-lm")); } // NOTE: Assets ================================================================================ uint64_t robocopy_timings[2] = {}; { robocopy_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { robocopy_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_FArray8 common_copy_cmd_args = {}; if (msvc_build) { Dqn_FArray_AddCArrayAssert(&common_copy_cmd_args, { DQN_STR8("robocopy"), DQN_STR8("/NJH"), DQN_STR8("/NJS"), DQN_STR8("/NDL"), DQN_STR8("/NP"), }); } else { Dqn_FArray_AddCArrayAssert(&common_copy_cmd_args, { DQN_STR8("cp"), DQN_STR8("--recursive"), }); } Dqn_Str8 textures_dest = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Data/Textures", DQN_STR_FMT(build_dir)); DQN_HARD_ASSERT(Dqn_Fs_MakeDir(textures_dest)); Dqn_FArray8> copy_cmd_list = {}; if (msvc_build) { Dqn_FArray8 copy_atlas = common_copy_cmd_args; Dqn_FArray_AddCArrayAssert(©_atlas, { Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Data/Textures", DQN_STR_FMT(exe_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Data/Textures atlas.*", DQN_STR_FMT(build_dir)), }); Dqn_FArray_Add(©_cmd_list, copy_atlas); } else { Dqn_FArray8 copy_atlas_txt = common_copy_cmd_args; Dqn_FArray_AddCArrayAssert(©_atlas_txt, { Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Data/Textures/atlas.txt", DQN_STR_FMT(exe_dir)), textures_dest, }); Dqn_FArray_Add(©_cmd_list, copy_atlas_txt); Dqn_FArray8 copy_atlas_png = common_copy_cmd_args; Dqn_FArray_AddCArrayAssert(©_atlas_png, { Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Data/Textures/atlas.png", DQN_STR_FMT(exe_dir)), textures_dest, }); Dqn_FArray_Add(©_cmd_list, copy_atlas_png); } Dqn_FArray8 copy_fonts_cmd = common_copy_cmd_args; Dqn_FArray_AddCArrayAssert(©_fonts_cmd, { Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Data/Fonts", DQN_STR_FMT(exe_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Data/Fonts", DQN_STR_FMT(build_dir)), }); Dqn_FArray_Add(©_cmd_list, copy_fonts_cmd); Dqn_FArray8 copy_audio_cmd = common_copy_cmd_args; Dqn_FArray_AddCArrayAssert(©_audio_cmd, { Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Data/Audio", DQN_STR_FMT(exe_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Data/Audio", DQN_STR_FMT(build_dir)), }); Dqn_FArray_Add(©_cmd_list, copy_audio_cmd); for (Dqn_FArray8 const ©_cmd : copy_cmd_list) { Dqn_Slice copy_cmd_slice = Dqn_FArray_Slice(©_cmd); Dqn_Str8 rendered_line = Dqn_Slice_Str8Render(scratch.arena, copy_cmd_slice, DQN_STR8(" ")); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(rendered_line)); if (!dry_run) { if (msvc_build) { // NOTE: Robocopy returns 1 on success so we don't use the ExecOrAbort function Dqn_OS_Exec(copy_cmd_slice, /*working_dir*/ {}); } else { Dqn_OS_ExecOrAbort(copy_cmd_slice, /*working_dir*/ {}); } } } } // NOTE: Raylib ================================================================================ Dqn_Str8 const raylib_dir = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/External/tely/External/raylib", DQN_STR_FMT(exe_dir)); Dqn_Slice const raylib_base_files = Dqn_Slice_InitCArray(scratch.arena, { Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rcore.c", DQN_STR_FMT(raylib_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/utils.c", DQN_STR_FMT(raylib_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/raudio.c", DQN_STR_FMT(raylib_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rmodels.c", DQN_STR_FMT(raylib_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rtext.c", DQN_STR_FMT(raylib_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rtextures.c", DQN_STR_FMT(raylib_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rshapes.c", DQN_STR_FMT(raylib_dir)), }); Dqn_FArray32 raylib_pc_output_files = {}; uint64_t raylib_pc_timings[2] = {}; { raylib_pc_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { raylib_pc_timings[1] = Dqn_OS_PerfCounterNow(); }; // NOTE: Setup raylib build context ======================================================== Dqn_FArray32 compile_flags = {}; Dqn_CPPBuildContext build_context = {}; { build_context.include_dirs = Dqn_Slice_InitCArray(scratch.arena, { Dqn_FsPath_ConvertF(scratch.arena, "%.*s", DQN_STR_FMT(raylib_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/external/glfw/include", DQN_STR_FMT(raylib_dir)), Dqn_FsPath_ConvertF(scratch.arena, "%.*s/glfw/deps/mingw", DQN_STR_FMT(raylib_dir)), }); if (msvc_build) { Dqn_FArray_AddCArrayAssert(&compile_flags, { DQN_STR8("cl"), DQN_STR8("-w"), DQN_STR8("-Z7"), DQN_STR8("-MT"), DQN_STR8("-EHsc"), DQN_STR8("-nologo"), }); } else { Dqn_FArray_AddCArrayAssert(&compile_flags, { DQN_STR8("gcc"), DQN_STR8("-g"), }); } Dqn_FArray_AddCArrayAssert(&compile_flags, { DQN_STR8("-c"), // Compile object files only DQN_STR8("-D"), DQN_STR8("_DEFAULT_SOURCE"), DQN_STR8("-D"), DQN_STR8("PLATFORM_DESKTOP"), }); build_context.build_dir = build_dir; build_context.compile_flags = Dqn_FArray_Slice(&compile_flags); build_context.flags_style = flags_style; } // NOTE: Compile each file separately with a custom output name ============================ for (Dqn_Str8 base_file : raylib_base_files) { Dqn_Str8 file_stem = Dqn_Str8_FileNameNoExtension(base_file); Dqn_CPPBuildCompileFile build_file = {}; build_file.input_file_path = base_file; build_file.output_file_path = Dqn_Str8_InitF(scratch.allocator, "raylib_%.*s.%s", DQN_STR_FMT(file_stem), msvc_build ? "obj" : "o"); build_context.compile_files = Dqn_Slice_Init(&build_file, 1); Dqn_FArray_AddAssert(&raylib_pc_output_files, build_file.output_file_path); Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLineStr8(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.arena); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_CacheBuild); } // NOTE: Build rlgfw ======================================================================= { Dqn_CPPBuildCompileFile build_file = {}; build_file.input_file_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/rglfw.c", DQN_STR_FMT(raylib_dir)); build_file.output_file_path = Dqn_Str8_InitF(scratch.allocator, "raylib_rglfw.%s", msvc_build ? "obj" : "o"); build_context.compile_files = Dqn_Slice_InitCArray(scratch.arena, {build_file}); Dqn_FArray_AddAssert(&raylib_pc_output_files, build_file.output_file_path); Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLineStr8(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.arena); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_CacheBuild); } } // NOTE: sokol_audio ================================================================================ Dqn_Str8 sokol_audio_source_code_file = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/External/sokol/sokol_audio.c", DQN_STR_FMT(tely_dir)); uint64_t sokol_audio_pc_timings[2] = {}; Dqn_FArray32 sokol_audio_pc_output_files = {}; { sokol_audio_pc_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { sokol_audio_pc_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_Str8 output_file = Dqn_Str8_InitF(scratch.allocator, "sokol_audio.%s", msvc_build ? "obj" : "o"); Dqn_CPPBuildContext build_context = {}; build_context.flags_style = flags_style; build_context.compile_files = Dqn_Slice_InitCArray(scratch.arena, { Dqn_CPPBuildCompileFile{ {}, // Prefix flags {}, // Suffix flags sokol_audio_source_code_file, output_file, }, }); Dqn_FArray_AddAssert(&sokol_audio_pc_output_files, output_file); Dqn_FArray32 compile_flags = common_compile_flags; Dqn_FArray_AddAssert(&compile_flags, DQN_STR8("-c")); build_context.compile_flags = Dqn_FArray_Slice(&compile_flags); build_context.build_dir = build_dir; Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLineStr8(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.arena); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_CacheBuild); } // NOTE: Feely Pona Sprite Packer ============================================================== uint64_t feely_pona_sprite_packer_timings[2] = {}; if (msvc_build) { feely_pona_sprite_packer_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { feely_pona_sprite_packer_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_CPPBuildContext build_context = {}; build_context.flags_style = flags_style; build_context.compile_files = Dqn_Slice_InitCArray(scratch.arena, { Dqn_CPPBuildCompileFile{{} /*Prefix flags*/, {} /*Suffix flags*/, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_sprite_packer.cpp", DQN_STR_FMT(code_dir)) }, }); build_context.compile_flags = Dqn_FArray_Slice(&common_compile_flags); build_context.link_flags = Dqn_FArray_Slice(&common_link_flags); build_context.build_dir = build_dir; Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLineStr8(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.arena); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_CacheBuild); } // NOTE: Feely Pona Link Flags ================================================================= Dqn_FArray32 feely_pona_platform_link_flags = common_link_flags; { // NOTE: Link to raylib object files and windows libs ====================================== Dqn_FArray_AddArrayAssert(&feely_pona_platform_link_flags, raylib_pc_output_files.data, raylib_pc_output_files.size); Dqn_FArray_AddArrayAssert(&feely_pona_platform_link_flags, sokol_audio_pc_output_files.data, sokol_audio_pc_output_files.size); if (msvc_build) { Dqn_FArray_AddCArrayAssert(&feely_pona_platform_link_flags, { DQN_STR8("gdi32.lib"), // raylib DQN_STR8("opengl32.lib"), // raylib DQN_STR8("winmm.lib"), // raylib DQN_STR8("user32.lib"), DQN_STR8("shell32.lib"), }); } else { Dqn_FArray_AddCArrayAssert(&feely_pona_platform_link_flags, { DQN_STR8("-lpthread"), }); #if defined(DQN_OS_UNIX) Dqn_FArray_AddCArrayAssert(&feely_pona_platform_link_flags, { DQN_STR8("-ldl"), DQN_STR8("-lasound"), }); #else Dqn_FArray_AddCArrayAssert(&feely_pona_platform_link_flags, { DQN_STR8("-lgdi32"), // raylib DQN_STR8("-lopengl32"), // raylib DQN_STR8("-lwinmm"), // raylib DQN_STR8("-lole32"), // sokol_audio DQN_STR8("-lbcrypt"), // dqn DQN_STR8("-lwininet"), // dqn DQN_STR8("-ldbghelp"), // dqn }); #endif } } // NOTE: Feely Pona No DLL ===================================================================== uint64_t feely_pona_no_dll_timings[2] = {}; if (!dev_fast_build) { feely_pona_no_dll_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { feely_pona_no_dll_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_CPPBuildCompileFile build_file = {}; build_file.input_file_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_unity.h", DQN_STR_FMT(code_dir)); build_file.output_file_path = Dqn_FsPath_ConvertF(scratch.arena, "terry_cherry"); if (msvc_build) { build_file.prefix_flags = Dqn_Slice_InitCArray(scratch.arena, {DQN_STR8("-Tp")}); } else { build_file.prefix_flags = Dqn_Slice_InitCArray(scratch.arena, {DQN_STR8("-xc++")}); build_file.suffix_flags = Dqn_Slice_InitCArray(scratch.arena, {DQN_STR8("-xnone")}); } Dqn_FArray32 compile_flags = common_compile_flags; Dqn_FArray_AddCArrayAssert(&compile_flags, { DQN_STR8("-D"), DQN_STR8("TELY_WITH_PLATFORM"), DQN_STR8("-D"), DQN_STR8("FEELY_PONA_IMPLEMENTATION"), }); Dqn_CPPBuildContext feely_pona_no_dll_build_context = {}; feely_pona_no_dll_build_context.flags_style = flags_style; feely_pona_no_dll_build_context.compile_files = Dqn_Slice_Init(&build_file, 1); feely_pona_no_dll_build_context.include_dirs = Dqn_Slice_Init(DQN_CAST(Dqn_Str8 *)&raylib_dir, 1); feely_pona_no_dll_build_context.compile_flags = Dqn_FArray_Slice(&compile_flags); feely_pona_no_dll_build_context.build_dir = build_dir; feely_pona_no_dll_build_context.link_flags = Dqn_FArray_Slice(&feely_pona_platform_link_flags); Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLineStr8(feely_pona_no_dll_build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.arena); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) Dqn_CPPBuild_ExecOrAbort(feely_pona_no_dll_build_context, Dqn_CPPBuildMode_AlwaysRebuild); } // NOTE: Feely Pona DLL ======================================================================== uint64_t feely_pona_dll_timings[2] = {}; if (msvc_build) { feely_pona_dll_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { feely_pona_dll_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_CPPBuildCompileFile build_file = {}; build_file.input_file_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_unity.h", DQN_STR_FMT(code_dir)); build_file.output_file_path = Dqn_FsPath_ConvertF(scratch.arena, "terry_cherry_dev_dll"); build_file.prefix_flags = Dqn_Slice_InitCArray(scratch.arena, {DQN_STR8("-Tp")}); Dqn_FArray32 compile_flags = common_compile_flags; Dqn_FArray_AddAssert(&compile_flags, DQN_STR8("-LD")); Dqn_FArray_AddAssert(&compile_flags, DQN_STR8("-D FEELY_PONA_IMPLEMENTATION")); if (!dev_fast_build) Dqn_FArray_AddAssert(&compile_flags, DQN_STR8("-analyze")); Dqn_CPPBuildContext build_context = {}; build_context.flags_style = flags_style; build_context.compile_files = Dqn_Slice_Init(&build_file, 1); build_context.compile_flags = Dqn_FArray_Slice(&compile_flags); build_context.link_flags = {}; build_context.build_dir = build_dir; Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLineStr8(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.arena); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_AlwaysRebuild); } // NOTE: Feely Pona platform =================================================================== uint64_t feely_pona_platform_timings[2] = {}; if (msvc_build) { feely_pona_platform_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { feely_pona_platform_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_CPPBuildCompileFile build_file = {}; build_file.input_file_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_unity.h", DQN_STR_FMT(code_dir)); build_file.output_file_path = Dqn_FsPath_ConvertF(scratch.arena, "terry_cherry_dev", DQN_STR_FMT(code_dir)); build_file.prefix_flags = Dqn_Slice_InitCArray(scratch.arena, {DQN_STR8("-Tp")}); Dqn_FArray32 compile_flags = common_compile_flags; Dqn_FArray_AddCArrayAssert(&compile_flags, { DQN_STR8("-D"), DQN_STR8("TELY_WITH_PLATFORM"), DQN_STR8("-D"), DQN_STR8("TELY_WITH_PLATFORM_DLL"), }); Dqn_CPPBuildContext build_context = {}; build_context.flags_style = flags_style; build_context.compile_files = Dqn_Slice_Init(&build_file, 1); build_context.compile_flags = Dqn_FArray_Slice(&compile_flags); build_context.link_flags = Dqn_FArray_Slice(&feely_pona_platform_link_flags); build_context.build_dir = build_dir; build_context.include_dirs = Dqn_Slice_InitCArray(scratch.arena, {raylib_dir}); Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLineStr8(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.arena); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) { Dqn_Str8 exe_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/terry_cherry_dev.exe", DQN_STR_FMT(build_dir)); bool exe_is_locked = false; if (Dqn_Fs_Exists(exe_path)) { Dqn_FsFile exe_file = Dqn_Fs_OpenFile(exe_path, Dqn_FsFileOpen_OpenIfExist, Dqn_FsFileAccess_Read | Dqn_FsFileAccess_Write); exe_is_locked = exe_file.error_size; Dqn_Fs_CloseFile(&exe_file); } if (!exe_is_locked) { Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_AlwaysRebuild); } } } // NOTE: raylib emscripten ===================================================================== uint64_t raylib_emscripten_timings[2] = {}; uint64_t feely_pona_emscripten_timings[2] = {}; if (msvc_build && target_web) { Dqn_Str8 const raylib_emscripten_lib_name = DQN_STR8("raylib_emscripten.a"); bool debug_build = false; Dqn_FArray32 build_specific_compile_flags = {}; if (debug_build) { Dqn_FArray_AddCArrayAssert(&build_specific_compile_flags, {DQN_STR8("-s"), DQN_STR8("ASSERTIONS=2")}); Dqn_FArray_AddCArrayAssert(&build_specific_compile_flags, {DQN_STR8("-s"), DQN_STR8("SAFE_HEAP=0")}); Dqn_FArray_AddCArrayAssert(&build_specific_compile_flags, {DQN_STR8("-s"), DQN_STR8("STACK_OVERFLOW_CHECK=2")}); Dqn_FArray_AddCArrayAssert(&build_specific_compile_flags, {DQN_STR8("--profiling-funcs")}); // Expose function names in stack trace Dqn_FArray_AddCArrayAssert(&build_specific_compile_flags, {DQN_STR8("-g")}); // Debug symbols } else { Dqn_FArray_AddCArrayAssert(&build_specific_compile_flags, {DQN_STR8("-Os")}); // Optimise for size } // NOTE: Compile each raylib file separately with emcc ===================================== { raylib_emscripten_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { raylib_emscripten_timings[1] = Dqn_OS_PerfCounterNow(); }; // NOTE: Setup build context =========================================================== Dqn_FArray32 emscripten_obj_files = {}; Dqn_CPPBuildContext raylib_emscripten_build_context = {}; raylib_emscripten_build_context.flags_style = Dqn_CPPBuildFlagsStyle_GCC; for (Dqn_Str8 base_file : raylib_base_files) { Dqn_Str8 file_stem = Dqn_Str8_FileNameNoExtension(base_file); // NOTE: Append "emscripten" suffix to the object files Dqn_CPPBuildCompileFile build_file = {}; build_file.input_file_path = base_file; build_file.output_file_path = Dqn_Str8_InitF(scratch.allocator, "raylib_%.*s_emscripten.o", DQN_STR_FMT(file_stem)); Dqn_FArray32 compile_flags = {}; Dqn_FArray_AddCArrayAssert(&compile_flags, { DQN_STR8("cmd"), DQN_STR8("/C"), DQN_STR8("emcc.bat"), DQN_STR8("-c"), // Compile and assemble, but do not link DQN_STR8("-Wall"), DQN_STR8("-Os"), // Optimize for size DQN_STR8("-D PLATFORM_WEB"), DQN_STR8("-D GRAPHICS_API_OPENGL_ES2"), }); Dqn_FArray_AddArrayAssert(&compile_flags, build_specific_compile_flags.data, build_specific_compile_flags.size); raylib_emscripten_build_context.compile_files = Dqn_Slice_Init(&build_file, 1); raylib_emscripten_build_context.compile_flags = Dqn_FArray_Slice(&compile_flags); raylib_emscripten_build_context.build_dir = build_dir; Dqn_FArray_AddAssert(&emscripten_obj_files, build_file.output_file_path); Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLineStr8(raylib_emscripten_build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.arena); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) Dqn_CPPBuild_ExecOrAbort(raylib_emscripten_build_context, Dqn_CPPBuildMode_CacheBuild); } { Dqn_Str8 base_file = sokol_audio_source_code_file; // NOTE: Append "emscripten" suffix to the object files Dqn_CPPBuildCompileFile build_file = {}; build_file.input_file_path = base_file; build_file.output_file_path = Dqn_Str8_InitF(scratch.allocator, "sokol_audio_emscripten.o"); Dqn_FArray32 compile_flags = Dqn_FArray_InitCArray({ DQN_STR8("cmd"), DQN_STR8("/C"), DQN_STR8("emcc.bat"), DQN_STR8("-c"), // Compile and assemble, but do not link DQN_STR8("-Wall"), DQN_STR8("-Os"), // Optimize for size }); Dqn_FArray_AddArrayAssert(&compile_flags, build_specific_compile_flags.data, build_specific_compile_flags.size); raylib_emscripten_build_context.compile_files = Dqn_Slice_Init(&build_file, 1); raylib_emscripten_build_context.compile_flags = Dqn_FArray_Slice(&compile_flags); raylib_emscripten_build_context.build_dir = build_dir; Dqn_FArray_AddAssert(&emscripten_obj_files, build_file.output_file_path); Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLineStr8(raylib_emscripten_build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.arena); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) Dqn_CPPBuild_ExecOrAbort(raylib_emscripten_build_context, Dqn_CPPBuildMode_CacheBuild); } // NOTE: Build the wasm raylib+sokol_audio library ===================================== { Dqn_FArray32 cmd_args = Dqn_FArray_InitCArray({ DQN_STR8("cmd"), DQN_STR8("/C"), DQN_STR8("emar.bat"), DQN_STR8("rcs"), raylib_emscripten_lib_name, }); Dqn_FArray_AddArrayAssert(&cmd_args, emscripten_obj_files.data, emscripten_obj_files.size); Dqn_Slice cmd_slice = Dqn_FArray_Slice(&cmd_args); Dqn_Str8 cmd = Dqn_Slice_Str8Render(scratch.arena, cmd_slice, DQN_STR8(" ") /*separator*/); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) Dqn_OS_ExecOrAbort(cmd_slice, build_dir); } } // NOTE: feely pona emscripten ============================================================= { feely_pona_emscripten_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { feely_pona_emscripten_timings[1] = Dqn_OS_PerfCounterNow(); }; // NOTE: feely pona emscripten shell ======================================================= Dqn_Str8 html_shell_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_emscripten_shell.html", DQN_STR_FMT(build_dir)); { Dqn_Str8 html_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_emscripten_shell.html", DQN_STR_FMT(code_dir)); Dqn_Str8 html_buffer = Dqn_Fs_Read(html_path, scratch.allocator); if (!DQN_CHECKF(html_buffer.size, "Failed to read Emscripten HTML shell file. The file at\n\n '%.*s'\n\ndoes not exist or is not readable", DQN_STR_FMT(html_path))) return -1; Dqn_Str8 version_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_version.txt", DQN_STR_FMT(code_dir)); Dqn_Str8 version_buffer = Dqn_Fs_Read(version_path, scratch.allocator); Dqn_Str8SplitAllocResult version_parts = Dqn_Str8_SplitAlloc(scratch.allocator, version_buffer, DQN_STR8("\n")); if (!DQN_CHECKF(version_parts.size == 3, "Version file '%.*s' must have 3 lines containing, date, commit hash and number of commits. The buffer we tried extracting information from was\n\n%.*s\n\n", DQN_STR_FMT(version_path), DQN_STR_FMT(version_buffer))) { return -1; } Dqn_Str8 date = Dqn_Str8_TrimWhitespaceAround(version_parts.data[0]); Dqn_Str8 commit_hash = Dqn_Str8_TrimWhitespaceAround(version_parts.data[1]); Dqn_Str8 commit_count = Dqn_Str8_TrimWhitespaceAround(version_parts.data[2]); Dqn_Str8 version_text = Dqn_Str8_InitF(scratch.allocator, "%.*s edition rev. %.*s-%.*s", DQN_STR_FMT(date), DQN_STR_FMT(commit_count), DQN_STR_FMT(commit_hash)); Dqn_Str8 html_buffer_processed = Dqn_Str8_Replace(html_buffer, DQN_STR8("@version@"), version_text, 0 /*start_index*/, scratch.allocator); if (!DQN_CHECKF(Dqn_Fs_Write(html_shell_path, html_buffer_processed), "Failed to write Emscripten HTML shell with the project version inserted into it. We were unable to write to the target location\n\n '%.*s'\n", DQN_STR_FMT(html_shell_path))) return -1; } // NOTE: Compile with emcc ============================================================= Dqn_Str8 output_name = DQN_STR8("Terry_Cherry"); Dqn_CPPBuildCompileFile build_file = {}; build_file.prefix_flags = Dqn_Slice_InitCArray(scratch.arena, {DQN_STR8("-xc++")}); build_file.suffix_flags = Dqn_Slice_InitCArray(scratch.arena, {DQN_STR8("-xnone")}); build_file.input_file_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_unity.cpp", DQN_STR_FMT(code_dir)); build_file.output_file_path = Dqn_Str8_InitF(scratch.allocator, "%.*s.html", DQN_STR_FMT(output_name)); Dqn_CPPBuildContext build_context = {}; build_context.flags_style = Dqn_CPPBuildFlagsStyle_GCC; build_context.compile_file_obj_suffix = DQN_CPP_BUILD_OBJ_SUFFIX_O; build_context.compile_files = Dqn_Slice_Init(&build_file, 1); Dqn_FArray32 compile_flags = Dqn_FArray_InitCArray({ DQN_STR8("cmd"), DQN_STR8("/C"), DQN_STR8("emcc.bat"), DQN_STR8("-Wall"), DQN_STR8("--shell-file"), html_shell_path, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s", DQN_STR_FMT(build_dir), DQN_STR_FMT(raylib_emscripten_lib_name)), DQN_STR8("-s"), DQN_STR8("USE_GLFW=3"), DQN_STR8("-s"), DQN_STR8("TOTAL_MEMORY=512MB"), DQN_STR8("-s"), DQN_STR8("TOTAL_STACK=32MB"), DQN_STR8("-s"), DQN_STR8("ALLOW_MEMORY_GROWTH"), DQN_STR8("--preload-file"), DQN_STR8("Data"), DQN_STR8("-msimd128"), DQN_STR8("-msse2"), DQN_STR8("-D"), DQN_STR8("TELY_WITH_PLATFORM"), DQN_STR8("-D"), DQN_STR8("FEELY_PONA_IMPLEMENTATION"), }); Dqn_FArray_AddArrayAssert(&compile_flags, build_specific_compile_flags.data, build_specific_compile_flags.size); build_context.compile_flags = Dqn_FArray_Slice(&compile_flags); build_context.build_dir = build_dir; Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLineStr8(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.arena); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd)); if (!dry_run) Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_CacheBuild); // NOTE: Move the files to a directory Dqn_Str8 folder_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s", DQN_STR_FMT(build_dir), DQN_STR_FMT(output_name)); if (!Dqn_Fs_DirExists(folder_path)) { Dqn_FArray32 mkdir_cmd = {}; Dqn_FArray_AddAssert(&mkdir_cmd, DQN_STR8("mkdir")); Dqn_FArray_AddAssert(&mkdir_cmd, folder_path); Dqn_OS_ExecOrAbort(Dqn_FArray_Slice(&mkdir_cmd), {}); } Dqn_Str8 const generated_file_extension[] = { DQN_STR8("data"), DQN_STR8("html"), DQN_STR8("js"), DQN_STR8("wasm"), }; for (Dqn_Str8 file_ext : generated_file_extension) { Dqn_Str8 src_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.%.*s", DQN_STR_FMT(build_dir), DQN_STR_FMT(output_name), DQN_STR_FMT(file_ext)); Dqn_Str8 dest_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.%.*s", DQN_STR_FMT(folder_path), DQN_STR_FMT(output_name), DQN_STR_FMT(file_ext)); Dqn_FArray32 cmd_args = {}; Dqn_FArray_AddAssert(&cmd_args, DQN_STR8("cmd")); Dqn_FArray_AddAssert(&cmd_args, DQN_STR8("/C")); Dqn_FArray_AddAssert(&cmd_args, DQN_STR8("move")); Dqn_FArray_AddAssert(&cmd_args, DQN_STR8("/Y")); Dqn_FArray_AddAssert(&cmd_args, src_path); Dqn_FArray_AddAssert(&cmd_args, dest_path); Dqn_Slice cmd_slice = Dqn_FArray_Slice(&cmd_args); Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(scratch.arena, cmd_slice, DQN_STR8(" ")); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "[BUILD] Executing '%.*s'", DQN_STR_FMT(cmd_rendered)); if (!dry_run) Dqn_OS_ExecOrAbort(cmd_slice, build_dir); } } } build_timings[1] = Dqn_OS_PerfCounterNow(); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "\n-- Dqn_CPPBuild Timings (%.2fms)", Dqn_OS_PerfCounterMs(build_timings[0], build_timings[1])); 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_pc_timings[0], raylib_pc_timings[1])); Dqn_Print_StdLnF(Dqn_PrintStd_Out, " sokol_audio: %.2fms", Dqn_OS_PerfCounterMs(sokol_audio_pc_timings[0], sokol_audio_pc_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-raylib): %.2fms", Dqn_OS_PerfCounterMs(feely_pona_platform_timings[0], feely_pona_platform_timings[1])); if (target_web) { Dqn_Print_StdLnF(Dqn_PrintStd_Out, " raylib (emscripten): %.2fms", Dqn_OS_PerfCounterMs(raylib_emscripten_timings[0], raylib_emscripten_timings[1])); Dqn_Print_StdLnF(Dqn_PrintStd_Out, " feely pona (emscripten): %.2fms", Dqn_OS_PerfCounterMs(feely_pona_emscripten_timings[0], feely_pona_emscripten_timings[1])); } return 0; }