asset compression
Some checks failed
CI / build-test-release (push) Failing after 15m11s

This commit is contained in:
Jorijn van der Graaf 2026-05-12 01:16:40 +02:00
commit 03717b5f33
9 changed files with 230 additions and 14 deletions

View file

@ -17,6 +17,7 @@ copy /Y interfaces\Crafter.Build-External.cppm share\crafter-build\
copy /Y interfaces\Crafter.Build-Clang.cppm share\crafter-build\ copy /Y interfaces\Crafter.Build-Clang.cppm share\crafter-build\
copy /Y interfaces\Crafter.Build-Test.cppm share\crafter-build\ copy /Y interfaces\Crafter.Build-Test.cppm share\crafter-build\
copy /Y interfaces\Crafter.Build-Progress.cppm share\crafter-build\ copy /Y interfaces\Crafter.Build-Progress.cppm share\crafter-build\
copy /Y interfaces\Crafter.Build-Asset.cppm share\crafter-build\
copy /Y interfaces\Crafter.Build-Api.h share\crafter-build\ copy /Y interfaces\Crafter.Build-Api.h share\crafter-build\
if not exist .\build\glslang\NUL git clone https://github.com/KhronosGroup/glslang.git .\build\glslang if not exist .\build\glslang\NUL git clone https://github.com/KhronosGroup/glslang.git .\build\glslang
@ -55,6 +56,9 @@ clang++ %common_options% -fmodule-output interfaces\Crafter.Build-External.cppm
clang++ %common_options% -fmodule-output interfaces\Crafter.Build-Clang.cppm -o .\build\Crafter.Build-Clang.o clang++ %common_options% -fmodule-output interfaces\Crafter.Build-Clang.cppm -o .\build\Crafter.Build-Clang.o
clang++ %common_options% -fmodule-output interfaces\Crafter.Build-Test.cppm -o .\build\Crafter.Build-Test.o clang++ %common_options% -fmodule-output interfaces\Crafter.Build-Test.cppm -o .\build\Crafter.Build-Test.o
clang++ %common_options% -fmodule-output interfaces\Crafter.Build-Progress.cppm -o .\build\Crafter.Build-Progress.o clang++ %common_options% -fmodule-output interfaces\Crafter.Build-Progress.cppm -o .\build\Crafter.Build-Progress.o
REM Asset partition: bootstrap compiles it WITHOUT CRAFTER_BUILD_HAS_ASSET so
REM CompressAsset takes the stub branch (no Crafter.Asset PCM exists yet).
clang++ %common_options% -fmodule-output interfaces\Crafter.Build-Asset.cppm -o .\build\Crafter.Build-Asset.o
clang++ %common_options% -fmodule-output interfaces\Crafter.Build.cppm -o .\build\Crafter.Build.o clang++ %common_options% -fmodule-output interfaces\Crafter.Build.cppm -o .\build\Crafter.Build.o
clang++ %common_options% .\implementations\Crafter.Build-Shader.cpp -o .\build\Crafter.Build-Shader_impl.o clang++ %common_options% .\implementations\Crafter.Build-Shader.cpp -o .\build\Crafter.Build-Shader_impl.o
@ -65,6 +69,7 @@ clang++ %common_options% .\implementations\Crafter.Build-External.cpp -o .\build
clang++ %common_options% .\implementations\Crafter.Build-Clang.cpp -o .\build\Crafter.Build-Clang_impl.o clang++ %common_options% .\implementations\Crafter.Build-Clang.cpp -o .\build\Crafter.Build-Clang_impl.o
clang++ %common_options% .\implementations\Crafter.Build-Test.cpp -o .\build\Crafter.Build-Test_impl.o clang++ %common_options% .\implementations\Crafter.Build-Test.cpp -o .\build\Crafter.Build-Test_impl.o
clang++ %common_options% .\implementations\Crafter.Build-Progress.cpp -o .\build\Crafter.Build-Progress_impl.o clang++ %common_options% .\implementations\Crafter.Build-Progress.cpp -o .\build\Crafter.Build-Progress_impl.o
clang++ %common_options% .\implementations\Crafter.Build-Asset.cpp -o .\build\Crafter.Build-Asset_impl.o
clang++ %common_options% .\implementations\main.cpp -o .\build\main.o clang++ %common_options% .\implementations\main.cpp -o .\build\main.o
REM Step 1: link all impl .o files into crafter-build.dll, generating crafter-build.lib import lib REM Step 1: link all impl .o files into crafter-build.dll, generating crafter-build.lib import lib
@ -79,6 +84,7 @@ clang++ %useLibcLinker% -shared -std=c++26 -O3 -march=%CRAFTER_BUILD_MARCH% -mtu
.\build\Crafter.Build-Clang.o ^ .\build\Crafter.Build-Clang.o ^
.\build\Crafter.Build-Test.o ^ .\build\Crafter.Build-Test.o ^
.\build\Crafter.Build-Progress.o ^ .\build\Crafter.Build-Progress.o ^
.\build\Crafter.Build-Asset.o ^
.\build\Crafter.Build.o ^ .\build\Crafter.Build.o ^
.\build\Crafter.Build-Shader_impl.o ^ .\build\Crafter.Build-Shader_impl.o ^
.\build\Crafter.Build-Platform_impl.o ^ .\build\Crafter.Build-Platform_impl.o ^
@ -88,6 +94,7 @@ clang++ %useLibcLinker% -shared -std=c++26 -O3 -march=%CRAFTER_BUILD_MARCH% -mtu
.\build\Crafter.Build-Clang_impl.o ^ .\build\Crafter.Build-Clang_impl.o ^
.\build\Crafter.Build-Test_impl.o ^ .\build\Crafter.Build-Test_impl.o ^
.\build\Crafter.Build-Progress_impl.o ^ .\build\Crafter.Build-Progress_impl.o ^
.\build\Crafter.Build-Asset_impl.o ^
-o .\bin\crafter-build.dll -o .\bin\crafter-build.dll
REM Step 2: link the launcher exe against crafter-build.lib REM Step 2: link the launcher exe against crafter-build.lib
@ -106,6 +113,7 @@ llvm-lib.exe /OUT:.\lib\crafter-build-static.lib ^
.\build\Crafter.Build-Clang.o ^ .\build\Crafter.Build-Clang.o ^
.\build\Crafter.Build-Test.o ^ .\build\Crafter.Build-Test.o ^
.\build\Crafter.Build-Progress.o ^ .\build\Crafter.Build-Progress.o ^
.\build\Crafter.Build-Asset.o ^
.\build\Crafter.Build.o ^ .\build\Crafter.Build.o ^
.\build\Crafter.Build-Shader_impl.o ^ .\build\Crafter.Build-Shader_impl.o ^
.\build\Crafter.Build-Platform_impl.o ^ .\build\Crafter.Build-Platform_impl.o ^
@ -114,6 +122,7 @@ llvm-lib.exe /OUT:.\lib\crafter-build-static.lib ^
.\build\Crafter.Build-External_impl.o ^ .\build\Crafter.Build-External_impl.o ^
.\build\Crafter.Build-Clang_impl.o ^ .\build\Crafter.Build-Clang_impl.o ^
.\build\Crafter.Build-Test_impl.o ^ .\build\Crafter.Build-Test_impl.o ^
.\build\Crafter.Build-Progress_impl.o .\build\Crafter.Build-Progress_impl.o ^
.\build\Crafter.Build-Asset_impl.o
copy /Y "%LIBCXX_DIR%\lib\c++.dll" ".\bin\c++.dll" copy /Y "%LIBCXX_DIR%\lib\c++.dll" ".\bin\c++.dll"

View file

@ -16,6 +16,7 @@ cp interfaces/Crafter.Build-External.cppm share/crafter-build/
cp interfaces/Crafter.Build-Clang.cppm share/crafter-build/ cp interfaces/Crafter.Build-Clang.cppm share/crafter-build/
cp interfaces/Crafter.Build-Test.cppm share/crafter-build/ cp interfaces/Crafter.Build-Test.cppm share/crafter-build/
cp interfaces/Crafter.Build-Progress.cppm share/crafter-build/ cp interfaces/Crafter.Build-Progress.cppm share/crafter-build/
cp interfaces/Crafter.Build-Asset.cppm share/crafter-build/
cp interfaces/Crafter.Build-Api.h share/crafter-build/ cp interfaces/Crafter.Build-Api.h share/crafter-build/
git clone https://github.com/KhronosGroup/glslang.git ./build/glslang git clone https://github.com/KhronosGroup/glslang.git ./build/glslang
@ -52,6 +53,11 @@ clang++ $common_options -fmodule-output interfaces/Crafter.Build-External.cppm -
clang++ $common_options -fmodule-output interfaces/Crafter.Build-Clang.cppm -o ./build/Crafter.Build-Clang.o clang++ $common_options -fmodule-output interfaces/Crafter.Build-Clang.cppm -o ./build/Crafter.Build-Clang.o
clang++ $common_options -fmodule-output interfaces/Crafter.Build-Test.cppm -o ./build/Crafter.Build-Test.o clang++ $common_options -fmodule-output interfaces/Crafter.Build-Test.cppm -o ./build/Crafter.Build-Test.o
clang++ $common_options -fmodule-output interfaces/Crafter.Build-Progress.cppm -o ./build/Crafter.Build-Progress.o clang++ $common_options -fmodule-output interfaces/Crafter.Build-Progress.cppm -o ./build/Crafter.Build-Progress.o
# Asset partition: bootstrap compiles it WITHOUT CRAFTER_BUILD_HAS_ASSET so
# CompressAsset takes the stub branch and the impl unit doesn't try to
# `import Crafter.Asset` (no Crafter.Asset PCM exists at this stage — the
# self-host pass that follows wires in the real dep).
clang++ $common_options -fmodule-output interfaces/Crafter.Build-Asset.cppm -o ./build/Crafter.Build-Asset.o
clang++ $common_options -fmodule-output interfaces/Crafter.Build.cppm -o ./build/Crafter.Build.o clang++ $common_options -fmodule-output interfaces/Crafter.Build.cppm -o ./build/Crafter.Build.o
clang++ $common_options ./implementations/Crafter.Build-Shader.cpp -o ./build/Crafter.Build-Shader_impl.o clang++ $common_options ./implementations/Crafter.Build-Shader.cpp -o ./build/Crafter.Build-Shader_impl.o
@ -62,6 +68,7 @@ clang++ $common_options ./implementations/Crafter.Build-External.cpp -o ./build/
clang++ $common_options ./implementations/Crafter.Build-Clang.cpp -o ./build/Crafter.Build-Clang_impl.o clang++ $common_options ./implementations/Crafter.Build-Clang.cpp -o ./build/Crafter.Build-Clang_impl.o
clang++ $common_options ./implementations/Crafter.Build-Test.cpp -o ./build/Crafter.Build-Test_impl.o clang++ $common_options ./implementations/Crafter.Build-Test.cpp -o ./build/Crafter.Build-Test_impl.o
clang++ $common_options ./implementations/Crafter.Build-Progress.cpp -o ./build/Crafter.Build-Progress_impl.o clang++ $common_options ./implementations/Crafter.Build-Progress.cpp -o ./build/Crafter.Build-Progress_impl.o
clang++ $common_options ./implementations/Crafter.Build-Asset.cpp -o ./build/Crafter.Build-Asset_impl.o
clang++ $common_options ./implementations/main.cpp -o ./build/main.o clang++ $common_options ./implementations/main.cpp -o ./build/main.o
ar rcs ./lib/libcrafter-build.a \ ar rcs ./lib/libcrafter-build.a \
@ -73,6 +80,7 @@ ar rcs ./lib/libcrafter-build.a \
./build/Crafter.Build-Clang.o \ ./build/Crafter.Build-Clang.o \
./build/Crafter.Build-Test.o \ ./build/Crafter.Build-Test.o \
./build/Crafter.Build-Progress.o \ ./build/Crafter.Build-Progress.o \
./build/Crafter.Build-Asset.o \
./build/Crafter.Build.o \ ./build/Crafter.Build.o \
./build/Crafter.Build-Shader_impl.o \ ./build/Crafter.Build-Shader_impl.o \
./build/Crafter.Build-Platform_impl.o \ ./build/Crafter.Build-Platform_impl.o \
@ -81,7 +89,8 @@ ar rcs ./lib/libcrafter-build.a \
./build/Crafter.Build-External_impl.o \ ./build/Crafter.Build-External_impl.o \
./build/Crafter.Build-Clang_impl.o \ ./build/Crafter.Build-Clang_impl.o \
./build/Crafter.Build-Test_impl.o \ ./build/Crafter.Build-Test_impl.o \
./build/Crafter.Build-Progress_impl.o ./build/Crafter.Build-Progress_impl.o \
./build/Crafter.Build-Asset_impl.o
clang++ -std=c++26 -stdlib=libc++ -O3 -march=$MARCH -mtune=$MTUNE -fuse-ld=lld \ clang++ -std=c++26 -stdlib=libc++ -O3 -march=$MARCH -mtune=$MTUNE -fuse-ld=lld \
-Wl,--export-dynamic \ -Wl,--export-dynamic \
@ -94,6 +103,7 @@ clang++ -std=c++26 -stdlib=libc++ -O3 -march=$MARCH -mtune=$MTUNE -fuse-ld=lld \
./build/Crafter.Build-Clang.o \ ./build/Crafter.Build-Clang.o \
./build/Crafter.Build-Test.o \ ./build/Crafter.Build-Test.o \
./build/Crafter.Build-Progress.o \ ./build/Crafter.Build-Progress.o \
./build/Crafter.Build-Asset.o \
./build/Crafter.Build.o \ ./build/Crafter.Build.o \
./build/Crafter.Build-Shader_impl.o \ ./build/Crafter.Build-Shader_impl.o \
./build/Crafter.Build-Platform_impl.o \ ./build/Crafter.Build-Platform_impl.o \
@ -103,6 +113,7 @@ clang++ -std=c++26 -stdlib=libc++ -O3 -march=$MARCH -mtune=$MTUNE -fuse-ld=lld \
./build/Crafter.Build-Clang_impl.o \ ./build/Crafter.Build-Clang_impl.o \
./build/Crafter.Build-Test_impl.o \ ./build/Crafter.Build-Test_impl.o \
./build/Crafter.Build-Progress_impl.o \ ./build/Crafter.Build-Progress_impl.o \
./build/Crafter.Build-Asset_impl.o \
./build/main.o \ ./build/main.o \
-lSPIRV -lGenericCodeGen -lglslang -lOSDependent -lMachineIndependent -lglslang-default-resource-limits \ -lSPIRV -lGenericCodeGen -lglslang -lOSDependent -lMachineIndependent -lglslang-default-resource-limits \
-ldl \ -ldl \

