This commit is contained in:
parent
659103a123
commit
03717b5f33
9 changed files with 230 additions and 14 deletions
55
implementations/Crafter.Build-Asset.cpp
Normal file
55
implementations/Crafter.Build-Asset.cpp
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import :Clang;
|
|||
import :Platform;
|
||||
import :Test;
|
||||
import :Progress;
|
||||
import :Asset;
|
||||
namespace fs = std::filesystem;
|
||||
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]() {
|
||||
Progress::Task task(std::format("Copying files for {}", config.name));
|
||||
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());
|
||||
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::function<void(Configuration*)> addFlags = [&](Configuration* dep) {
|
||||
if (!seen.insert(dep).second) return;
|
||||
for (const auto& entry : fs::recursive_directory_iterator(dep->path)) {
|
||||
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) {
|
||||
addFlags(sub);
|
||||
}
|
||||
|
|
@ -450,6 +487,7 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
addFlags(dep);
|
||||
}
|
||||
}
|
||||
command += includeFlags;
|
||||
|
||||
for(Configuration* dep : config.dependencies) {
|
||||
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) {
|
||||
if(define.value.empty()) {
|
||||
command += std::format(" -D {}", define.name);
|
||||
defineFlags += std::format(" -D {}", define.name);
|
||||
} 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) {
|
||||
command += " " + flag;
|
||||
userFlags += " " + flag;
|
||||
}
|
||||
command += userFlags;
|
||||
|
||||
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 srcPath = cFile.string() + ".c";
|
||||
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()));
|
||||
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;
|
||||
|
||||
bool expected = false;
|
||||
|
|
@ -632,6 +678,16 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
if (!fs::exists(src)) continue;
|
||||
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* dep : config.dependencies) forwardDepArtifacts(dep);
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ fs::path Crafter::GetCacheDir() {
|
|||
}
|
||||
|
||||
namespace {
|
||||
constexpr std::array<std::string_view, 9> kCrafterBuildModules = {
|
||||
constexpr std::array<std::string_view, 10> kCrafterBuildModules = {
|
||||
"Crafter.Build-Shader",
|
||||
"Crafter.Build-Platform",
|
||||
"Crafter.Build-Interface",
|
||||
|
|
@ -236,6 +236,7 @@ namespace {
|
|||
"Crafter.Build-Clang",
|
||||
"Crafter.Build-Test",
|
||||
"Crafter.Build-Progress",
|
||||
"Crafter.Build-Asset",
|
||||
"Crafter.Build",
|
||||
};
|
||||
}
|
||||
|
|
@ -737,7 +738,7 @@ std::string Crafter::GetBaseCommand(const Configuration& config) {
|
|||
}
|
||||
|
||||
namespace {
|
||||
constexpr std::array<std::string_view, 9> kCrafterBuildModules = {
|
||||
constexpr std::array<std::string_view, 10> kCrafterBuildModules = {
|
||||
"Crafter.Build-Shader",
|
||||
"Crafter.Build-Platform",
|
||||
"Crafter.Build-Interface",
|
||||
|
|
@ -746,6 +747,7 @@ namespace {
|
|||
"Crafter.Build-Clang",
|
||||
"Crafter.Build-Test",
|
||||
"Crafter.Build-Progress",
|
||||
"Crafter.Build-Asset",
|
||||
"Crafter.Build",
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue