V2: WASI, -r flag, CI pipeline, examples & tests cleanup
Some checks failed
CI / build-test-release (pull_request) Failing after 44s
Some checks failed
CI / build-test-release (pull_request) Failing after 44s
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
cdfdb976c8
commit
eaee502e8c
102 changed files with 2211 additions and 686 deletions
152
project.cpp
152
project.cpp
|
|
@ -5,15 +5,17 @@ using namespace Crafter;
|
|||
|
||||
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
|
||||
bool debug = false;
|
||||
std::string target = "x86_64-pc-linux-gnu";
|
||||
for (std::string_view arg : args) {
|
||||
if (arg == "--debug") debug = true;
|
||||
else if (arg.starts_with("--target=")) target = std::string(arg.substr(std::string_view("--target=").size()));
|
||||
}
|
||||
|
||||
static auto crafterBuildLib = std::make_unique<Configuration>();
|
||||
crafterBuildLib->path = "./";
|
||||
crafterBuildLib->name = "crafter.build-lib";
|
||||
crafterBuildLib->outputName = "crafter-build";
|
||||
crafterBuildLib->target = "x86_64-pc-linux-gnu";
|
||||
crafterBuildLib->target = target;
|
||||
crafterBuildLib->type = ConfigurationType::LibraryStatic;
|
||||
crafterBuildLib->debug = debug;
|
||||
{
|
||||
|
|
@ -44,6 +46,12 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
glslang.source.branch = "main";
|
||||
glslang.builder = ExternalBuilder::CMake;
|
||||
glslang.options = { "-DENABLE_OPT=OFF" };
|
||||
// mingw cross-build: skip the standalone executable. We only consume the
|
||||
// libraries, and glslang.exe pulls in libgcc_eh which needs pthread that
|
||||
// mingw-w64 doesn't link by default.
|
||||
if (target == "x86_64-w64-mingw32") {
|
||||
glslang.options.push_back("-DENABLE_GLSLANG_BINARIES=OFF");
|
||||
}
|
||||
glslang.includeDirs = { "" };
|
||||
glslang.libs = { "SPIRV", "GenericCodeGen", "glslang", "OSDependent", "MachineIndependent", "glslang-default-resource-limits" };
|
||||
|
||||
|
|
@ -51,7 +59,7 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
cfg.path = "./";
|
||||
cfg.name = "crafter.build-exe";
|
||||
cfg.outputName = "crafter-build";
|
||||
cfg.target = "x86_64-pc-linux-gnu";
|
||||
cfg.target = target;
|
||||
cfg.type = ConfigurationType::Executable;
|
||||
cfg.debug = debug;
|
||||
cfg.dependencies = { crafterBuildLib.get() };
|
||||
|
|
@ -60,142 +68,10 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
std::array<fs::path, 1> implementations = { "implementations/main" };
|
||||
cfg.GetInterfacesAndImplementations(interfaces, implementations);
|
||||
}
|
||||
cfg.linkFlags.push_back("-Wl,--export-dynamic");
|
||||
cfg.linkFlags.push_back("-ldl");
|
||||
|
||||
// ----- Single-driver, multi-target tests (each appears once per (target, runner)). -----
|
||||
|
||||
// Lifetime holders for per-target lib Configurations referenced by Tests.
|
||||
static std::vector<std::unique_ptr<Configuration>> testLibPool;
|
||||
|
||||
struct TargetRunner {
|
||||
std::string target;
|
||||
TestRunner runner;
|
||||
std::vector<std::string> extraLinkFlags;
|
||||
};
|
||||
std::vector<TargetRunner> targets = {
|
||||
{ "x86_64-pc-linux-gnu", TestRunner::Local(), {} },
|
||||
{ "x86_64-w64-mingw32", TestRunner::SshWin("winvm", "C:/temp/crafter-tests"), {"-lstdc++exp"} },
|
||||
};
|
||||
|
||||
auto addPerTarget = [&](std::string name, auto buildOne) {
|
||||
for (auto& tr : targets) {
|
||||
Test t;
|
||||
t.config.name = name;
|
||||
t.config.target = tr.target;
|
||||
t.config.type = ConfigurationType::Executable;
|
||||
t.config.linkFlags.push_back("-fuse-ld=lld");
|
||||
for (auto& f : tr.extraLinkFlags) t.config.linkFlags.push_back(f);
|
||||
buildOne(t, tr.target);
|
||||
t.runner = tr.runner;
|
||||
cfg.tests.push_back(std::move(t));
|
||||
}
|
||||
};
|
||||
|
||||
addPerTarget("HelloWorld", [](Test& t, std::string_view) {
|
||||
t.config.path = "tests/fixtures/hello-world/";
|
||||
t.config.outputName = "hello";
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
t.config.GetInterfacesAndImplementations(ifaces, impls);
|
||||
});
|
||||
|
||||
addPerTarget("WithModule", [](Test& t, std::string_view) {
|
||||
t.config.path = "tests/fixtures/with-module/";
|
||||
t.config.outputName = "hello-mod";
|
||||
std::array<fs::path, 1> ifaces = { "interfaces/Greeter" };
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
t.config.GetInterfacesAndImplementations(ifaces, impls);
|
||||
});
|
||||
|
||||
addPerTarget("Defines", [](Test& t, std::string_view) {
|
||||
t.config.path = "tests/fixtures/defines/";
|
||||
t.config.outputName = "defines-app";
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
t.config.GetInterfacesAndImplementations(ifaces, impls);
|
||||
t.config.defines.push_back({"CRAFTER_TEST_FOO", "42"});
|
||||
});
|
||||
|
||||
addPerTarget("CrossProjectModule", [&](Test& t, std::string_view target) {
|
||||
auto fooLib = std::make_unique<Configuration>();
|
||||
fooLib->path = "tests/fixtures/cross-project/lib/";
|
||||
fooLib->name = std::format("Foo-cross-project-{}", target);
|
||||
fooLib->outputName = "Foo";
|
||||
fooLib->target = std::string(target);
|
||||
fooLib->type = ConfigurationType::LibraryStatic;
|
||||
std::array<fs::path, 1> libIfaces = { "Foo" };
|
||||
std::array<fs::path, 0> libImpls = {};
|
||||
fooLib->GetInterfacesAndImplementations(libIfaces, libImpls);
|
||||
Configuration* fooLibPtr = fooLib.get();
|
||||
testLibPool.push_back(std::move(fooLib));
|
||||
|
||||
t.config.path = "tests/fixtures/cross-project/";
|
||||
t.config.outputName = "cross-app";
|
||||
t.config.dependencies = { fooLibPtr };
|
||||
std::array<fs::path, 0> mainIfaces = {};
|
||||
std::array<fs::path, 1> mainImpls = { "main" };
|
||||
t.config.GetInterfacesAndImplementations(mainIfaces, mainImpls);
|
||||
});
|
||||
|
||||
auto makeDiamondLib = [&](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/fixtures/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;
|
||||
};
|
||||
|
||||
addPerTarget("Diamond", [&](Test& t, std::string_view target) {
|
||||
auto X = makeDiamondLib("X", "X", target, {});
|
||||
Configuration* xDeps[] = { X.get() };
|
||||
auto B = makeDiamondLib("B", "B", target, xDeps);
|
||||
auto C = makeDiamondLib("C", "C", target, xDeps);
|
||||
Configuration* mainDeps[] = { B.get(), C.get() };
|
||||
|
||||
t.config.path = "tests/fixtures/diamond/";
|
||||
t.config.outputName = "diamond-app";
|
||||
t.config.dependencies.assign(mainDeps, mainDeps + 2);
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
t.config.GetInterfacesAndImplementations(ifaces, impls);
|
||||
|
||||
testLibPool.push_back(std::move(X));
|
||||
testLibPool.push_back(std::move(B));
|
||||
testLibPool.push_back(std::move(C));
|
||||
});
|
||||
|
||||
// ----- Outer-driver tests (still need their own driver source for multi-step
|
||||
// logic or meta-runner behavior). -----
|
||||
|
||||
auto addOuterDriverTest = [&](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));
|
||||
};
|
||||
|
||||
addOuterDriverTest("Incremental");
|
||||
addOuterDriverTest("BuildError");
|
||||
addOuterDriverTest("Libraries");
|
||||
addOuterDriverTest("RunnerClassification");
|
||||
addOuterDriverTest("QemuUser");
|
||||
addOuterDriverTest("SshRunner");
|
||||
addOuterDriverTest("CrossArchAarch64");
|
||||
addOuterDriverTest("WindowsViaSsh");
|
||||
if (target == "x86_64-pc-linux-gnu") {
|
||||
cfg.linkFlags.push_back("-Wl,--export-dynamic");
|
||||
cfg.linkFlags.push_back("-ldl");
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue