/* Crafter® Build Copyright (C) 2026 Catcrafts® Catcrafts.net LGPL-3.0-only. Verifies that when an executable consumes a library that registers shaders or asset files, those runtime artifacts ship 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 them. Covers a .spv (cfg.shaders), a flat asset (cfg.files file), and a directory tree (cfg.files dir). Also verifies cfg.buildFiles' include-path semantic: the consumer's own shader compile resolves `#include "ui-shared.glsl"` against the lib's buildFiles in-place from the dep's source tree (no copy), and the file is NOT mirrored into any build/bin dir. */ 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(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; } fs::path appAsset = cfg.BinDir() / "asset.txt"; if (!fs::exists(appAsset)) { std::println(std::cerr, "exe-side asset.txt missing at {}", appAsset.string()); return 1; } if (ReadFile(appAsset) != "crafter-build asset\n") { std::println(std::cerr, "asset.txt content mismatch at {}", appAsset.string()); return 1; } fs::path appNested = cfg.BinDir() / "data" / "nested.txt"; if (!fs::exists(appNested)) { std::println(std::cerr, "exe-side data/nested.txt missing at {}", appNested.string()); return 1; } if (ReadFile(appNested) != "nested asset\n") { std::println(std::cerr, "data/nested.txt content mismatch at {}", appNested.string()); return 1; } // The consumer shader includes a header from the lib's buildFiles. // Successful build (above) means glslang resolved the include // in-place from the dep's source tree — verify the resulting .spv // and the SPIR-V magic, same as for the lib's shader. fs::path consumerSpv = cfg.BinDir() / "consumer.spv"; if (!fs::exists(consumerSpv)) { std::println(std::cerr, "consumer .spv missing at {}", consumerSpv.string()); return 1; } std::ifstream cf(consumerSpv, std::ios::binary); unsigned char cmagic[4] = {}; cf.read(reinterpret_cast(cmagic), 4); if (cmagic[0] != 0x03 || cmagic[1] != 0x02 || cmagic[2] != 0x23 || cmagic[3] != 0x07) { std::println(std::cerr, "SPIR-V magic mismatch in consumer.spv: got {:#04x} {:#04x} {:#04x} {:#04x}", cmagic[0], cmagic[1], cmagic[2], cmagic[3]); return 1; } // buildFiles must NOT be copied anywhere — they're read in place. for (fs::path probe : { cfg.BinDir() / "ui-shared.glsl", cfg.BuildDir() / "ui-shared.glsl", cfg.dependencies[0]->BinDir() / "ui-shared.glsl", cfg.dependencies[0]->BuildDir() / "ui-shared.glsl", }) { if (fs::exists(probe)) { std::println(std::cerr, "ui-shared.glsl unexpectedly copied to {}", probe.string()); return 1; } } return 0; } catch (const std::exception& e) { std::println(std::cerr, "test exception: {}", e.what()); return 1; } }