2026-07-22 18:16:38 +02:00
|
|
|
//SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
|
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
|
|
test: broaden self-test coverage beyond the compile smoke test
The suite had only HelloWorld, which built and exited an empty exe. Add
in-process tests covering each public surface area users actually touch:
- StaticLib / ModuleInterface / DependencyLink — Build() against
fixtures for libraries, project-local module interfaces, and
cross-config module deps with link verification (runs the built exe).
- ShaderCompile — drives Shader::Compile directly, validates SPIR-V
magic + Check() idempotency.
- StandardArgs — covers --debug, --target=, --march=, --mtune=,
--lib/--shared promotions, and ArgQuery::Has / Get.
- TestRunnerSpec — FromSpec parse rules, ForTarget routing for host,
wasm32-wasip1, aarch64-linux-gnu (+ sysroot QEMU_LD_PREFIX),
i686 → qemu-i386 rewrite, mingw → wine on Linux hosts, FromEnv.
- VariantId — confirms type / debug / sysroot / defines / compileFlags /
target / march all perturb the cache key, plus PcmDir routing.
- WasiBrowserRuntime — calls EnableWasiBrowserRuntime, asserts the
three cfg.files entries get registered and index.html had its
template placeholder substituted.
- RunSingleTestExit — drives RunSingleTest against tiny sh scripts and
pins the documented exit-code mapping (0/77/non-zero) and the
Cmd-prefix runner path.
Closes #12.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 19:38:18 +00:00
|
|
|
import std;
|
|
|
|
|
import Crafter.Build;
|
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
using namespace Crafter;
|
|
|
|
|
|
|
|
|
|
// POSIX env helper — std::setenv is non-standard. Linux-only is fine here;
|
|
|
|
|
// project.cpp gates this test on x86_64-pc-linux-gnu.
|
|
|
|
|
extern "C" int setenv(const char* name, const char* value, int overwrite);
|
|
|
|
|
|
|
|
|
|
// EnableWasiBrowserRuntime stamps runtime.js, a generated index.html, and a
|
|
|
|
|
// files.json manifest onto cfg.files so the build step copies them next to
|
|
|
|
|
// the .wasm. We don't run the wasm build here (avoids requiring wasi-sdk +
|
|
|
|
|
// the wasi-libc++ PCM rebuild on top of every test); we just verify the
|
|
|
|
|
// helper produced the right artifacts and registered them on the config.
|
|
|
|
|
int main() {
|
|
|
|
|
Configuration cfg;
|
|
|
|
|
cfg.path = fs::temp_directory_path() / "crafter-build-wasi-runtime-test";
|
|
|
|
|
cfg.name = "wasi-helper";
|
|
|
|
|
cfg.outputName = "wasi-helper";
|
|
|
|
|
cfg.target = "wasm32-wasip1";
|
|
|
|
|
cfg.type = ConfigurationType::Executable;
|
|
|
|
|
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
fs::remove_all(cfg.path, ec);
|
|
|
|
|
fs::create_directories(cfg.path);
|
|
|
|
|
|
|
|
|
|
// EnableWasiBrowserRuntime looks up runtime.js + index.html.in under
|
|
|
|
|
// GetCrafterBuildHome()/wasi-runtime. The test binary lives in
|
|
|
|
|
// bin/<variant>/, so the auto-detect walks parents and won't find a
|
|
|
|
|
// share/crafter-build/Crafter.Build.cppm anchor — point CRAFTER_BUILD_HOME
|
|
|
|
|
// at the repo root (cwd when crafter-build test runs) so it picks up
|
|
|
|
|
// ./wasi-runtime/. Probe both repo root and the system-installed share
|
|
|
|
|
// dir to also work from a packaged checkout.
|
|
|
|
|
fs::path repoWasi = fs::current_path() / "wasi-runtime";
|
|
|
|
|
fs::path systemHome = "/usr/local/share/crafter-build";
|
|
|
|
|
if (fs::exists(repoWasi / "runtime.js")) {
|
|
|
|
|
setenv("CRAFTER_BUILD_HOME", fs::current_path().c_str(), 1);
|
|
|
|
|
} else if (fs::exists(systemHome / "wasi-runtime" / "runtime.js")) {
|
|
|
|
|
setenv("CRAFTER_BUILD_HOME", systemHome.c_str(), 1);
|
|
|
|
|
} else {
|
|
|
|
|
std::println(std::cerr,
|
|
|
|
|
"wasi-runtime assets not found near {} or {}",
|
|
|
|
|
repoWasi.string(), systemHome.string());
|
|
|
|
|
return 77; // skipped — environmental, not a code defect
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EnableWasiBrowserRuntime(cfg);
|
|
|
|
|
|
|
|
|
|
// Three files registered: runtime.js, generated index.html, files.json.
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
The browser wasm pipeline hardcoded -msimd128 for every wasm32 target and
baked a single wasm URL into index.html, so newer codegen features that
aren't yet baseline across engines (relaxed SIMD today; threads, future SIMD
revisions later) couldn't be adopted without dropping the browsers that lack
them.
Add a general, feature-parameterized mechanism owned entirely by
Crafter.Build:
- Configuration::wasmVariants declares N codegen variants (label, extra -m
flags, runtime probes). Build() compiles the baseline plus one
outputName.<label>.wasm per variant, recompiling the whole graph (incl.
dep libs + std PCM) with the variant's flags — relaxed-SIMD is per-TU
codegen, not a link switch. wasmVariantFlags folds into VariantId so each
variant's objects/PCMs land in their own build+bin dir.
- EnableWasiBrowserRuntime emits a variants.json manifest (label -> url +
probes), preferred-first with the baseline as the universal fallback.
- The shipped runtime.js runs inlined wasm-feature-detect probes
(relaxed-simd, simd, tail-call, bulk-memory, exception-handling, threads),
picks the first variant whose probes all pass, and falls back to the single
baked CRAFTER_WASM_URL when no manifest is present (backward compatible).
- EnableWasiRelaxedSimdVariant registers the relaxed-SIMD variant — the
motivating case (Chrome 114+/Firefox 120+ enable it by default; Safari
still flag-gates it as of mid-2026).
Verified end to end: a wasm32-wasip1 build emits both wasi-hello.wasm and
wasi-hello.relaxed-simd.wasm + variants.json; Firefox selects the
relaxed-simd variant and runs it.
Resolves #24
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:14:18 +00:00
|
|
|
if (cfg.files.size() != 4) {
|
|
|
|
|
std::println(std::cerr, "expected 4 cfg.files entries, got {}", cfg.files.size());
|
test: broaden self-test coverage beyond the compile smoke test
The suite had only HelloWorld, which built and exited an empty exe. Add
in-process tests covering each public surface area users actually touch:
- StaticLib / ModuleInterface / DependencyLink — Build() against
fixtures for libraries, project-local module interfaces, and
cross-config module deps with link verification (runs the built exe).
- ShaderCompile — drives Shader::Compile directly, validates SPIR-V
magic + Check() idempotency.
- StandardArgs — covers --debug, --target=, --march=, --mtune=,
--lib/--shared promotions, and ArgQuery::Has / Get.
- TestRunnerSpec — FromSpec parse rules, ForTarget routing for host,
wasm32-wasip1, aarch64-linux-gnu (+ sysroot QEMU_LD_PREFIX),
i686 → qemu-i386 rewrite, mingw → wine on Linux hosts, FromEnv.
- VariantId — confirms type / debug / sysroot / defines / compileFlags /
target / march all perturb the cache key, plus PcmDir routing.
- WasiBrowserRuntime — calls EnableWasiBrowserRuntime, asserts the
three cfg.files entries get registered and index.html had its
template placeholder substituted.
- RunSingleTestExit — drives RunSingleTest against tiny sh scripts and
pins the documented exit-code mapping (0/77/non-zero) and the
Cmd-prefix runner path.
Closes #12.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 19:38:18 +00:00
|
|
|
for (const auto& f : cfg.files) std::println(std::cerr, " - {}", f.string());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto findByName = [&](std::string_view name) -> const fs::path* {
|
|
|
|
|
for (const fs::path& f : cfg.files) {
|
|
|
|
|
if (f.filename() == name) return &f;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const fs::path* runtimeJs = findByName("runtime.js");
|
|
|
|
|
const fs::path* indexHtml = findByName("index.html");
|
|
|
|
|
const fs::path* manifest = findByName("files.json");
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
The browser wasm pipeline hardcoded -msimd128 for every wasm32 target and
baked a single wasm URL into index.html, so newer codegen features that
aren't yet baseline across engines (relaxed SIMD today; threads, future SIMD
revisions later) couldn't be adopted without dropping the browsers that lack
them.
Add a general, feature-parameterized mechanism owned entirely by
Crafter.Build:
- Configuration::wasmVariants declares N codegen variants (label, extra -m
flags, runtime probes). Build() compiles the baseline plus one
outputName.<label>.wasm per variant, recompiling the whole graph (incl.
dep libs + std PCM) with the variant's flags — relaxed-SIMD is per-TU
codegen, not a link switch. wasmVariantFlags folds into VariantId so each
variant's objects/PCMs land in their own build+bin dir.
- EnableWasiBrowserRuntime emits a variants.json manifest (label -> url +
probes), preferred-first with the baseline as the universal fallback.
- The shipped runtime.js runs inlined wasm-feature-detect probes
(relaxed-simd, simd, tail-call, bulk-memory, exception-handling, threads),
picks the first variant whose probes all pass, and falls back to the single
baked CRAFTER_WASM_URL when no manifest is present (backward compatible).
- EnableWasiRelaxedSimdVariant registers the relaxed-SIMD variant — the
motivating case (Chrome 114+/Firefox 120+ enable it by default; Safari
still flag-gates it as of mid-2026).
Verified end to end: a wasm32-wasip1 build emits both wasi-hello.wasm and
wasi-hello.relaxed-simd.wasm + variants.json; Firefox selects the
relaxed-simd variant and runs it.
Resolves #24
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:14:18 +00:00
|
|
|
const fs::path* variants = findByName("variants.json");
|
test: broaden self-test coverage beyond the compile smoke test
The suite had only HelloWorld, which built and exited an empty exe. Add
in-process tests covering each public surface area users actually touch:
- StaticLib / ModuleInterface / DependencyLink — Build() against
fixtures for libraries, project-local module interfaces, and
cross-config module deps with link verification (runs the built exe).
- ShaderCompile — drives Shader::Compile directly, validates SPIR-V
magic + Check() idempotency.
- StandardArgs — covers --debug, --target=, --march=, --mtune=,
--lib/--shared promotions, and ArgQuery::Has / Get.
- TestRunnerSpec — FromSpec parse rules, ForTarget routing for host,
wasm32-wasip1, aarch64-linux-gnu (+ sysroot QEMU_LD_PREFIX),
i686 → qemu-i386 rewrite, mingw → wine on Linux hosts, FromEnv.
- VariantId — confirms type / debug / sysroot / defines / compileFlags /
target / march all perturb the cache key, plus PcmDir routing.
- WasiBrowserRuntime — calls EnableWasiBrowserRuntime, asserts the
three cfg.files entries get registered and index.html had its
template placeholder substituted.
- RunSingleTestExit — drives RunSingleTest against tiny sh scripts and
pins the documented exit-code mapping (0/77/non-zero) and the
Cmd-prefix runner path.
Closes #12.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 19:38:18 +00:00
|
|
|
|
|
|
|
|
if (!runtimeJs) { std::println(std::cerr, "runtime.js not registered"); return 1; }
|
|
|
|
|
if (!indexHtml) { std::println(std::cerr, "index.html not registered"); return 1; }
|
|
|
|
|
if (!manifest) { std::println(std::cerr, "files.json not registered"); return 1; }
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
The browser wasm pipeline hardcoded -msimd128 for every wasm32 target and
baked a single wasm URL into index.html, so newer codegen features that
aren't yet baseline across engines (relaxed SIMD today; threads, future SIMD
revisions later) couldn't be adopted without dropping the browsers that lack
them.
Add a general, feature-parameterized mechanism owned entirely by
Crafter.Build:
- Configuration::wasmVariants declares N codegen variants (label, extra -m
flags, runtime probes). Build() compiles the baseline plus one
outputName.<label>.wasm per variant, recompiling the whole graph (incl.
dep libs + std PCM) with the variant's flags — relaxed-SIMD is per-TU
codegen, not a link switch. wasmVariantFlags folds into VariantId so each
variant's objects/PCMs land in their own build+bin dir.
- EnableWasiBrowserRuntime emits a variants.json manifest (label -> url +
probes), preferred-first with the baseline as the universal fallback.
- The shipped runtime.js runs inlined wasm-feature-detect probes
(relaxed-simd, simd, tail-call, bulk-memory, exception-handling, threads),
picks the first variant whose probes all pass, and falls back to the single
baked CRAFTER_WASM_URL when no manifest is present (backward compatible).
- EnableWasiRelaxedSimdVariant registers the relaxed-SIMD variant — the
motivating case (Chrome 114+/Firefox 120+ enable it by default; Safari
still flag-gates it as of mid-2026).
Verified end to end: a wasm32-wasip1 build emits both wasi-hello.wasm and
wasi-hello.relaxed-simd.wasm + variants.json; Firefox selects the
relaxed-simd variant and runs it.
Resolves #24
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:14:18 +00:00
|
|
|
if (!variants) { std::println(std::cerr, "variants.json not registered"); return 1; }
|
test: broaden self-test coverage beyond the compile smoke test
The suite had only HelloWorld, which built and exited an empty exe. Add
in-process tests covering each public surface area users actually touch:
- StaticLib / ModuleInterface / DependencyLink — Build() against
fixtures for libraries, project-local module interfaces, and
cross-config module deps with link verification (runs the built exe).
- ShaderCompile — drives Shader::Compile directly, validates SPIR-V
magic + Check() idempotency.
- StandardArgs — covers --debug, --target=, --march=, --mtune=,
--lib/--shared promotions, and ArgQuery::Has / Get.
- TestRunnerSpec — FromSpec parse rules, ForTarget routing for host,
wasm32-wasip1, aarch64-linux-gnu (+ sysroot QEMU_LD_PREFIX),
i686 → qemu-i386 rewrite, mingw → wine on Linux hosts, FromEnv.
- VariantId — confirms type / debug / sysroot / defines / compileFlags /
target / march all perturb the cache key, plus PcmDir routing.
- WasiBrowserRuntime — calls EnableWasiBrowserRuntime, asserts the
three cfg.files entries get registered and index.html had its
template placeholder substituted.
- RunSingleTestExit — drives RunSingleTest against tiny sh scripts and
pins the documented exit-code mapping (0/77/non-zero) and the
Cmd-prefix runner path.
Closes #12.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 19:38:18 +00:00
|
|
|
|
|
|
|
|
// The generated index.html and manifest live under the project's build/
|
|
|
|
|
// wasi-runtime/<name>/ directory and must actually exist on disk.
|
|
|
|
|
if (!fs::exists(*indexHtml)) {
|
|
|
|
|
std::println(std::cerr, "index.html not produced at {}", indexHtml->string());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (!fs::exists(*manifest)) {
|
|
|
|
|
std::println(std::cerr, "files.json not produced at {}", manifest->string());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
The browser wasm pipeline hardcoded -msimd128 for every wasm32 target and
baked a single wasm URL into index.html, so newer codegen features that
aren't yet baseline across engines (relaxed SIMD today; threads, future SIMD
revisions later) couldn't be adopted without dropping the browsers that lack
them.
Add a general, feature-parameterized mechanism owned entirely by
Crafter.Build:
- Configuration::wasmVariants declares N codegen variants (label, extra -m
flags, runtime probes). Build() compiles the baseline plus one
outputName.<label>.wasm per variant, recompiling the whole graph (incl.
dep libs + std PCM) with the variant's flags — relaxed-SIMD is per-TU
codegen, not a link switch. wasmVariantFlags folds into VariantId so each
variant's objects/PCMs land in their own build+bin dir.
- EnableWasiBrowserRuntime emits a variants.json manifest (label -> url +
probes), preferred-first with the baseline as the universal fallback.
- The shipped runtime.js runs inlined wasm-feature-detect probes
(relaxed-simd, simd, tail-call, bulk-memory, exception-handling, threads),
picks the first variant whose probes all pass, and falls back to the single
baked CRAFTER_WASM_URL when no manifest is present (backward compatible).
- EnableWasiRelaxedSimdVariant registers the relaxed-SIMD variant — the
motivating case (Chrome 114+/Firefox 120+ enable it by default; Safari
still flag-gates it as of mid-2026).
Verified end to end: a wasm32-wasip1 build emits both wasi-hello.wasm and
wasi-hello.relaxed-simd.wasm + variants.json; Firefox selects the
relaxed-simd variant and runs it.
Resolves #24
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:14:18 +00:00
|
|
|
if (!fs::exists(*variants)) {
|
|
|
|
|
std::println(std::cerr, "variants.json not produced at {}", variants->string());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// With no declared variants, variants.json is a single baseline entry
|
|
|
|
|
// pointing at outputName.wasm with no probes.
|
|
|
|
|
{
|
|
|
|
|
std::ifstream vin(*variants);
|
|
|
|
|
std::string vjson{std::istreambuf_iterator<char>(vin), std::istreambuf_iterator<char>()};
|
|
|
|
|
if (vjson.find("\"wasi-helper.wasm\"") == std::string::npos) {
|
|
|
|
|
std::println(std::cerr, "variants.json missing baseline url: {}", vjson);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (vjson.find("relaxed-simd") != std::string::npos) {
|
|
|
|
|
std::println(std::cerr, "variants.json unexpectedly mentions relaxed-simd: {}", vjson);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
test: broaden self-test coverage beyond the compile smoke test
The suite had only HelloWorld, which built and exited an empty exe. Add
in-process tests covering each public surface area users actually touch:
- StaticLib / ModuleInterface / DependencyLink — Build() against
fixtures for libraries, project-local module interfaces, and
cross-config module deps with link verification (runs the built exe).
- ShaderCompile — drives Shader::Compile directly, validates SPIR-V
magic + Check() idempotency.
- StandardArgs — covers --debug, --target=, --march=, --mtune=,
--lib/--shared promotions, and ArgQuery::Has / Get.
- TestRunnerSpec — FromSpec parse rules, ForTarget routing for host,
wasm32-wasip1, aarch64-linux-gnu (+ sysroot QEMU_LD_PREFIX),
i686 → qemu-i386 rewrite, mingw → wine on Linux hosts, FromEnv.
- VariantId — confirms type / debug / sysroot / defines / compileFlags /
target / march all perturb the cache key, plus PcmDir routing.
- WasiBrowserRuntime — calls EnableWasiBrowserRuntime, asserts the
three cfg.files entries get registered and index.html had its
template placeholder substituted.
- RunSingleTestExit — drives RunSingleTest against tiny sh scripts and
pins the documented exit-code mapping (0/77/non-zero) and the
Cmd-prefix runner path.
Closes #12.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 19:38:18 +00:00
|
|
|
|
|
|
|
|
// The template's {{WASM}} placeholder should have been replaced with
|
|
|
|
|
// the configured outputName.
|
|
|
|
|
std::ifstream in(*indexHtml);
|
|
|
|
|
std::string html{std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>()};
|
|
|
|
|
if (html.find("wasi-helper.wasm") == std::string::npos) {
|
|
|
|
|
std::println(std::cerr, "index.html missing wasi-helper.wasm reference");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (html.find("{{WASM}}") != std::string::npos) {
|
|
|
|
|
std::println(std::cerr, "index.html still contains unreplaced {{WASM}} placeholder");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fs::remove_all(cfg.path, ec);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|