// build.cpp — Build script for autosample // // Bootstrap (one-time): // Windows: cl /nologo build.cpp // macOS: c++ build.cpp -o build // After that, just run: ./build (or build.exe on Windows) #define BUILD_IMPLEMENTATION #include "build.h" //////////////////////////////// // lunasvg/plutovg static library build helpers static const char *plutovg_sources[] = { "vendor/lunasvg/plutovg/source/plutovg-blend.c", "vendor/lunasvg/plutovg/source/plutovg-canvas.c", "vendor/lunasvg/plutovg/source/plutovg-font.c", "vendor/lunasvg/plutovg/source/plutovg-ft-math.c", "vendor/lunasvg/plutovg/source/plutovg-ft-raster.c", "vendor/lunasvg/plutovg/source/plutovg-ft-stroker.c", "vendor/lunasvg/plutovg/source/plutovg-matrix.c", "vendor/lunasvg/plutovg/source/plutovg-paint.c", "vendor/lunasvg/plutovg/source/plutovg-path.c", "vendor/lunasvg/plutovg/source/plutovg-rasterize.c", "vendor/lunasvg/plutovg/source/plutovg-surface.c", }; static const char *lunasvg_sources[] = { "vendor/lunasvg/source/graphics.cpp", "vendor/lunasvg/source/lunasvg.cpp", "vendor/lunasvg/source/svgelement.cpp", "vendor/lunasvg/source/svggeometryelement.cpp", "vendor/lunasvg/source/svglayoutstate.cpp", "vendor/lunasvg/source/svgpaintelement.cpp", "vendor/lunasvg/source/svgparser.cpp", "vendor/lunasvg/source/svgproperty.cpp", "vendor/lunasvg/source/svgrenderstate.cpp", "vendor/lunasvg/source/svgtextelement.cpp", }; //////////////////////////////// // FreeType source files static const char *freetype_sources[] = { "vendor/freetype/src/base/ftsystem.c", "vendor/freetype/src/base/ftinit.c", "vendor/freetype/src/base/ftdebug.c", "vendor/freetype/src/base/ftbase.c", "vendor/freetype/src/base/ftbbox.c", "vendor/freetype/src/base/ftglyph.c", "vendor/freetype/src/base/ftbitmap.c", "vendor/freetype/src/base/ftmm.c", "vendor/freetype/src/truetype/truetype.c", "vendor/freetype/src/sfnt/sfnt.c", "vendor/freetype/src/smooth/smooth.c", "vendor/freetype/src/autofit/autofit.c", "vendor/freetype/src/psnames/psnames.c", "vendor/freetype/src/pshinter/pshinter.c", "vendor/freetype/src/gzip/ftgzip.c", }; //////////////////////////////// // Font embedding — reads a .ttf and writes a C header with the data as a byte array static bool embed_font_file(const char *ttf_path, const char *header_path) { if (!needs_rebuild1(header_path, ttf_path)) { build_log(LOG_INFO, "Font header %s is up to date", header_path); return true; } String_Builder sb = {}; if (!sb_read_file(&sb, ttf_path)) return false; FILE *out = fopen(header_path, "wb"); if (!out) { build_log(LOG_ERROR, "Could not open %s for writing", header_path); sb_free(&sb); return false; } fprintf(out, "// Auto-generated from %s — do not edit\n", ttf_path); fprintf(out, "#pragma once\n\n"); fprintf(out, "static const unsigned char font_inter_data[] = {\n"); for (size_t i = 0; i < sb.count; i++) { if (i % 16 == 0) fprintf(out, " "); fprintf(out, "0x%02x,", (unsigned char)sb.items[i]); if (i % 16 == 15 || i == sb.count - 1) fprintf(out, "\n"); } fprintf(out, "};\n\n"); fprintf(out, "static const unsigned int font_inter_size = %zu;\n", sb.count); fclose(out); build_log(LOG_INFO, "Generated %s (%zu bytes)", header_path, sb.count); sb_free(&sb); return true; } #ifdef __APPLE__ //////////////////////////////// // macOS build (clang++ with Objective-C++) static const char *frameworks[] = { "-framework", "Metal", "-framework", "Cocoa", "-framework", "CoreAudio", "-framework", "AudioToolbox", "-framework", "CoreMIDI", "-framework", "QuartzCore", "-framework", "CoreFoundation", }; static bool build_lunasvg_lib(const char *build_dir, bool debug) { const char *obj_dir = temp_sprintf("%s/lunasvg_obj", build_dir); const char *lib_path = "vendor/lunasvg/liblunasvg.a"; // Collect all source paths to check if rebuild is needed { const char *all_sources[ARRAY_LEN(plutovg_sources) + ARRAY_LEN(lunasvg_sources)]; size_t n = 0; for (size_t i = 0; i < ARRAY_LEN(plutovg_sources); i++) all_sources[n++] = plutovg_sources[i]; for (size_t i = 0; i < ARRAY_LEN(lunasvg_sources); i++) all_sources[n++] = lunasvg_sources[i]; if (!needs_rebuild(lib_path, all_sources, n)) { build_log(LOG_INFO, "lunasvg is up to date"); return true; } } if (!mkdir_if_not_exists(obj_dir)) return false; // Compile plutovg C sources for (size_t i = 0; i < ARRAY_LEN(plutovg_sources); i++) { const char *src = plutovg_sources[i]; const char *base = strrchr(src, '/'); base = base ? base + 1 : src; char obj_name[256]; snprintf(obj_name, sizeof(obj_name), "%s", base); char *dot = strrchr(obj_name, '.'); if (dot) { dot[1] = 'o'; dot[2] = '\0'; } const char *obj_path = temp_sprintf("%s/%s", obj_dir, obj_name); Cmd cmd = {}; cmd_append(&cmd, "clang"); cmd_append(&cmd, "-std=c11", "-c"); cmd_append(&cmd, "-DPLUTOVG_BUILD", "-DPLUTOVG_BUILD_STATIC"); cmd_append(&cmd, "-Ivendor/lunasvg/plutovg/include"); cmd_append(&cmd, "-Ivendor/lunasvg/plutovg/source"); cmd_append(&cmd, "-Wall", "-Wno-unused-function", "-Wno-unused-parameter"); if (debug) { cmd_append(&cmd, "-g", "-O0"); } else { cmd_append(&cmd, "-O2"); } cmd_append(&cmd, "-o", obj_path); cmd_append(&cmd, src); if (!cmd_run(&cmd)) return false; } // Compile lunasvg C++ sources for (size_t i = 0; i < ARRAY_LEN(lunasvg_sources); i++) { const char *src = lunasvg_sources[i]; const char *base = strrchr(src, '/'); base = base ? base + 1 : src; char obj_name[256]; snprintf(obj_name, sizeof(obj_name), "%s", base); char *dot = strrchr(obj_name, '.'); if (dot) { dot[1] = 'o'; dot[2] = '\0'; } const char *obj_path = temp_sprintf("%s/%s", obj_dir, obj_name); Cmd cmd = {}; cmd_append(&cmd, "clang++"); cmd_append(&cmd, "-std=c++17", "-c"); cmd_append(&cmd, "-fno-exceptions", "-fno-rtti"); cmd_append(&cmd, "-DLUNASVG_BUILD", "-DLUNASVG_BUILD_STATIC", "-DPLUTOVG_BUILD_STATIC"); cmd_append(&cmd, "-Ivendor/lunasvg/include"); cmd_append(&cmd, "-Ivendor/lunasvg/source"); cmd_append(&cmd, "-Ivendor/lunasvg/plutovg/include"); cmd_append(&cmd, "-Wall", "-Wno-unused-function", "-Wno-unused-parameter"); if (debug) { cmd_append(&cmd, "-g", "-O0"); } else { cmd_append(&cmd, "-O2"); } cmd_append(&cmd, "-o", obj_path); cmd_append(&cmd, src); if (!cmd_run(&cmd)) return false; } // Archive into static library { Cmd cmd = {}; cmd_append(&cmd, "ar", "rcs", lib_path); for (size_t i = 0; i < ARRAY_LEN(plutovg_sources); i++) { const char *src = plutovg_sources[i]; const char *base = strrchr(src, '/'); base = base ? base + 1 : src; char obj_name[256]; snprintf(obj_name, sizeof(obj_name), "%s", base); char *dot = strrchr(obj_name, '.'); if (dot) { dot[1] = 'o'; dot[2] = '\0'; } cmd__append(&cmd, 1, temp_sprintf("%s/%s", obj_dir, obj_name)); } for (size_t i = 0; i < ARRAY_LEN(lunasvg_sources); i++) { const char *src = lunasvg_sources[i]; const char *base = strrchr(src, '/'); base = base ? base + 1 : src; char obj_name[256]; snprintf(obj_name, sizeof(obj_name), "%s", base); char *dot = strrchr(obj_name, '.'); if (dot) { dot[1] = 'o'; dot[2] = '\0'; } cmd__append(&cmd, 1, temp_sprintf("%s/%s", obj_dir, obj_name)); } if (!cmd_run(&cmd)) return false; } // Clean up obj dir — only the .a is needed { Cmd cmd = {}; cmd_append(&cmd, "rm", "-rf", obj_dir); cmd_run(&cmd); } build_log(LOG_INFO, "Built %s", lib_path); return true; } static bool build_freetype_lib(const char *build_dir, bool debug) { const char *obj_dir = temp_sprintf("%s/freetype_obj", build_dir); const char *lib_path = "vendor/freetype/libfreetype.a"; if (!needs_rebuild(lib_path, freetype_sources, ARRAY_LEN(freetype_sources))) { build_log(LOG_INFO, "freetype is up to date"); return true; } if (!mkdir_if_not_exists(obj_dir)) return false; for (size_t i = 0; i < ARRAY_LEN(freetype_sources); i++) { const char *src = freetype_sources[i]; const char *base = strrchr(src, '/'); base = base ? base + 1 : src; char obj_name[256]; snprintf(obj_name, sizeof(obj_name), "%s", base); char *dot = strrchr(obj_name, '.'); if (dot) { dot[1] = 'o'; dot[2] = '\0'; } const char *obj_path = temp_sprintf("%s/%s", obj_dir, obj_name); Cmd cmd = {}; cmd_append(&cmd, "clang"); cmd_append(&cmd, "-std=c11", "-c"); cmd_append(&cmd, "-DFT2_BUILD_LIBRARY"); cmd_append(&cmd, "-Ivendor/freetype/include"); cmd_append(&cmd, "-Wall", "-Wno-unused-function", "-Wno-unused-parameter"); if (debug) { cmd_append(&cmd, "-g", "-O0"); } else { cmd_append(&cmd, "-O2"); } cmd_append(&cmd, "-o", obj_path); cmd_append(&cmd, src); if (!cmd_run(&cmd)) return false; } // Archive { Cmd cmd = {}; cmd_append(&cmd, "ar", "rcs", lib_path); for (size_t i = 0; i < ARRAY_LEN(freetype_sources); i++) { const char *src = freetype_sources[i]; const char *base = strrchr(src, '/'); base = base ? base + 1 : src; char obj_name[256]; snprintf(obj_name, sizeof(obj_name), "%s", base); char *dot = strrchr(obj_name, '.'); if (dot) { dot[1] = 'o'; dot[2] = '\0'; } cmd__append(&cmd, 1, temp_sprintf("%s/%s", obj_dir, obj_name)); } if (!cmd_run(&cmd)) return false; } // Clean up obj dir { Cmd cmd = {}; cmd_append(&cmd, "rm", "-rf", obj_dir); cmd_run(&cmd); } build_log(LOG_INFO, "Built %s", lib_path); return true; } int main(int argc, char **argv) { GO_REBUILD_URSELF(argc, argv); bool debug = false; bool clean = false; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "debug") == 0) debug = true; else if (strcmp(argv[i], "clean") == 0) clean = true; else { build_log(LOG_ERROR, "unknown argument: %s", argv[i]); build_log(LOG_ERROR, "usage: %s [debug] [clean]", argv[0]); return 1; } } const char *build_dir = debug ? "build_debug" : "build_release"; if (clean) { build_log(LOG_INFO, "Cleaning build artifacts"); { Cmd cmd = {}; cmd_append(&cmd, "rm", "-rf", "build_debug"); cmd_run(&cmd); } { Cmd cmd = {}; cmd_append(&cmd, "rm", "-rf", "build_release"); cmd_run(&cmd); } remove("vendor/lunasvg/liblunasvg.a"); remove("vendor/freetype/libfreetype.a"); remove("src/renderer/font_inter.gen.h"); return 0; } // App bundle paths const char *app_dir = temp_sprintf("%s/autosample.app", build_dir); const char *contents = temp_sprintf("%s/Contents", app_dir); const char *macos_dir = temp_sprintf("%s/Contents/MacOS", app_dir); const char *res_dir = temp_sprintf("%s/Contents/Resources", app_dir); const char *binary_path = temp_sprintf("%s/Contents/MacOS/autosample", app_dir); if (!mkdir_if_not_exists(build_dir)) return 1; if (!mkdir_if_not_exists(app_dir)) return 1; if (!mkdir_if_not_exists(contents)) return 1; if (!mkdir_if_not_exists(macos_dir)) return 1; if (!mkdir_if_not_exists(res_dir)) return 1; // Build static libraries if (!build_lunasvg_lib(build_dir, debug)) return 1; if (!build_freetype_lib(build_dir, debug)) return 1; // Generate embedded font header if (!embed_font_file("assets/fonts/Inter-Regular.ttf", "src/renderer/font_inter.gen.h")) return 1; // Unity build: single clang++ invocation compiles main.cpp (which #includes everything) { Cmd cmd = {}; cmd_append(&cmd, "clang++"); cmd_append(&cmd, "-std=c++20", "-x", "objective-c++"); cmd_append(&cmd, "-fno-exceptions", "-fno-rtti"); cmd_append(&cmd, "-Wall", "-Wextra", "-Wno-missing-field-initializers"); cmd_append(&cmd, "-Wno-deprecated-declarations"); cmd_append(&cmd, "-Isrc", "-Ivendor/clay"); cmd_append(&cmd, "-Ivendor/lunasvg/include"); cmd_append(&cmd, "-Ivendor/freetype/include"); cmd_append(&cmd, "-DLUNASVG_BUILD_STATIC"); if (debug) { cmd_append(&cmd, "-g", "-O0", "-D_DEBUG"); } else { cmd_append(&cmd, "-O2", "-DNDEBUG"); } cmd_append(&cmd, "-o", binary_path); cmd_append(&cmd, "src/main.cpp"); // Reset language mode so .a is treated as a library, not source cmd_append(&cmd, "-x", "none"); cmd_append(&cmd, "vendor/lunasvg/liblunasvg.a"); cmd_append(&cmd, "vendor/freetype/libfreetype.a"); for (size_t i = 0; i < ARRAY_LEN(frameworks); i++) cmd__append(&cmd, 1, frameworks[i]); cmd_append(&cmd, "-lstdc++"); if (!cmd_run(&cmd)) return 1; } // Write Info.plist { const char *plist_path = temp_sprintf("%s/Contents/Info.plist", app_dir); const char *plist = "\n" "\n" "\n" "\n" " CFBundleExecutable\n" " autosample\n" " CFBundleIdentifier\n" " com.autosample.app\n" " CFBundleName\n" " autosample\n" " CFBundleVersion\n" " 0.1\n" " CFBundleShortVersionString\n" " 0.1\n" " CFBundlePackageType\n" " APPL\n" " NSHighResolutionCapable\n" " \n" " NSSupportsAutomaticGraphicsSwitching\n" " \n" "\n" "\n"; write_entire_file(plist_path, plist, strlen(plist)); } build_log(LOG_INFO, "Build complete: %s", app_dir); return 0; } #else //////////////////////////////// // Windows build (MSVC cl.exe) // Vulkan SDK path — detected from environment or default install location static const char *get_vulkan_sdk_path(void) { const char *env = getenv("VULKAN_SDK"); if (env && env[0]) return env; return "C:\\VulkanSDK\\1.4.341.1"; } static const char *link_libs[] = { "user32.lib", "gdi32.lib", "shell32.lib", "ole32.lib", "advapi32.lib", "dwmapi.lib", "winmm.lib", }; //////////////////////////////// // SPIR-V shader compilation — compiles .glsl to .spv, then embeds as C header static bool compile_shader(const char *glslc_path, const char *src, const char *spv_path, const char *stage) { Cmd cmd = {}; cmd_append(&cmd, glslc_path, temp_sprintf("-fshader-stage=%s", stage), "-o", spv_path, src); return cmd_run(&cmd); } static bool embed_spirv(const char *spv_path, const char *header_path, const char *array_name) { String_Builder sb = {}; if (!sb_read_file(&sb, spv_path)) return false; FILE *out = fopen(header_path, "wb"); if (!out) { build_log(LOG_ERROR, "Could not open %s for writing", header_path); sb_free(&sb); return false; } fprintf(out, "// Auto-generated from %s — do not edit\n", spv_path); fprintf(out, "#pragma once\n\n"); // SPIR-V is U32-aligned, emit as uint32_t array size_t word_count = sb.count / 4; fprintf(out, "#include \n\n"); fprintf(out, "static const uint32_t %s[] = {\n", array_name); const uint32_t *words = (const uint32_t *)sb.items; for (size_t i = 0; i < word_count; i++) { if (i % 8 == 0) fprintf(out, " "); fprintf(out, "0x%08x,", words[i]); if (i % 8 == 7 || i == word_count - 1) fprintf(out, "\n"); } fprintf(out, "};\n"); fclose(out); build_log(LOG_INFO, "Generated %s (%zu bytes)", header_path, sb.count); sb_free(&sb); return true; } static bool compile_and_embed_shaders(const char *build_dir) { const char *vk_sdk = get_vulkan_sdk_path(); const char *glslc = temp_sprintf("%s\\Bin\\glslc.exe", vk_sdk); const char *vert_src = "src/renderer/ui.v.glsl"; const char *frag_src = "src/renderer/ui.f.glsl"; const char *vert_spv = temp_sprintf("%s/ui_vert.spv", build_dir); const char *frag_spv = temp_sprintf("%s/ui_frag.spv", build_dir); const char *vert_hdr = "src/renderer/ui_vert.spv.h"; const char *frag_hdr = "src/renderer/ui_frag.spv.h"; if (needs_rebuild1(vert_hdr, vert_src)) { build_log(LOG_INFO, "Compiling vertex shader"); if (!compile_shader(glslc, vert_src, vert_spv, "vertex")) return false; if (!embed_spirv(vert_spv, vert_hdr, "ui_vert_spv")) return false; } else { build_log(LOG_INFO, "Vertex shader is up to date"); } if (needs_rebuild1(frag_hdr, frag_src)) { build_log(LOG_INFO, "Compiling fragment shader"); if (!compile_shader(glslc, frag_src, frag_spv, "fragment")) return false; if (!embed_spirv(frag_spv, frag_hdr, "ui_frag_spv")) return false; } else { build_log(LOG_INFO, "Fragment shader is up to date"); } return true; } static bool build_lunasvg_lib(const char *build_dir, bool debug) { const char *obj_dir = temp_sprintf("%s\\lunasvg_obj", build_dir); const char *lib_path = debug ? "vendor\\lunasvg\\lunasvg_d.lib" : "vendor\\lunasvg\\lunasvg.lib"; // Check if rebuild is needed { const char *all_sources[ARRAY_LEN(plutovg_sources) + ARRAY_LEN(lunasvg_sources)]; size_t n = 0; for (size_t i = 0; i < ARRAY_LEN(plutovg_sources); i++) all_sources[n++] = plutovg_sources[i]; for (size_t i = 0; i < ARRAY_LEN(lunasvg_sources); i++) all_sources[n++] = lunasvg_sources[i]; if (!needs_rebuild(lib_path, all_sources, n)) { build_log(LOG_INFO, "lunasvg is up to date"); return true; } } if (!mkdir_if_not_exists(obj_dir)) return false; // Compile plutovg C sources for (size_t i = 0; i < ARRAY_LEN(plutovg_sources); i++) { const char *src = plutovg_sources[i]; Cmd cmd = {}; cmd_append(&cmd, "cl.exe", "/nologo", "/c"); cmd_append(&cmd, "/std:c11"); cmd_append(&cmd, "/DPLUTOVG_BUILD", "/DPLUTOVG_BUILD_STATIC"); cmd_append(&cmd, "/Ivendor/lunasvg/plutovg/include"); cmd_append(&cmd, "/Ivendor/lunasvg/plutovg/source"); cmd_append(&cmd, "/W3"); if (debug) { cmd_append(&cmd, "/MTd", "/Zi", "/Od"); } else { cmd_append(&cmd, "/MT", "/O2"); } cmd_append(&cmd, temp_sprintf("/Fo:%s/", obj_dir)); cmd_append(&cmd, src); if (!cmd_run(&cmd)) return false; } // Compile lunasvg C++ sources for (size_t i = 0; i < ARRAY_LEN(lunasvg_sources); i++) { const char *src = lunasvg_sources[i]; Cmd cmd = {}; cmd_append(&cmd, "cl.exe", "/nologo", "/c"); cmd_append(&cmd, "/std:c++17", "/EHsc"); cmd_append(&cmd, "/DLUNASVG_BUILD", "/DLUNASVG_BUILD_STATIC", "/DPLUTOVG_BUILD_STATIC"); cmd_append(&cmd, "/Ivendor/lunasvg/include"); cmd_append(&cmd, "/Ivendor/lunasvg/source"); cmd_append(&cmd, "/Ivendor/lunasvg/plutovg/include"); cmd_append(&cmd, "/W3"); if (debug) { cmd_append(&cmd, "/MTd", "/Zi", "/Od"); } else { cmd_append(&cmd, "/MT", "/O2"); } cmd_append(&cmd, temp_sprintf("/Fo:%s/", obj_dir)); cmd_append(&cmd, src); if (!cmd_run(&cmd)) return false; } // Archive into static library { Cmd cmd = {}; cmd_append(&cmd, "lib.exe", "/nologo", temp_sprintf("/OUT:%s", lib_path)); cmd_append(&cmd, temp_sprintf("%s/*.obj", obj_dir)); if (!cmd_run(&cmd)) return false; } // Clean up obj dir — only the .lib is needed { Cmd cmd = {}; cmd_append(&cmd, "cmd.exe", "/c", temp_sprintf("if exist %s rmdir /s /q %s", obj_dir, obj_dir)); cmd_run(&cmd); } build_log(LOG_INFO, "Built %s", lib_path); return true; } static bool build_freetype_lib(const char *build_dir, bool debug) { const char *obj_dir = temp_sprintf("%s\\freetype_obj", build_dir); const char *lib_path = debug ? "vendor\\freetype\\freetype_d.lib" : "vendor\\freetype\\freetype.lib"; if (!needs_rebuild(lib_path, freetype_sources, ARRAY_LEN(freetype_sources))) { build_log(LOG_INFO, "freetype is up to date"); return true; } if (!mkdir_if_not_exists(obj_dir)) return false; for (size_t i = 0; i < ARRAY_LEN(freetype_sources); i++) { const char *src = freetype_sources[i]; Cmd cmd = {}; cmd_append(&cmd, "cl.exe", "/nologo", "/c"); cmd_append(&cmd, "/std:c11"); cmd_append(&cmd, "/DFT2_BUILD_LIBRARY"); cmd_append(&cmd, "/Ivendor/freetype/include"); cmd_append(&cmd, "/W3"); if (debug) { cmd_append(&cmd, "/MTd", "/Zi", "/Od"); } else { cmd_append(&cmd, "/MT", "/O2"); } cmd_append(&cmd, temp_sprintf("/Fo:%s/", obj_dir)); cmd_append(&cmd, src); if (!cmd_run(&cmd)) return false; } // Archive { Cmd cmd = {}; cmd_append(&cmd, "lib.exe", "/nologo", temp_sprintf("/OUT:%s", lib_path)); cmd_append(&cmd, temp_sprintf("%s/*.obj", obj_dir)); if (!cmd_run(&cmd)) return false; } // Clean up obj dir { Cmd cmd = {}; cmd_append(&cmd, "cmd.exe", "/c", temp_sprintf("if exist %s rmdir /s /q %s", obj_dir, obj_dir)); cmd_run(&cmd); } build_log(LOG_INFO, "Built %s", lib_path); return true; } int main(int argc, char **argv) { GO_REBUILD_URSELF(argc, argv); bool debug = false; bool clean = false; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "debug") == 0) debug = true; else if (strcmp(argv[i], "clean") == 0) clean = true; else { build_log(LOG_ERROR, "unknown argument: %s", argv[i]); build_log(LOG_ERROR, "usage: %s [debug] [clean]", argv[0]); return 1; } } const char *build_dir = debug ? "build_debug" : "build_release"; if (clean) { build_log(LOG_INFO, "Cleaning build artifacts"); { Cmd cmd = {}; cmd_append(&cmd, "cmd.exe", "/c", "if exist build_debug rmdir /s /q build_debug"); cmd_run(&cmd); } { Cmd cmd = {}; cmd_append(&cmd, "cmd.exe", "/c", "if exist build_release rmdir /s /q build_release"); cmd_run(&cmd); } remove("vendor\\lunasvg\\lunasvg.lib"); remove("vendor\\lunasvg\\lunasvg_d.lib"); remove("vendor\\freetype\\freetype.lib"); remove("vendor\\freetype\\freetype_d.lib"); remove("src\\renderer\\font_inter.gen.h"); remove("src\\renderer\\ui_vert.spv.h"); remove("src\\renderer\\ui_frag.spv.h"); return 0; } if (!mkdir_if_not_exists(build_dir)) return 1; // Build static libraries if (!build_lunasvg_lib(build_dir, debug)) return 1; if (!build_freetype_lib(build_dir, debug)) return 1; // Generate embedded font header if (!embed_font_file("assets/fonts/Inter-Regular.ttf", "src\\renderer\\font_inter.gen.h")) return 1; // Compile GLSL shaders to SPIR-V and embed as C headers if (!compile_and_embed_shaders(build_dir)) return 1; const char *vk_sdk = get_vulkan_sdk_path(); const char *vk_include = temp_sprintf("/I%s/Include", vk_sdk); const char *vk_lib = temp_sprintf("%s/Lib/vulkan-1.lib", vk_sdk); // Unity build: single cl.exe invocation compiles main.cpp (which #includes everything including the vulkan renderer) { Cmd cmd = {}; cmd_append(&cmd, "cl.exe"); cmd_append(&cmd, "/nologo", "/std:c++20", "/EHsc", "/W3"); cmd_append(&cmd, "/Isrc", "/Ivendor/clay"); cmd_append(&cmd, "/Ivendor/lunasvg/include"); cmd_append(&cmd, "/Ivendor/freetype/include"); cmd_append(&cmd, vk_include); cmd_append(&cmd, "/DLUNASVG_BUILD_STATIC"); if (debug) { cmd_append(&cmd, "/MTd", "/Zi", "/Od", "/D_DEBUG"); } else { cmd_append(&cmd, "/MT", "/Zi", "/O2", "/DNDEBUG"); } cmd_append(&cmd, temp_sprintf("/Fe:%s/autosample.exe", build_dir)); cmd_append(&cmd, temp_sprintf("/Fo:%s/", build_dir)); cmd_append(&cmd, temp_sprintf("/Fd:%s/autosample.pdb", build_dir)); cmd_append(&cmd, "src/main.cpp"); cmd_append(&cmd, "/link"); cmd_append(&cmd, "/MACHINE:X64"); cmd_append(&cmd, "/SUBSYSTEM:CONSOLE"); cmd_append(&cmd, temp_sprintf("/PDB:%s/autosample.pdb", build_dir)); cmd_append(&cmd, "/DEBUG"); cmd_append(&cmd, debug ? "vendor/lunasvg/lunasvg_d.lib" : "vendor/lunasvg/lunasvg.lib"); cmd_append(&cmd, debug ? "vendor/freetype/freetype_d.lib" : "vendor/freetype/freetype.lib"); cmd_append(&cmd, vk_lib); for (size_t i = 0; i < ARRAY_LEN(link_libs); i++) cmd__append(&cmd, 1, link_libs[i]); if (!cmd_run(&cmd)) return 1; } // Clean up obj files delete_file(temp_sprintf("%s/main.obj", build_dir)); build_log(LOG_INFO, "Build complete: %s/autosample.exe", build_dir); return 0; } #endif