// #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]") 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; 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 { PRINT_HELP; return 0; } } #if 0 RebuildProgramIfRequired(argc, argv); #else Dqn_Print_StdLnF(Dqn_PrintStd_Out, "-- Dqn_CPPBuild v0"); #endif 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_Slice common_compile_flags = Dqn_Slice_InitCArrayCopy(scratch.arena, { DQN_STR8("cl"), DQN_STR8("/W4"), DQN_STR8("/Z7"), DQN_STR8("/MT"), DQN_STR8("/EHsc"), DQN_STR8("/nologo"), }); Dqn_Slice common_link_flags = Dqn_Slice_InitCArrayCopy(scratch.arena, { DQN_STR8("/link"), DQN_STR8("/incremental:no"), }); // NOTE: Assets ================================================================================ uint64_t robocopy_timings[2] = {}; { robocopy_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { robocopy_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_Str8 robocopy_cmd[] = { Dqn_Str8_InitF(scratch.allocator, "robocopy /NJH /NJS /NDL /NP %.*s\\Data\\Textures %.*s\\Data\\Textures atlas.*", DQN_STR_FMT(exe_dir), DQN_STR_FMT(build_dir)), Dqn_Str8_InitF(scratch.allocator, "robocopy /MIR /NJH /NJS /NDL /NP %.*s\\Data\\Fonts %.*s\\Data\\Fonts", DQN_STR_FMT(exe_dir), DQN_STR_FMT(build_dir)), Dqn_Str8_InitF(scratch.allocator, "robocopy /MIR /NJH /NJS /NDL /NP %.*s\\Data\\Audio %.*s\\Data\\Audio", DQN_STR_FMT(exe_dir), DQN_STR_FMT(build_dir)), }; for (Dqn_Str8 cmd : robocopy_cmd) { if (dry_run) Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); else Dqn_OS_Exec(cmd, /*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_InitCArrayCopy(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_List raylib_pc_output_files = Dqn_List_Init(scratch.arena, 16); 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_CPPBuildContext build_context = {}; { build_context.include_dirs = Dqn_Slice_InitCArrayCopy(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)), }); build_context.compile_flags = Dqn_Slice_InitCArrayCopy(scratch.arena, { DQN_STR8("cl"), DQN_STR8("/w"), DQN_STR8("/c"), DQN_STR8("/D _DEFAULT_SOURCE"), DQN_STR8("/D PLATFORM_DESKTOP"), DQN_STR8("/Z7"), DQN_STR8("/MT"), DQN_STR8("/EHsc"), DQN_STR8("/nologo"), }); build_context.build_dir = build_dir; build_context.compiler = Dqn_CPPBuildCompiler_MSVC; } // 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.obj", DQN_STR_FMT(file_stem)); build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, {build_file}); Dqn_List_Add(&raylib_pc_output_files, build_file.output_file_path); if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { 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.obj"); build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, {build_file}); Dqn_List_Add(&raylib_pc_output_files, build_file.output_file_path); if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { 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_List sokol_audio_pc_output_files = Dqn_List_Init(scratch.arena, 16); { sokol_audio_pc_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { sokol_audio_pc_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_CPPBuildContext build_context = {}; build_context.compiler = Dqn_CPPBuildCompiler_MSVC; build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_CPPBuildCompileFile{ {}, // Args sokol_audio_source_code_file, }, }); Dqn_List_Add(&sokol_audio_pc_output_files, DQN_STR8("sokol_audio.obj")); Dqn_List compile_flags = Dqn_List_InitSliceCopy(scratch.arena, 16, common_compile_flags); Dqn_List_Add(&compile_flags, DQN_STR8("/c")); build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena); build_context.build_dir = build_dir; if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_CacheBuild); } } // NOTE: QOI Converter ========================================================================= uint64_t qoi_converter_timings[2] = {}; { qoi_converter_timings[0] = Dqn_OS_PerfCounterNow(); DQN_DEFER { qoi_converter_timings[1] = Dqn_OS_PerfCounterNow(); }; Dqn_CPPBuildContext build_context = {}; build_context.compiler = Dqn_CPPBuildCompiler_MSVC; build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/External/qoiconv.c", DQN_STR_FMT(code_dir)) }, }); build_context.compile_flags = Dqn_Slice_InitCArrayCopy(scratch.arena, {DQN_STR8("cl"), DQN_STR8("-O2"), DQN_STR8("-MT"), DQN_STR8("/nologo")}); build_context.link_flags = Dqn_Slice_InitCArrayCopy(scratch.arena, {DQN_STR8("/link"), DQN_STR8("/incremental:no")}); build_context.include_dirs = Dqn_Slice_InitCArrayCopy(scratch.arena, {Dqn_FsPath_ConvertF(scratch.arena, "%.*s/External/stb", DQN_STR_FMT(tely_dir))}); build_context.build_dir = build_dir; if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_CacheBuild); } } // 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_CPPBuildContext build_context = {}; build_context.compiler = Dqn_CPPBuildCompiler_MSVC; build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_sprite_packer.cpp", DQN_STR_FMT(code_dir)) }, }); build_context.compile_flags = common_compile_flags; build_context.link_flags = common_link_flags; build_context.build_dir = build_dir; if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_CacheBuild); } } // NOTE: Feely Pona Link Flags ================================================================= Dqn_Slice feely_pona_platform_link_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_ListIterator it = {}; Dqn_List_Iterate(&raylib_pc_output_files, &it, 0); ) Dqn_List_Add(&link_flags, *it.data); for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&sokol_audio_pc_output_files, &it, 0); ) Dqn_List_Add(&link_flags, *it.data); Dqn_List_Add(&link_flags, DQN_STR8("gdi32.lib")); Dqn_List_Add(&link_flags, DQN_STR8("opengl32.lib")); Dqn_List_Add(&link_flags, DQN_STR8("winmm.lib")); Dqn_List_Add(&link_flags, DQN_STR8("user32.lib")); Dqn_List_Add(&link_flags, DQN_STR8("shell32.lib")); } feely_pona_platform_link_flags = Dqn_List_ToSliceCopy(&link_flags, scratch.arena); } // 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"); build_file.flags = Dqn_Slice_InitCArrayCopy(scratch.arena, {DQN_STR8("/Tp")}); Dqn_List compile_flags = Dqn_List_InitSliceCopy(scratch.arena, 16, common_compile_flags); Dqn_List_AddCArray(&compile_flags, { DQN_STR8("/D TELY_WITH_PLATFORM"), DQN_STR8("/D FEELY_PONA_IMPLEMENTATION"), }); Dqn_CPPBuildContext feely_pona_no_dll_build_context = {}; feely_pona_no_dll_build_context.compiler = Dqn_CPPBuildCompiler_MSVC; feely_pona_no_dll_build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, {build_file}); feely_pona_no_dll_build_context.include_dirs = Dqn_Slice_InitCArrayCopy(scratch.arena, {raylib_dir}); feely_pona_no_dll_build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena); feely_pona_no_dll_build_context.build_dir = build_dir; feely_pona_no_dll_build_context.link_flags = feely_pona_platform_link_flags; if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(feely_pona_no_dll_build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { Dqn_CPPBuild_ExecOrAbort(feely_pona_no_dll_build_context, Dqn_CPPBuildMode_AlwaysRebuild); } } // 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_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.flags = Dqn_Slice_InitCArrayCopy(scratch.arena, {DQN_STR8("/Tp")}); Dqn_List compile_flags = Dqn_List_InitSliceCopy(scratch.arena, 16, common_compile_flags); Dqn_List_AddCArray(&compile_flags, { DQN_STR8("/LD"), DQN_STR8("/D FEELY_PONA_IMPLEMENTATION"), }); if (!dev_fast_build) Dqn_List_Add(&compile_flags, DQN_STR8("/analyze")); Dqn_CPPBuildContext build_context = {}; build_context.compiler = Dqn_CPPBuildCompiler_MSVC; build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, {build_file}); build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena); build_context.link_flags = {}; build_context.build_dir = build_dir; if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_AlwaysRebuild); } } // 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_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.flags = Dqn_Slice_InitCArrayCopy(scratch.arena, {DQN_STR8("/Tp")}); Dqn_List compile_flags = Dqn_List_InitSliceCopy(scratch.arena, 128, common_compile_flags); Dqn_List_AddCArray(&compile_flags, { DQN_STR8("/D TELY_WITH_PLATFORM"), DQN_STR8("/D TELY_WITH_PLATFORM_DLL"), }); Dqn_CPPBuildContext build_context = {}; build_context.compiler = Dqn_CPPBuildCompiler_MSVC; build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, {build_file}); build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena); build_context.link_flags = feely_pona_platform_link_flags; build_context.build_dir = build_dir; build_context.include_dirs = Dqn_Slice_InitCArrayCopy(scratch.arena, {raylib_dir}); if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { 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 (target_web) { Dqn_Str8 const raylib_emscripten_lib_name = DQN_STR8("raylib_emscripten.a"); bool debug_build = false; Dqn_List build_specific_compile_flags = {}; if (debug_build) { build_specific_compile_flags = Dqn_List_InitCArrayCopy(scratch.arena, 32, { DQN_STR8("-s"), DQN_STR8("ASSERTIONS=2"), DQN_STR8("-s"), DQN_STR8("SAFE_HEAP=0"), DQN_STR8("-s"), DQN_STR8("STACK_OVERFLOW_CHECK=2"), DQN_STR8("--profiling-funcs"), // Expose function names in stack trace DQN_STR8("-g"), // Debug symbols }); } else { build_specific_compile_flags = Dqn_List_InitCArrayCopy(scratch.arena, 32, { 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_List emscripten_obj_files = Dqn_List_Init(scratch.arena, 16); Dqn_CPPBuildContext raylib_emscripten_build_context = {}; raylib_emscripten_build_context.compiler = Dqn_CPPBuildCompiler_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_List compile_flags = Dqn_List_InitCArrayCopy(scratch.arena, 32, { 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_List_AddList(&compile_flags, build_specific_compile_flags); raylib_emscripten_build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, {build_file}); raylib_emscripten_build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena); raylib_emscripten_build_context.build_dir = build_dir; Dqn_List_Add(&emscripten_obj_files, build_file.output_file_path); if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(raylib_emscripten_build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { 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_List compile_flags = Dqn_List_InitCArrayCopy(scratch.arena, 32, { 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_List_AddList(&compile_flags, build_specific_compile_flags); raylib_emscripten_build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, {build_file}); raylib_emscripten_build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena); raylib_emscripten_build_context.build_dir = build_dir; Dqn_List_Add(&emscripten_obj_files, build_file.output_file_path); if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(raylib_emscripten_build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { Dqn_CPPBuild_ExecOrAbort(raylib_emscripten_build_context, Dqn_CPPBuildMode_CacheBuild); } } // NOTE: Build the wasm raylib+sokol_audio library ===================================== { Dqn_Str8Builder builder = {}; builder.allocator = scratch.allocator; Dqn_Str8Builder_AppendF(&builder, "cmd /C emar.bat rcs %.*s", DQN_STR_FMT(raylib_emscripten_lib_name)); for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&emscripten_obj_files, &it, 0); ) Dqn_Str8Builder_AppendF(&builder, " %.*s", DQN_STR_FMT(*it.data)); Dqn_Str8 cmd = Dqn_Str8Builder_Build(&builder, scratch.allocator); if (dry_run) { Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { Dqn_OS_ExecOrAbort(cmd, 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_CPPBuildContext build_context = {}; build_context.compile_file_obj_suffix = DQN_CPP_BUILD_OBJ_SUFFIX_O; build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, { Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_unity.cpp", DQN_STR_FMT(code_dir)) }, }); Dqn_Str8 output_name = DQN_STR8("Terry_Cherry"); Dqn_List compile_flags = Dqn_List_InitCArrayCopy(scratch.arena, 32, { DQN_STR8("cmd"), DQN_STR8("/C"), DQN_STR8("emcc.bat"), DQN_STR8("-o"), Dqn_Str8_InitF(scratch.allocator, "%.*s.html", DQN_STR_FMT(output_name)), 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_List_AddList(&compile_flags, build_specific_compile_flags); build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena); build_context.build_dir = build_dir; if (dry_run) { Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator); Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { 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_Str8 mkdir_cmd = Dqn_Str8_InitF(scratch.allocator, "mkdir %.*s", DQN_STR_FMT(folder_path)); Dqn_OS_ExecOrAbort(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_Str8 cmd = Dqn_Str8_InitF(scratch.allocator, "cmd /C move /Y %.*s %.*s", DQN_STR_FMT(src_path), DQN_STR_FMT(dest_path)); if (dry_run) { Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd)); } else { Dqn_OS_ExecOrAbort(cmd, 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, " qoi_converter: %.2fms", Dqn_OS_PerfCounterMs(qoi_converter_timings[0], qoi_converter_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; }