This commit is contained in:
parent
df9436c51d
commit
de9865b583
8 changed files with 163 additions and 1 deletions
|
|
@ -572,6 +572,36 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
||||||
return {buildError, false, {}};
|
return {buildError, false, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ship .spv files from transitive deps' bin dirs alongside the executable
|
||||||
|
// so consumers find shaders next to the binary at runtime. Only an exe is
|
||||||
|
// a deployment unit; intermediate libs don't need to forward shaders since
|
||||||
|
// the exe walks all transitive deps. Runs outside the repack gate because
|
||||||
|
// the relink mtime check above only watches .so/.dll/.a, so a shader-only
|
||||||
|
// change in a dep wouldn't trigger repack but still needs the new .spv
|
||||||
|
// copied across.
|
||||||
|
if (config.type == ConfigurationType::Executable) {
|
||||||
|
try {
|
||||||
|
std::unordered_set<Configuration*> shaderSeen;
|
||||||
|
std::function<void(Configuration*)> copyDepShaders = [&](Configuration* dep) {
|
||||||
|
if (!shaderSeen.insert(dep).second) return;
|
||||||
|
fs::path depDir = dep->BinDir();
|
||||||
|
for (const Shader& shader : dep->shaders) {
|
||||||
|
fs::path src = depDir / shader.path.filename().replace_extension("spv");
|
||||||
|
if (!fs::exists(src)) continue;
|
||||||
|
fs::path dest = outputDir / src.filename();
|
||||||
|
if (!fs::exists(dest) || fs::last_write_time(src) > fs::last_write_time(dest)) {
|
||||||
|
fs::copy_file(src, dest, fs::copy_options::overwrite_existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Configuration* sub : dep->dependencies) copyDepShaders(sub);
|
||||||
|
};
|
||||||
|
for (Configuration* dep : config.dependencies) copyDepShaders(dep);
|
||||||
|
} catch (const fs::filesystem_error& e) {
|
||||||
|
for(std::thread& thread : threads) thread.join();
|
||||||
|
return {e.what(), false, {}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(repack.load()) {
|
if(repack.load()) {
|
||||||
buildResult.repack = true;
|
buildResult.repack = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,8 @@ namespace Crafter {
|
||||||
|
|
||||||
}
|
}
|
||||||
bool Shader::Check(const fs::path& outputDir) const {
|
bool Shader::Check(const fs::path& outputDir) const {
|
||||||
return fs::exists((outputDir/path.filename()).generic_string()+".spv") && fs::last_write_time(path.generic_string()+".glsl") < fs::last_write_time((outputDir/path.filename()).generic_string()+".spv");
|
fs::path spv = outputDir / path.filename().replace_extension("spv");
|
||||||
|
return fs::exists(spv) && fs::last_write_time(path) < fs::last_write_time(spv);
|
||||||
}
|
}
|
||||||
std::string Shader::Compile(const fs::path& outputDir) const {
|
std::string Shader::Compile(const fs::path& outputDir) const {
|
||||||
EShLanguage glslangType = ToEShLanguage(type);
|
EShLanguage glslangType = ToEShLanguage(type);
|
||||||
|
|
|
||||||
58
tests/ShaderDep/ShaderDep.cpp
Normal file
58
tests/ShaderDep/ShaderDep.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
Crafter® Build
|
||||||
|
Copyright (C) 2026 Catcrafts®
|
||||||
|
Catcrafts.net
|
||||||
|
|
||||||
|
LGPL-3.0-only.
|
||||||
|
|
||||||
|
Verifies that when an executable consumes a library that registers a shader,
|
||||||
|
the resulting .spv ships in the executable's bin dir alongside the binary —
|
||||||
|
not just in the lib's own bin dir, where the runtime exe wouldn't find it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import std;
|
||||||
|
import Crafter.Build;
|
||||||
|
#include "../_shared/TestUtil.h"
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
using namespace TestUtil;
|
||||||
|
using namespace Crafter;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
|
fs::path src = fs::current_path() / "tests" / "ShaderDep" / "inner";
|
||||||
|
Configuration cfg = LoadFixture("ShaderDep", src);
|
||||||
|
|
||||||
|
auto br = BuildOnce(cfg);
|
||||||
|
if (!br.result.empty()) {
|
||||||
|
std::println(std::cerr, "build failed:\n{}", br.result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path libSpv = cfg.dependencies[0]->BinDir() / "triangle.spv";
|
||||||
|
fs::path appSpv = cfg.BinDir() / "triangle.spv";
|
||||||
|
|
||||||
|
if (!fs::exists(libSpv)) {
|
||||||
|
std::println(std::cerr, "lib .spv missing at {}", libSpv.string());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!fs::exists(appSpv)) {
|
||||||
|
std::println(std::cerr, "exe .spv missing at {}", appSpv.string());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream f(appSpv, std::ios::binary);
|
||||||
|
unsigned char magic[4] = {};
|
||||||
|
f.read(reinterpret_cast<char*>(magic), 4);
|
||||||
|
if (magic[0] != 0x03 || magic[1] != 0x02 || magic[2] != 0x23 || magic[3] != 0x07) {
|
||||||
|
std::println(std::cerr,
|
||||||
|
"SPIR-V magic mismatch in exe-side copy: got {:#04x} {:#04x} {:#04x} {:#04x}",
|
||||||
|
magic[0], magic[1], magic[2], magic[3]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::println(std::cerr, "test exception: {}", e.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
tests/ShaderDep/inner/lib/ShaderLib.cppm
Normal file
3
tests/ShaderDep/inner/lib/ShaderLib.cppm
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export module ShaderLib;
|
||||||
|
|
||||||
|
export int Identify() { return 0; }
|
||||||
13
tests/ShaderDep/inner/lib/triangle.glsl
Normal file
13
tests/ShaderDep/inner/lib/triangle.glsl
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) out vec3 color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 positions[3] = vec2[](
|
||||||
|
vec2( 0.0, -0.5),
|
||||||
|
vec2( 0.5, 0.5),
|
||||||
|
vec2(-0.5, 0.5)
|
||||||
|
);
|
||||||
|
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||||
|
color = vec3(1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
4
tests/ShaderDep/inner/main.cpp
Normal file
4
tests/ShaderDep/inner/main.cpp
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import std;
|
||||||
|
import ShaderLib;
|
||||||
|
|
||||||
|
int main() { return Identify(); }
|
||||||
33
tests/ShaderDep/inner/project.cpp
Normal file
33
tests/ShaderDep/inner/project.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import std;
|
||||||
|
import Crafter.Build;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
using namespace Crafter;
|
||||||
|
|
||||||
|
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view>) {
|
||||||
|
static auto ShaderLib = std::make_unique<Configuration>();
|
||||||
|
ShaderLib->path = "./lib/";
|
||||||
|
ShaderLib->name = "ShaderLib";
|
||||||
|
ShaderLib->outputName = "ShaderLib";
|
||||||
|
ShaderLib->target = "x86_64-pc-linux-gnu";
|
||||||
|
ShaderLib->type = ConfigurationType::LibraryStatic;
|
||||||
|
{
|
||||||
|
std::array<fs::path, 1> ifaces = { "ShaderLib" };
|
||||||
|
std::array<fs::path, 0> impls = {};
|
||||||
|
ShaderLib->GetInterfacesAndImplementations(ifaces, impls);
|
||||||
|
}
|
||||||
|
ShaderLib->shaders.emplace_back("./lib/triangle.glsl", "main", ShaderType::Vertex);
|
||||||
|
|
||||||
|
Configuration app;
|
||||||
|
app.path = "./";
|
||||||
|
app.name = "shader-dep-app";
|
||||||
|
app.outputName = "shader-dep-app";
|
||||||
|
app.target = "x86_64-pc-linux-gnu";
|
||||||
|
app.type = ConfigurationType::Executable;
|
||||||
|
app.dependencies = { ShaderLib.get() };
|
||||||
|
{
|
||||||
|
std::array<fs::path, 0> ifaces = {};
|
||||||
|
std::array<fs::path, 1> impls = { "main" };
|
||||||
|
app.GetInterfacesAndImplementations(ifaces, impls);
|
||||||
|
}
|
||||||
|
return app;
|
||||||
|
}
|
||||||
20
tests/ShaderDep/project.cpp
Normal file
20
tests/ShaderDep/project.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import std;
|
||||||
|
import Crafter.Build;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
using namespace Crafter;
|
||||||
|
|
||||||
|
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view>) {
|
||||||
|
Configuration cfg;
|
||||||
|
cfg.path = "tests/ShaderDep/";
|
||||||
|
cfg.name = "ShaderDep";
|
||||||
|
cfg.outputName = "ShaderDep";
|
||||||
|
cfg.target = "x86_64-pc-linux-gnu";
|
||||||
|
cfg.type = ConfigurationType::Executable;
|
||||||
|
cfg.dependencies = { ParentLib("crafter.build-lib") };
|
||||||
|
cfg.linkFlags.push_back("-Wl,--export-dynamic");
|
||||||
|
cfg.linkFlags.push_back("-ldl");
|
||||||
|
std::array<fs::path, 0> ifaces = {};
|
||||||
|
std::array<fs::path, 1> impls = { "ShaderDep" };
|
||||||
|
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue