This commit is contained in:
parent
999370880f
commit
725910eb9c
64 changed files with 0 additions and 1293 deletions
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 Catcrafts®
|
||||
Catcrafts.net
|
||||
|
||||
LGPL-3.0-only.
|
||||
*/
|
||||
|
||||
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" / "BuildError" / "inner";
|
||||
Configuration cfg = LoadFixture("BuildError", src);
|
||||
fs::path work = fs::current_path();
|
||||
|
||||
auto br = BuildOnce(cfg);
|
||||
if (br.result.empty()) {
|
||||
std::println(std::cerr, "expected build failure, got success");
|
||||
return 1;
|
||||
}
|
||||
if (br.result.find("undefined_symbol_xyzzy_oqv") == std::string::npos) {
|
||||
std::println(std::cerr, "diagnostic missing unresolved-name reference:\n{}", br.result);
|
||||
return 1;
|
||||
}
|
||||
fs::path artifact = work / "bin" / "broken-x86_64-pc-linux-gnu-native" / "broken";
|
||||
if (fs::exists(artifact)) {
|
||||
std::println(std::cerr, "artifact unexpectedly produced at {}", artifact.string());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::println(std::cerr, "test exception: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
int main() {
|
||||
return undefined_symbol_xyzzy_oqv;
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
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 = "./";
|
||||
cfg.name = "broken";
|
||||
cfg.outputName = "broken";
|
||||
cfg.target = "x86_64-pc-linux-gnu";
|
||||
cfg.type = ConfigurationType::Executable;
|
||||
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
|
||||
cfg.linkFlags.push_back("-Wl,--export-dynamic");
|
||||
cfg.linkFlags.push_back("-ldl");
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
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/BuildError/";
|
||||
cfg.name = "BuildError";
|
||||
cfg.outputName = "BuildError";
|
||||
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 = { "BuildError" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import std;
|
||||
|
||||
int main() {
|
||||
std::println("hi from {}-bit aarch64", sizeof(void*) * 8);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# End-to-end cross-arch build:
|
||||
# crafter-build cross-compiles main.cpp for aarch64-linux-gnu using the
|
||||
# Arch Linux ARM sysroot, and the runner derivation (TestRunner::ForTarget)
|
||||
# wraps the produced ELF in `qemu-aarch64`. cfg.sysroot is forwarded to
|
||||
# QEMU_LD_PREFIX automatically so the target's dynamic linker is reachable.
|
||||
target = "aarch64-linux-gnu"
|
||||
march = "armv8-a"
|
||||
mtune = "generic"
|
||||
sysroot = "/opt/aarch64-rootfs"
|
||||
requires = [
|
||||
"tool:qemu-aarch64",
|
||||
"file:/opt/aarch64-rootfs/usr/share/libc++/v1/std.cppm",
|
||||
]
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
export module Foo;
|
||||
|
||||
export int Compute() {
|
||||
return 7 * 6;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import std;
|
||||
import Foo;
|
||||
|
||||
int main() {
|
||||
if (Compute() != 42) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import std;
|
||||
import Crafter.Build;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Crafter;
|
||||
|
||||
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view>) {
|
||||
// Host-only: verifies cross-project module imports work, doesn't exercise
|
||||
// any cross-compile path. See tests/Diamond for the same rationale.
|
||||
std::string target = HostTarget();
|
||||
|
||||
static auto fooLib = std::make_unique<Configuration>();
|
||||
fooLib->path = "tests/CrossProjectModule/lib/";
|
||||
fooLib->name = std::format("Foo-{}", target);
|
||||
fooLib->outputName = "Foo";
|
||||
fooLib->target = target;
|
||||
fooLib->type = ConfigurationType::LibraryStatic;
|
||||
{
|
||||
std::array<fs::path, 1> ifaces = { "Foo" };
|
||||
std::array<fs::path, 0> impls = {};
|
||||
fooLib->GetInterfacesAndImplementations(ifaces, impls);
|
||||
}
|
||||
|
||||
Configuration cfg;
|
||||
cfg.path = "tests/CrossProjectModule/";
|
||||
cfg.name = "CrossProjectModule";
|
||||
cfg.outputName = "cross-app";
|
||||
cfg.target = target;
|
||||
cfg.type = ConfigurationType::Executable;
|
||||
cfg.dependencies = { fooLib.get() };
|
||||
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 Catcrafts®
|
||||
Catcrafts.net
|
||||
|
||||
LGPL-3.0-only.
|
||||
|
||||
End-to-end CUDA build through the V2 pipeline:
|
||||
the inner project.cpp registers a .cu translation unit via cfg.cuda, and
|
||||
crafter.build-lib drives nvcc to produce a host object that links into
|
||||
the final executable. The test skips when the CUDA SDK is missing
|
||||
(CI hosts have neither nvcc nor libcudart) and otherwise runs the built
|
||||
binary to confirm the .cu's host-callable entry returns through the link.
|
||||
|
||||
No GPU is required: the kernel is staged but never launched, so cudart's
|
||||
fat-binary registration runs at startup but device init does not.
|
||||
*/
|
||||
|
||||
import std;
|
||||
import Crafter.Build;
|
||||
#include "../_shared/TestUtil.h"
|
||||
namespace fs = std::filesystem;
|
||||
using namespace TestUtil;
|
||||
using namespace Crafter;
|
||||
|
||||
namespace {
|
||||
bool ToolPresent(std::string_view name) {
|
||||
return std::system(std::format("which {} > /dev/null 2>&1", name).c_str()) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
if (!ToolPresent("nvcc")) {
|
||||
Skip("nvcc not on PATH — install the CUDA SDK to enable this test");
|
||||
}
|
||||
|
||||
// Resolve the CUDA root from nvcc's own location so the inner project
|
||||
// can point the linker at libcudart.so without a hardcoded path.
|
||||
// Standard SDK layout puts nvcc at <root>/bin/nvcc and cudart at
|
||||
// <root>/lib64; if either assumption is broken we skip rather than
|
||||
// fail, since that means the install is non-standard.
|
||||
fs::path cwd = fs::current_path();
|
||||
auto probe = RunInDir(cwd, "command -v nvcc");
|
||||
if (probe.exitCode != 0 || probe.output.empty()) {
|
||||
Skip("nvcc not on PATH");
|
||||
}
|
||||
std::string nvccPath = probe.output;
|
||||
while (!nvccPath.empty() && (nvccPath.back() == '\n' || nvccPath.back() == '\r')) {
|
||||
nvccPath.pop_back();
|
||||
}
|
||||
fs::path real = fs::canonical(nvccPath);
|
||||
fs::path cudaLibDir = real.parent_path().parent_path() / "lib64";
|
||||
if (!fs::exists(cudaLibDir / "libcudart.so")) {
|
||||
Skip(std::format("libcudart.so not found at {} — incomplete CUDA SDK install",
|
||||
cudaLibDir.string()));
|
||||
}
|
||||
::setenv("CRAFTER_CUDA_LIBDIR", cudaLibDir.string().c_str(), 1);
|
||||
|
||||
fs::path src = cwd / "tests" / "Cuda" / "inner";
|
||||
Configuration cfg = LoadFixture("Cuda", src);
|
||||
fs::path work = fs::current_path();
|
||||
|
||||
auto br = BuildOnce(cfg);
|
||||
if (!br.result.empty()) {
|
||||
std::println(std::cerr, "build failed:\n{}", br.result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fs::path artifact = cfg.BinDir() / "cuda-test";
|
||||
if (!fs::exists(artifact)) {
|
||||
std::println(std::cerr, "expected artifact missing at {}", artifact.string());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// libcudart.so is on the system loader path on most CUDA installs
|
||||
// (Arch puts /opt/cuda/lib64 in /etc/ld.so.conf.d), but other distros
|
||||
// don't, so set LD_LIBRARY_PATH explicitly for the run.
|
||||
auto run = RunInDir(work, std::format(
|
||||
"LD_LIBRARY_PATH='{}' '{}'", cudaLibDir.string(), artifact.string()));
|
||||
if (run.exitCode != 0) {
|
||||
std::println(std::cerr, "artifact exited nonzero (rc={}):\n{}", run.exitCode, run.output);
|
||||
return 1;
|
||||
}
|
||||
if (run.output != "answer=42\n") {
|
||||
std::println(std::cerr, "output mismatch:\n expected: \"answer=42\\n\"\n got: {:?}", run.output);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::println(std::cerr, "test exception: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// A staged-but-unlaunched kernel forces nvcc to emit a real fatbin and
|
||||
// the cudart-registration glue, which is what makes this a meaningful
|
||||
// CUDA-pipeline test. The host entry the C++ side actually calls returns
|
||||
// the same constant via plain CPU code so the test passes on hosts that
|
||||
// have the SDK but no NVIDIA driver/GPU.
|
||||
__device__ int compute_on_device() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
__global__ void kernel(int* out) {
|
||||
*out = compute_on_device();
|
||||
}
|
||||
|
||||
extern "C" int kernel_compute() {
|
||||
return 42;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import std;
|
||||
|
||||
extern "C" int kernel_compute();
|
||||
|
||||
int main() {
|
||||
std::println("answer={}", kernel_compute());
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
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 = "./";
|
||||
cfg.name = "cuda-test";
|
||||
cfg.outputName = "cuda-test";
|
||||
cfg.target = "x86_64-pc-linux-gnu";
|
||||
cfg.type = ConfigurationType::Executable;
|
||||
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
|
||||
cfg.cuda.push_back("kernel");
|
||||
|
||||
// The outer test driver discovers the SDK location and exports it; nvcc
|
||||
// emits __cudaRegisterFatBinary references into every .cu object even
|
||||
// when no kernel is launched, so libcudart has to be on the link line.
|
||||
if (const char* libDir = std::getenv("CRAFTER_CUDA_LIBDIR")) {
|
||||
cfg.linkFlags.push_back(std::format("-L{}", libDir));
|
||||
cfg.linkFlags.push_back("-lcudart");
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
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/Cuda/";
|
||||
cfg.name = "Cuda";
|
||||
cfg.outputName = "Cuda";
|
||||
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 = { "Cuda" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import std;
|
||||
|
||||
static_assert(CRAFTER_TEST_FOO == 42, "CRAFTER_TEST_FOO should be 42");
|
||||
|
||||
int main() {
|
||||
if (CRAFTER_TEST_FOO != 42) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
# Smoke test for cfg.defines propagation. main.cpp consumes CRAFTER_TEST_FOO
|
||||
# both at compile time (static_assert) and at runtime; if the define doesn't
|
||||
# reach the compile, the test won't even build.
|
||||
[defines]
|
||||
CRAFTER_TEST_FOO = "42"
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export module B;
|
||||
import X;
|
||||
|
||||
export int BValue() {
|
||||
return XValue() * 2;
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export module C;
|
||||
import X;
|
||||
|
||||
export int CValue() {
|
||||
return XValue() * 3;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
export module X;
|
||||
|
||||
export int XValue() {
|
||||
return 7;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import std;
|
||||
import B;
|
||||
import C;
|
||||
|
||||
int main() {
|
||||
if (BValue() != 14) return 1; // X(7) * 2
|
||||
if (CValue() != 21) return 1; // X(7) * 3
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
import std;
|
||||
import Crafter.Build;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Crafter;
|
||||
|
||||
namespace {
|
||||
std::unique_ptr<Configuration> MakeLib(std::string_view dir, std::string_view modName,
|
||||
std::string_view target,
|
||||
std::span<Configuration*> deps) {
|
||||
auto lib = std::make_unique<Configuration>();
|
||||
lib->path = std::format("tests/Diamond/{}/", dir);
|
||||
lib->name = std::format("{}-Diamond-{}", modName, target);
|
||||
lib->outputName = std::string(modName);
|
||||
lib->target = std::string(target);
|
||||
lib->type = ConfigurationType::LibraryStatic;
|
||||
lib->dependencies.assign(deps.begin(), deps.end());
|
||||
std::array<fs::path, 1> ifaces = { fs::path(modName) };
|
||||
std::array<fs::path, 0> impls = {};
|
||||
lib->GetInterfacesAndImplementations(ifaces, impls);
|
||||
return lib;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view>) {
|
||||
// Diamond exercises the build engine's dependency wiring at the host
|
||||
// target. There's no cross-compile plumbing (no sysroot, no per-target
|
||||
// toolchain), so we don't honor --target=. A real cross-arch test would
|
||||
// live in its own tests/<Name>/test.toml.
|
||||
std::string target = HostTarget();
|
||||
|
||||
static std::unique_ptr<Configuration> X, B, C;
|
||||
X = MakeLib("X", "X", target, {});
|
||||
Configuration* xDeps[] = { X.get() };
|
||||
B = MakeLib("B", "B", target, xDeps);
|
||||
C = MakeLib("C", "C", target, xDeps);
|
||||
Configuration* mainDeps[] = { B.get(), C.get() };
|
||||
|
||||
Configuration cfg;
|
||||
cfg.path = "tests/Diamond/";
|
||||
cfg.name = "Diamond";
|
||||
cfg.outputName = "diamond-app";
|
||||
cfg.target = target;
|
||||
cfg.type = ConfigurationType::Executable;
|
||||
cfg.dependencies.assign(mainDeps, mainDeps + 2);
|
||||
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import std;
|
||||
|
||||
int main() {
|
||||
// hello-world is degenerate: the runner reports ✅ on exit 0, which is the
|
||||
// signal that the build produced a runnable binary.
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 Catcrafts®
|
||||
Catcrafts.net
|
||||
|
||||
LGPL-3.0-only.
|
||||
*/
|
||||
|
||||
import std;
|
||||
import Crafter.Build;
|
||||
#include "../_shared/TestUtil.h"
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace TestUtil;
|
||||
using namespace Crafter;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
fs::path src = fs::current_path() / "tests" / "Incremental" / "inner";
|
||||
Configuration cfg = LoadFixture("Incremental", src);
|
||||
fs::path work = fs::current_path();
|
||||
|
||||
auto cold = BuildOnce(cfg);
|
||||
if (!cold.result.empty()) {
|
||||
std::println(std::cerr, "cold build failed:\n{}", cold.result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fs::path greeterObj = cfg.BuildDir() / "Greeter.o";
|
||||
fs::path mainObj = cfg.BuildDir() / "main_impl.o";
|
||||
if (!fs::exists(greeterObj) || !fs::exists(mainObj)) {
|
||||
std::println(std::cerr, "expected .o files missing after cold build");
|
||||
return 1;
|
||||
}
|
||||
auto greeter_t1 = fs::last_write_time(greeterObj);
|
||||
auto main_t1 = fs::last_write_time(mainObj);
|
||||
|
||||
auto noop = BuildOnce(cfg);
|
||||
if (!noop.result.empty()) {
|
||||
std::println(std::cerr, "no-op rebuild failed:\n{}", noop.result);
|
||||
return 1;
|
||||
}
|
||||
if (fs::last_write_time(greeterObj) != greeter_t1) {
|
||||
std::println(std::cerr, "no-op rebuild regenerated Greeter.o");
|
||||
return 1;
|
||||
}
|
||||
if (fs::last_write_time(mainObj) != main_t1) {
|
||||
std::println(std::cerr, "no-op rebuild regenerated main_impl.o");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Touch main.cpp: only main_impl.o should regenerate.
|
||||
fs::last_write_time(work / "main.cpp", std::chrono::file_clock::now() + 2s);
|
||||
auto touched = BuildOnce(cfg);
|
||||
if (!touched.result.empty()) {
|
||||
std::println(std::cerr, "rebuild after touch failed:\n{}", touched.result);
|
||||
return 1;
|
||||
}
|
||||
if (fs::last_write_time(greeterObj) != greeter_t1) {
|
||||
std::println(std::cerr, "touching main.cpp unnecessarily rebuilt Greeter.o");
|
||||
return 1;
|
||||
}
|
||||
if (fs::last_write_time(mainObj) <= main_t1) {
|
||||
std::println(std::cerr, "touching main.cpp did NOT rebuild main_impl.o");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::println(std::cerr, "test exception: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export module Greeter;
|
||||
import std;
|
||||
|
||||
export std::string Greet(std::string_view name) {
|
||||
return std::format("hello, {}!", name);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import std;
|
||||
import Greeter;
|
||||
|
||||
int main() {
|
||||
std::println("{}", Greet("crafter"));
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
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 = "./";
|
||||
cfg.name = "hello-mod";
|
||||
cfg.outputName = "hello-mod";
|
||||
cfg.target = "x86_64-pc-linux-gnu";
|
||||
cfg.type = ConfigurationType::Executable;
|
||||
|
||||
std::array<fs::path, 1> ifaces = { "interfaces/Greeter" };
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
|
||||
cfg.linkFlags.push_back("-Wl,--export-dynamic");
|
||||
cfg.linkFlags.push_back("-ldl");
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
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/Incremental/";
|
||||
cfg.name = "Incremental";
|
||||
cfg.outputName = "Incremental";
|
||||
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 = { "Incremental" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 Catcrafts®
|
||||
Catcrafts.net
|
||||
|
||||
LGPL-3.0-only.
|
||||
*/
|
||||
|
||||
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" / "Libraries" / "inner";
|
||||
Configuration cfg = LoadFixture("Libraries", src);
|
||||
fs::path work = fs::current_path();
|
||||
|
||||
auto br = BuildOnce(cfg);
|
||||
if (!br.result.empty()) {
|
||||
std::println(std::cerr, "build failed:\n{}", br.result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fs::path staticArchive = cfg.dependencies[0]->BinDir() / "libMathLib.a";
|
||||
fs::path dynamicSO = cfg.dependencies[1]->BinDir() / "libGreetLib.so";
|
||||
fs::path artifact = cfg.BinDir() / "libs-app";
|
||||
|
||||
if (!fs::exists(staticArchive)) {
|
||||
std::println(std::cerr, "static archive missing at {}", staticArchive.string());
|
||||
return 1;
|
||||
}
|
||||
if (!fs::exists(dynamicSO)) {
|
||||
std::println(std::cerr, "dynamic .so missing at {}", dynamicSO.string());
|
||||
return 1;
|
||||
}
|
||||
if (!fs::exists(artifact)) {
|
||||
std::println(std::cerr, "exe missing at {}", artifact.string());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Linked against the dynamic .so which lives in greetlib/bin/...; set
|
||||
// LD_LIBRARY_PATH explicitly for the artifact run.
|
||||
auto run = RunInDir(work, std::format(
|
||||
"LD_LIBRARY_PATH='{}' '{}'",
|
||||
dynamicSO.parent_path().string(), artifact.string()));
|
||||
if (run.exitCode != 0) {
|
||||
std::println(std::cerr, "artifact exited nonzero (rc={}):\n{}", run.exitCode, run.output);
|
||||
return 1;
|
||||
}
|
||||
if (run.output != "hi=42\n") {
|
||||
std::println(std::cerr, "output mismatch:\n expected: \"hi=42\\n\"\n got: {:?}", run.output);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::println(std::cerr, "test exception: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export module GreetLib;
|
||||
import std;
|
||||
|
||||
export std::string Greet() {
|
||||
return "hi";
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import std;
|
||||
import MathLib;
|
||||
import GreetLib;
|
||||
|
||||
int main() {
|
||||
std::println("{}={}", Greet(), Add(40, 2));
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
export module MathLib;
|
||||
|
||||
export int Add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
import std;
|
||||
import Crafter.Build;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Crafter;
|
||||
|
||||
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view>) {
|
||||
static auto MathStatic = std::make_unique<Configuration>();
|
||||
MathStatic->path = "./mathlib/";
|
||||
MathStatic->name = "MathLib";
|
||||
MathStatic->outputName = "MathLib";
|
||||
MathStatic->target = "x86_64-pc-linux-gnu";
|
||||
MathStatic->type = ConfigurationType::LibraryStatic;
|
||||
{
|
||||
std::array<fs::path, 1> ifaces = { "MathLib" };
|
||||
std::array<fs::path, 0> impls = {};
|
||||
MathStatic->GetInterfacesAndImplementations(ifaces, impls);
|
||||
}
|
||||
|
||||
static auto GreetDynamic = std::make_unique<Configuration>();
|
||||
GreetDynamic->path = "./greetlib/";
|
||||
GreetDynamic->name = "GreetLib";
|
||||
GreetDynamic->outputName = "GreetLib";
|
||||
GreetDynamic->target = "x86_64-pc-linux-gnu";
|
||||
GreetDynamic->type = ConfigurationType::LibraryDynamic;
|
||||
{
|
||||
std::array<fs::path, 1> ifaces = { "GreetLib" };
|
||||
std::array<fs::path, 0> impls = {};
|
||||
GreetDynamic->GetInterfacesAndImplementations(ifaces, impls);
|
||||
}
|
||||
|
||||
Configuration app;
|
||||
app.path = "./";
|
||||
app.name = "libs-app";
|
||||
app.outputName = "libs-app";
|
||||
app.target = "x86_64-pc-linux-gnu";
|
||||
app.type = ConfigurationType::Executable;
|
||||
app.dependencies = { MathStatic.get(), GreetDynamic.get() };
|
||||
{
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
app.GetInterfacesAndImplementations(ifaces, impls);
|
||||
}
|
||||
app.linkFlags.push_back("-Wl,--export-dynamic");
|
||||
app.linkFlags.push_back("-ldl");
|
||||
|
||||
return app;
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
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/Libraries/";
|
||||
cfg.name = "Libraries";
|
||||
cfg.outputName = "Libraries";
|
||||
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 = { "Libraries" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 Catcrafts®
|
||||
Catcrafts.net
|
||||
|
||||
LGPL-3.0-only.
|
||||
*/
|
||||
|
||||
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" / "RunnerClassification" / "inner";
|
||||
Configuration cfg = LoadFixture("RunnerClassification", src);
|
||||
|
||||
RunTestsOptions opts;
|
||||
opts.timeoutOverride = std::chrono::seconds(2); // Hang test bounded
|
||||
TestSummary summary = RunTests(cfg, opts);
|
||||
|
||||
if (summary.passed != 1 || summary.failed != 1 ||
|
||||
summary.crashed != 1 || summary.timedOut != 1 || summary.skipped != 0) {
|
||||
std::println(std::cerr,
|
||||
"outcome counts mismatch: passed={} failed={} crashed={} timedOut={} skipped={}",
|
||||
summary.passed, summary.failed, summary.crashed, summary.timedOut, summary.skipped);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::println(std::cerr, "test exception: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
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 = "./";
|
||||
cfg.name = "rc-meta";
|
||||
cfg.outputName = "rc-meta";
|
||||
cfg.target = "x86_64-pc-linux-gnu";
|
||||
cfg.type = ConfigurationType::Executable;
|
||||
|
||||
auto addTest = [&](std::string name) {
|
||||
Test t;
|
||||
t.config.path = "./";
|
||||
t.config.name = name;
|
||||
t.config.outputName = name;
|
||||
t.config.target = "x86_64-pc-linux-gnu";
|
||||
t.config.type = ConfigurationType::Executable;
|
||||
std::array<fs::path, 0> empty = {};
|
||||
std::array<fs::path, 1> impls = { fs::path("tests") / name };
|
||||
t.config.GetInterfacesAndImplementations(empty, impls);
|
||||
cfg.tests.push_back(std::move(t));
|
||||
};
|
||||
|
||||
addTest("Pass");
|
||||
addTest("Fail");
|
||||
addTest("Crash");
|
||||
addTest("Hang");
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
int main() {
|
||||
*(volatile int*)0 = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
int main() { return 1; }
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
int main() {
|
||||
for (;;) {}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
int main() { return 0; }
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
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/RunnerClassification/";
|
||||
cfg.name = "RunnerClassification";
|
||||
cfg.outputName = "RunnerClassification";
|
||||
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 = { "RunnerClassification" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 Catcrafts®
|
||||
Catcrafts.net
|
||||
|
||||
LGPL-3.0-only.
|
||||
|
||||
End-to-end shader build through the V2 pipeline:
|
||||
the inner project.cpp registers a vertex shader via cfg.shaders, and
|
||||
crafter.build-lib invokes glslang in-process to lower it to SPIR-V. The
|
||||
test verifies the resulting .spv file lands in the per-target bin dir
|
||||
and starts with the SPIR-V magic word — a zero-length output or one
|
||||
written under a different name would be flagged here rather than
|
||||
silently passing.
|
||||
*/
|
||||
|
||||
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" / "Shader" / "inner";
|
||||
Configuration cfg = LoadFixture("Shader", src);
|
||||
fs::path work = fs::current_path();
|
||||
|
||||
auto br = BuildOnce(cfg);
|
||||
if (!br.result.empty()) {
|
||||
std::println(std::cerr, "build failed:\n{}", br.result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fs::path spv = cfg.BinDir() / "triangle.spv";
|
||||
if (!fs::exists(spv)) {
|
||||
std::println(std::cerr, "expected SPIR-V output missing at {}", spv.string());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// SPIR-V module header magic is 0x07230203, written as a 32-bit word
|
||||
// in the module's endianness (always little-endian on the targets we
|
||||
// build). Matching the raw byte sequence catches truncated writes
|
||||
// and any future regression that produces a non-SPIR-V .spv.
|
||||
std::ifstream f(spv, 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: 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import std;
|
||||
|
||||
int main() { return 0; }
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
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 = "./";
|
||||
cfg.name = "shader-test";
|
||||
cfg.outputName = "shader-test";
|
||||
cfg.target = "x86_64-pc-linux-gnu";
|
||||
cfg.type = ConfigurationType::Executable;
|
||||
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
|
||||
cfg.shaders.emplace_back("triangle.glsl", "main", ShaderType::Vertex);
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#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);
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
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/Shader/";
|
||||
cfg.name = "Shader";
|
||||
cfg.outputName = "Shader";
|
||||
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 = { "Shader" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
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<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;
|
||||
}
|
||||
|
||||
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<char*>(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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#version 450
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
|
||||
#include "ui-shared.glsl"
|
||||
|
||||
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 = ui_color();
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export module ShaderLib;
|
||||
|
||||
export int Identify() { return 0; }
|
||||
|
|
@ -1 +0,0 @@
|
|||
crafter-build asset
|
||||
|
|
@ -1 +0,0 @@
|
|||
nested asset
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#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);
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef UI_SHARED_GLSL
|
||||
#define UI_SHARED_GLSL
|
||||
|
||||
vec3 ui_color() {
|
||||
return vec3(0.25, 0.5, 1.0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
import std;
|
||||
import ShaderLib;
|
||||
|
||||
int main() { return Identify(); }
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
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);
|
||||
ShaderLib->files.emplace_back("./lib/asset.txt");
|
||||
ShaderLib->files.emplace_back("./lib/data");
|
||||
ShaderLib->buildFiles.emplace_back("./lib/ui-shared.glsl");
|
||||
|
||||
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);
|
||||
}
|
||||
app.shaders.emplace_back("./consumer.glsl", "main", ShaderType::Vertex);
|
||||
return app;
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
import std;
|
||||
import Crafter.Build;
|
||||
using namespace Crafter;
|
||||
|
||||
int main() {
|
||||
// Local() is the no-op runner — empty exec template, name "local".
|
||||
if (TestRunner::Local().name != "local") return 1;
|
||||
|
||||
// FromSpec parses each known kind and labels it consistently.
|
||||
auto cmd = TestRunner::FromSpec("cmd:wine");
|
||||
if (!cmd || cmd->name != "cmd:wine") return 1;
|
||||
|
||||
auto local = TestRunner::FromSpec("local");
|
||||
if (!local || local->name != "local") return 1;
|
||||
|
||||
// Empty input returns nullopt; anything else unrecognized throws.
|
||||
// ssh/sshwin/wsl used to be valid specs; they're now bogus (issue #8).
|
||||
if (TestRunner::FromSpec("")) return 1;
|
||||
for (auto bogus : { "nonsense:thing", "ssh:somehost", "sshwin:winhost", "wsl" }) {
|
||||
try {
|
||||
TestRunner::FromSpec(bogus);
|
||||
return 1;
|
||||
} catch (const std::exception&) {}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
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/UnitLib/";
|
||||
cfg.name = "UnitLib";
|
||||
cfg.outputName = "UnitLib";
|
||||
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 = { "main" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import std;
|
||||
|
||||
int main() {
|
||||
std::println("Hello, WASI!");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# WASI build via the WASI SDK's libc++ + the wasm32-wasip1 target. The
|
||||
# runner derivation routes the .wasm artifact through `wasmtime`.
|
||||
target = "wasm32-wasip1"
|
||||
requires = [
|
||||
"tool:wasmtime",
|
||||
"file:/usr/share/wasi-sysroot/share/libc++/v1/std.cppm",
|
||||
]
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import std;
|
||||
|
||||
int main() {
|
||||
std::println("hi from win{}", sizeof(void*) * 8);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# Linux→Windows end-to-end:
|
||||
# crafter-build cross-compiles main.cpp to x86_64-w64-mingw32 (.exe) and the
|
||||
# runner derivation wraps it in `wine`. Replaces the old WindowsViaSsh test,
|
||||
# which required a reachable Windows VM and an ssh + cmd.exe shell chain.
|
||||
target = "x86_64-w64-mingw32"
|
||||
requires = [
|
||||
"tool:wine",
|
||||
"tool:x86_64-w64-mingw32-g++",
|
||||
]
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export module Greeter;
|
||||
import std;
|
||||
|
||||
export std::string Greet(std::string_view name) {
|
||||
return std::format("hello, {}!", name);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import std;
|
||||
import Greeter;
|
||||
|
||||
int main() {
|
||||
if (Greet("crafter") != "hello, crafter!") return 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#pragma once
|
||||
#include <stdlib.h> // setenv (POSIX, not in `import std`)
|
||||
|
||||
namespace TestUtil {
|
||||
// Exit code 77 follows the autoconf convention. crafter-build's test
|
||||
// runner maps it to TestOutcome::Skipped and renders the test's stdout
|
||||
// as the reason. Use this when the test discovers at runtime that its
|
||||
// preconditions aren't met (tool missing, env unset, etc).
|
||||
[[noreturn]] inline void Skip(std::string_view reason) {
|
||||
std::print("{}", reason);
|
||||
std::exit(77);
|
||||
}
|
||||
|
||||
inline std::string ReadFile(const std::filesystem::path& p) {
|
||||
std::ifstream f(p);
|
||||
std::stringstream ss;
|
||||
ss << f.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
inline std::filesystem::path CopyFixtureToTemp(std::string_view testName, const std::filesystem::path& source) {
|
||||
namespace fs = std::filesystem;
|
||||
fs::path tmp = fs::temp_directory_path() / std::format("crafter-test-{}", testName);
|
||||
fs::remove_all(tmp);
|
||||
fs::copy(source, tmp, fs::copy_options::recursive);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
struct CmdResult {
|
||||
int exitCode;
|
||||
std::string output;
|
||||
};
|
||||
|
||||
inline CmdResult RunInDir(const std::filesystem::path& cwd, std::string_view command) {
|
||||
namespace fs = std::filesystem;
|
||||
// Log inside cwd so parallel test drivers don't trample each other.
|
||||
fs::path log = cwd / ".crafter-cmd-output.log";
|
||||
std::string cmd = std::format("cd '{}' && {} > '{}' 2>&1",
|
||||
cwd.string(), command, log.string());
|
||||
int rc = std::system(cmd.c_str());
|
||||
std::string out = ReadFile(log);
|
||||
std::error_code ec;
|
||||
fs::remove(log, ec);
|
||||
return {rc, std::move(out)};
|
||||
}
|
||||
|
||||
// Build cfg with a fresh dep cache. Convenient for outer-driver tests that
|
||||
// exercise the build API in-process and don't need to share the dep cache
|
||||
// across multiple Build() calls.
|
||||
inline Crafter::BuildResult BuildOnce(Crafter::Configuration& cfg) {
|
||||
std::unordered_map<std::filesystem::path, std::shared_future<Crafter::BuildResult>> depResults;
|
||||
std::mutex depMutex;
|
||||
return Crafter::Build(cfg, depResults, depMutex);
|
||||
}
|
||||
|
||||
// Copy a fixture into a fresh temp dir, chdir there (so cfg.path = "./"
|
||||
// inside the inner project.cpp resolves to the temp dir), and load its
|
||||
// project.cpp. Common prep for in-process build/test API tests.
|
||||
//
|
||||
// Also sets CRAFTER_BUILD_HOME to <projectRoot>/share/crafter-build before
|
||||
// loading. The lib's default sourceDir is derived from /proc/self/exe, but
|
||||
// test exes live in tests/<Name>/bin/... rather than next to the project's
|
||||
// share/, so the default lookup misses. The test runner launches tests
|
||||
// with cwd = project root, so we capture that here before the chdir below.
|
||||
inline Crafter::Configuration LoadFixture(std::string_view testName,
|
||||
const std::filesystem::path& source,
|
||||
std::span<const std::string_view> args = {}) {
|
||||
auto projectRoot = std::filesystem::current_path();
|
||||
auto sharePath = projectRoot / "share" / "crafter-build";
|
||||
::setenv("CRAFTER_BUILD_HOME", sharePath.string().c_str(), 1);
|
||||
|
||||
auto work = CopyFixtureToTemp(testName, source);
|
||||
std::filesystem::current_path(work);
|
||||
return Crafter::LoadProject("project.cpp", args);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue