// SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® import std; import Crafter.Build; namespace fs = std::filesystem; using namespace Crafter; // What a wasm32 build refuses before it starts, now that every wasm unit has // C++ exceptions, each with a message naming the cause: // - -flto in compileFlags or linkFlags. LTO code generation drops the // exception handling, and the build used to succeed with every throw // escaping its catch (WebAssembly/wasi-sdk#629). // - -fno-exceptions. The std module is built with exceptions, and every // `import std;` failed on a BMI configuration mismatch. // - A shared library. The exception-enabled libc++ is static archives // only, and the link failed on a relocation that wants -fPIC. // None of them leaves bin/ or build/ behind, and none needs a working wasm // toolchain. The sysroot given does not exist, so a refusal that did not // fire would show up as the missing sysroot instead. namespace { std::int32_t failures = 0; void Check(bool cond, std::string_view msg) { if (!cond) { std::println(std::cerr, "FAIL: {}", msg); ++failures; } } struct Refusal { std::string_view what; std::vector compileFlags; std::vector linkFlags; ConfigurationType type; std::string_view expected; }; } int main() { // Build() reports a missing compiler before it gets to any of this. if (const Toolchain& tc = GetToolchain(); !tc.error.empty()) { std::println(std::cerr, "SKIP: {}", tc.error); return 77; } const fs::path scratch = fs::temp_directory_path() / "crafter-build-wasm-build-refusals"; fs::remove_all(scratch); fs::create_directories(scratch); const fs::path project = scratch / "project"; fs::copy(fs::current_path() / "tests" / "WasmBuildRefusals" / "fixture", project, fs::copy_options::recursive); const std::array refusals = { Refusal{ "-flto in compileFlags", { "-flto=thin" }, {}, ConfigurationType::Executable, "-flto=thin is not supported on wasm32: LTO code generation drops C++ exception handling" }, Refusal{ "-flto in linkFlags", {}, { "-flto" }, ConfigurationType::Executable, "-flto is not supported on wasm32" }, Refusal{ "-fno-exceptions", { "-fno-exceptions" }, {}, ConfigurationType::Executable, "-fno-exceptions is not supported on wasm32" }, Refusal{ "a shared library", {}, {}, ConfigurationType::LibraryDynamic, "wasm32 shared libraries are not supported" }, }; for (const Refusal& refusal : refusals) { Configuration app; app.path = project; app.name = "hello"; app.outputName = "hello"; app.target = "wasm32-wasip1"; app.sysroot = (scratch / "no-sysroot").string(); app.type = refusal.type; app.compileFlags = refusal.compileFlags; app.linkFlags = refusal.linkFlags; std::array ifaces = {}; std::array impls = { "main" }; app.GetInterfacesAndImplementations(ifaces, impls); std::unordered_map> depResults; std::mutex depMutex; const BuildResult r = Build(app, depResults, depMutex); Check(r.result.contains(refusal.expected), std::format("{} is refused with its cause: {}", refusal.what, r.result)); Check(!fs::exists(project / "bin") && !fs::exists(project / "build"), std::format("refusing {} leaves no bin/ or build/ behind", refusal.what)); } fs::remove_all(scratch); if (failures > 0) { std::println(std::cerr, "{} assertions failed", failures); return 1; } return 0; }