View file

@ -0,0 +1,55 @@
/*
Crafter® Build
Copyright (C) 2026 Catcrafts®
Catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module Crafter.Build:Asset_impl;
import :Asset;
import std;
#ifdef CRAFTER_BUILD_HAS_ASSET
import Crafter.Asset;
import Crafter.Math;
#endif
namespace fs = std::filesystem;
namespace Crafter {
std::string CompressAsset(const fs::path& input, const fs::path& output) {
std::string ext = input.extension().string();
#ifdef CRAFTER_BUILD_HAS_ASSET
try {
if (ext == ".png") {
auto tex = TextureAsset<Vector<std::uint8_t, 4, 4>>::LoadPNG<std::uint8_t>(input);
tex.SaveCompressed(output);
} else if (ext == ".obj") {
auto mesh = MeshAsset<VertexNormalTangentUVPacked>::LoadOBJ(input);
mesh.SaveCompressed(output);
} else {
return std::format("{}: unsupported asset extension '{}'", input.string(), ext);
}
} catch (const std::exception& e) {
return std::format("{}: {}", input.string(), e.what());
}
return {};
#else
return std::format(
"{}: crafter-build was bootstrapped without Crafter.Asset linkage; "
"rebuild via `./bin/crafter-build` so the self-host pass picks up the "
"Crafter.Asset dep declared in project.cpp",
input.string());
#endif
}
}

View file

@ -23,6 +23,7 @@ import :Clang;
import :Platform; import :Platform;
import :Test; import :Test;
import :Progress; import :Progress;
import :Asset;
namespace fs = std::filesystem; namespace fs = std::filesystem;
using namespace Crafter; using namespace Crafter;
@ -291,6 +292,38 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
}); });
} }
// Asset compilation: each .png/.obj listed in cfg.assets is run through
// Crafter.Asset's library (linked into crafter-build directly — see
// :Asset_impl) to produce the SaveCompressed .ctex/.cmesh in outputDir.
// Skipped if the output is newer than the source. Parallel with shaders
// and module compiles; cancellation participates in the same flag.
auto assetOutput = [](const fs::path& asset) -> std::optional<fs::path> {
std::string ext = asset.extension().string();
if (ext == ".png") return fs::path(asset.filename()).replace_extension(".ctex");
if (ext == ".obj") return fs::path(asset.filename()).replace_extension(".cmesh");
return std::nullopt;
};
for (const fs::path& asset : config.assets) {
std::optional<fs::path> outName = assetOutput(asset);
if (!outName) {
buildCancelled.store(true);
buildError = std::format("{}: unsupported asset extension (expected .png or .obj)", asset.string());
break;
}
fs::path out = outputDir / *outName;
if (fs::exists(out) && fs::exists(asset) && fs::last_write_time(asset) <= fs::last_write_time(out)) continue;
threads.emplace_back([&asset, out = std::move(out), &buildError, &buildCancelled]() {
Progress::Task task(std::format("Compressing asset {}", asset.filename().string()));
if (buildCancelled.load(std::memory_order_relaxed)) return;
std::string result = CompressAsset(asset, out);
if (result.empty()) return;
bool expected = false;
if (buildCancelled.compare_exchange_strong(expected, true)) {
buildError = std::move(result);
}
});
}
threads.emplace_back([&config, &outputDir, &buildCancelled, &buildError]() { threads.emplace_back([&config, &outputDir, &buildCancelled, &buildError]() {
Progress::Task task(std::format("Copying files for {}", config.name)); Progress::Task task(std::format("Copying files for {}", config.name));
if (buildCancelled.load(std::memory_order_relaxed)) return; if (buildCancelled.load(std::memory_order_relaxed)) return;
@ -432,16 +465,20 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
depThreads.reserve(config.dependencies.size()); depThreads.reserve(config.dependencies.size());
std::atomic<bool> repack(false); std::atomic<bool> repack(false);
// -I propagation that's valid for both C and C++ compiles. Module-only
// bits (-fprebuilt-module-path) stay on `command` only.
std::string includeFlags;
{ {
std::unordered_set<Configuration*> seen; std::unordered_set<Configuration*> seen;
std::function<void(Configuration*)> addFlags = [&](Configuration* dep) { std::function<void(Configuration*)> addFlags = [&](Configuration* dep) {
if (!seen.insert(dep).second) return; if (!seen.insert(dep).second) return;
for (const auto& entry : fs::recursive_directory_iterator(dep->path)) { for (const auto& entry : fs::recursive_directory_iterator(dep->path)) {
if (entry.is_directory() && entry.path().filename() == "include") { if (entry.is_directory() && entry.path().filename() == "include") {
command += " -I" + entry.path().string(); includeFlags += " -I" + entry.path().string();
} }
} }
command += std::format(" -I{} -fprebuilt-module-path={}", dep->path.string(), dep->PcmDir().string()); includeFlags += std::format(" -I{}", dep->path.string());
command += std::format(" -fprebuilt-module-path={}", dep->PcmDir().string());
for (Configuration* sub : dep->dependencies) { for (Configuration* sub : dep->dependencies) {
addFlags(sub); addFlags(sub);
} }
@ -450,6 +487,7 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
addFlags(dep); addFlags(dep);
} }
} }
command += includeFlags;
for(Configuration* dep : config.dependencies) { for(Configuration* dep : config.dependencies) {
depThreads.emplace_back([&, dep](){ depThreads.emplace_back([&, dep](){
@ -507,17 +545,25 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
}); });
} }
// Defines belong on both C and C++ compiles so vendored C dependencies
// can see configuration-level macros consistently with module sources.
std::string defineFlags;
for(const Define& define : config.defines) { for(const Define& define : config.defines) {
if(define.value.empty()) { if(define.value.empty()) {
command += std::format(" -D {}", define.name); defineFlags += std::format(" -D {}", define.name);
} else { } else {
command += std::format(" -D {}={}", define.name, define.value); defineFlags += std::format(" -D {}={}", define.name, define.value);
} }
} }
command += defineFlags;
// Track caller-provided compileFlags separately so the .c compile can
// pick them up too (vendored C deps usually need -I from this set).
std::string userFlags;
for(const std::string& flag : config.compileFlags) { for(const std::string& flag : config.compileFlags) {
command += " " + flag; userFlags += " " + flag;
} }
command += userFlags;
std::string cmakeBuildType; std::string cmakeBuildType;
@ -534,11 +580,11 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
const std::string objPath = (buildDir / cFile.filename()).string() + "_source.o"; const std::string objPath = (buildDir / cFile.filename()).string() + "_source.o";
const std::string srcPath = cFile.string() + ".c"; const std::string srcPath = cFile.string() + ".c";
if (!fs::exists(objPath) || (fs::exists(srcPath) && fs::last_write_time(srcPath) > fs::last_write_time(objPath))) { if (!fs::exists(objPath) || (fs::exists(srcPath) && fs::last_write_time(srcPath) > fs::last_write_time(objPath))) {
threads.emplace_back([&cFile, &buildDir, &buildError, &buildCancelled, &config]() { threads.emplace_back([&cFile, &buildDir, &buildError, &buildCancelled, &config, &includeFlags, &defineFlags, &userFlags]() {
Progress::Task task(std::format("Compiling {}.c", cFile.filename().string())); Progress::Task task(std::format("Compiling {}.c", cFile.filename().string()));
if (buildCancelled.load(std::memory_order_relaxed)) return; if (buildCancelled.load(std::memory_order_relaxed)) return;
std::string result = RunCommand(std::format("clang {}.c --target={} -march={} -mtune={} -O3 -c -o {}_source.o", cFile.string(), config.target, config.march, config.mtune, (buildDir / cFile.filename()).string())); std::string result = RunCommand(std::format("clang {}.c --target={} -march={} -mtune={} -O3 -c{}{}{} -o {}_source.o", cFile.string(), config.target, config.march, config.mtune, includeFlags, defineFlags, userFlags, (buildDir / cFile.filename()).string()));
if (result.empty()) return; if (result.empty()) return;
bool expected = false; bool expected = false;
@ -632,6 +678,16 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
if (!fs::exists(src)) continue; if (!fs::exists(src)) continue;
copyTree(src, outputDir / additionalFile.filename()); copyTree(src, outputDir / additionalFile.filename());
} }
for (const fs::path& asset : dep->assets) {
std::string ext = asset.extension().string();
fs::path srcName = asset.filename();
if (ext == ".png") srcName.replace_extension(".ctex");
else if (ext == ".obj") srcName.replace_extension(".cmesh");
else continue;
fs::path src = depBinDir / srcName;
if (!fs::exists(src)) continue;
copyTree(src, outputDir / srcName);
}
for (Configuration* sub : dep->dependencies) forwardDepArtifacts(sub); for (Configuration* sub : dep->dependencies) forwardDepArtifacts(sub);
}; };
for (Configuration* dep : config.dependencies) forwardDepArtifacts(dep); for (Configuration* dep : config.dependencies) forwardDepArtifacts(dep);

View file

@ -227,7 +227,7 @@ fs::path Crafter::GetCacheDir() {
} }
namespace { namespace {
constexpr std::array<std::string_view, 9> kCrafterBuildModules = { constexpr std::array<std::string_view, 10> kCrafterBuildModules = {
"Crafter.Build-Shader", "Crafter.Build-Shader",
"Crafter.Build-Platform", "Crafter.Build-Platform",
"Crafter.Build-Interface", "Crafter.Build-Interface",
@ -236,6 +236,7 @@ namespace {
"Crafter.Build-Clang", "Crafter.Build-Clang",
"Crafter.Build-Test", "Crafter.Build-Test",
"Crafter.Build-Progress", "Crafter.Build-Progress",
"Crafter.Build-Asset",
"Crafter.Build", "Crafter.Build",
}; };
} }
@ -737,7 +738,7 @@ std::string Crafter::GetBaseCommand(const Configuration& config) {
} }
namespace { namespace {
constexpr std::array<std::string_view, 9> kCrafterBuildModules = { constexpr std::array<std::string_view, 10> kCrafterBuildModules = {
"Crafter.Build-Shader", "Crafter.Build-Shader",
"Crafter.Build-Platform", "Crafter.Build-Platform",
"Crafter.Build-Interface", "Crafter.Build-Interface",
@ -746,6 +747,7 @@ namespace {
"Crafter.Build-Clang", "Crafter.Build-Clang",
"Crafter.Build-Test", "Crafter.Build-Test",
"Crafter.Build-Progress", "Crafter.Build-Progress",
"Crafter.Build-Asset",
"Crafter.Build", "Crafter.Build",
}; };

View file

@ -0,0 +1,39 @@
/*
Crafter® Build
Copyright (C) 2026 Catcrafts®
Catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#include "Crafter.Build-Api.h"
export module Crafter.Build:Asset;
import std;
namespace fs = std::filesystem;
namespace Crafter {
// Compress a build-time asset (.png/.obj) into its on-disk
// SaveCompressed counterpart (.ctex/.cmesh) using Crafter.Asset's
// library API in-process. Returns "" on success, an error message
// otherwise (matches Shader::Compile's contract).
//
// Real implementation lives in the impl unit and only compiles when
// CRAFTER_BUILD_HAS_ASSET is defined (i.e. when the build links
// Crafter.Asset). The bootstrap build.sh produces a stub variant
// that errors out at runtime if cfg.assets is non-empty — full
// support requires a self-host rebuild via crafter-build itself,
// which resolves the Crafter.Asset dep through project.cpp.
export CRAFTER_API std::string CompressAsset(const fs::path& input, const fs::path& output);
}

View file

@ -125,6 +125,15 @@ export namespace Crafter {
std::vector<fs::path> buildFiles; std::vector<fs::path> buildFiles;
std::vector<Define> defines; std::vector<Define> defines;
std::vector<Shader> shaders; std::vector<Shader> shaders;
// Source assets (.png, .obj) compressed via Crafter.Asset's
// SaveCompressed → .ctex/.cmesh in this configuration's bin dir.
// Requires Crafter.Asset in cfg.dependencies (transitively): the
// build engine locates it by name and uses its library API to do
// the conversion (an auxiliary host-target executable, generated
// once per build host, links Crafter.Asset and is invoked per
// asset). Forwarded to a consuming executable's bin dir alongside
// .spv shaders and cfg.files entries.
std::vector<fs::path> assets;
std::vector<ExternalDependency> externalDependencies; std::vector<ExternalDependency> externalDependencies;
std::vector<std::string> compileFlags; std::vector<std::string> compileFlags;
std::vector<std::string> linkFlags; std::vector<std::string> linkFlags;

View file

@ -25,3 +25,4 @@ export import :Shader;
export import :External; export import :External;
export import :Test; export import :Test;
export import :Progress; export import :Progress;
export import :Asset;

View file

@ -4,6 +4,30 @@ namespace fs = std::filesystem;
using namespace Crafter; using namespace Crafter;
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) { extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
std::vector<std::string> depArgs(args.begin(), args.end());
// Local-mode resolution for the Crafter.Math/Crafter.Asset deps that the
// self-host pass links in (so `cfg.assets` calls CompressAsset → Crafter.Asset
// in-process). `--local` points at sibling working trees instead of git;
// useful during cross-repo development so edits in ../Crafter.Asset are
// picked up without commit-and-pull.
bool useLocal = false;
for (std::string_view a : args) {
if (a == "--local") { useLocal = true; break; }
}
auto resolveDep = [&](std::string_view name, std::string_view gitUrl) -> Configuration* {
if (useLocal) {
return LocalProject({
.projectFile = fs::path("../") / name / "project.cpp",
.args = depArgs,
});
}
return GitProject({
.source = { .url = std::string(gitUrl) },
.args = depArgs,
});
};
static auto crafterBuildLib = std::make_unique<Configuration>(); static auto crafterBuildLib = std::make_unique<Configuration>();
crafterBuildLib->path = "./"; crafterBuildLib->path = "./";
crafterBuildLib->name = "crafter.build-lib"; crafterBuildLib->name = "crafter.build-lib";
@ -15,8 +39,16 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
crafterBuildLib->type = (crafterBuildLib->target == "x86_64-w64-mingw32" || crafterBuildLib->target == "x86_64-pc-windows-msvc") crafterBuildLib->type = (crafterBuildLib->target == "x86_64-w64-mingw32" || crafterBuildLib->target == "x86_64-pc-windows-msvc")
? ConfigurationType::LibraryDynamic ? ConfigurationType::LibraryDynamic
: ConfigurationType::LibraryStatic; : ConfigurationType::LibraryStatic;
// Self-host pass links Crafter.Asset (and its Crafter.Math dep) into the
// crafter-build binary so cfg.assets can call CompressAsset in-process.
// The bootstrap (build.sh) defines no such dep — its impl unit takes the
// stub branch and cfg.assets errors at runtime until a self-host rebuild.
Configuration* math = resolveDep("Crafter.Math", "https://forgejo.catcrafts.net/Catcrafts/Crafter.Math.git");
Configuration* asset = resolveDep("Crafter.Asset", "https://forgejo.catcrafts.net/Catcrafts/Crafter.Asset.git");
crafterBuildLib->dependencies = { math, asset };
crafterBuildLib->defines.push_back({"CRAFTER_BUILD_HAS_ASSET", ""});
{ {
std::array<fs::path, 9> interfaces = { std::array<fs::path, 10> interfaces = {
"interfaces/Crafter.Build", "interfaces/Crafter.Build",
"interfaces/Crafter.Build-Shader", "interfaces/Crafter.Build-Shader",
"interfaces/Crafter.Build-Platform", "interfaces/Crafter.Build-Platform",
@ -26,8 +58,9 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
"interfaces/Crafter.Build-Clang", "interfaces/Crafter.Build-Clang",
"interfaces/Crafter.Build-Test", "interfaces/Crafter.Build-Test",
"interfaces/Crafter.Build-Progress", "interfaces/Crafter.Build-Progress",
"interfaces/Crafter.Build-Asset",
}; };
std::array<fs::path, 8> implementations = { std::array<fs::path, 9> implementations = {
"implementations/Crafter.Build-Shader", "implementations/Crafter.Build-Shader",
"implementations/Crafter.Build-Platform", "implementations/Crafter.Build-Platform",
"implementations/Crafter.Build-Interface", "implementations/Crafter.Build-Interface",
@ -36,6 +69,7 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
"implementations/Crafter.Build-Clang", "implementations/Crafter.Build-Clang",
"implementations/Crafter.Build-Test", "implementations/Crafter.Build-Test",
"implementations/Crafter.Build-Progress", "implementations/Crafter.Build-Progress",
"implementations/Crafter.Build-Asset",
}; };
crafterBuildLib->GetInterfacesAndImplementations(interfaces, implementations); crafterBuildLib->GetInterfacesAndImplementations(interfaces, implementations);
} }