95 lines
4 KiB
C++
95 lines
4 KiB
C++
|
|
// 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;
|
||
|
|
|
||
|
|
// A WASI sysroot without exception support has to stop a wasm build with one
|
||
|
|
// line that says so. wasm builds compile with -fwasm-exceptions and link the
|
||
|
|
// libc++ and libunwind that wasi-sdk 33 and later ship under lib/<triple>/eh;
|
||
|
|
// a sysroot that lacks them (an older wasi-sdk, one built without its
|
||
|
|
// exceptions option) otherwise compiles and then fails the first link with
|
||
|
|
// "unable to find library -lunwind", which does not say why it was asked
|
||
|
|
// for. The check runs before the std module is built, so the first case
|
||
|
|
// needs no working wasm toolchain.
|
||
|
|
//
|
||
|
|
// A scratch sysroot is taken through three states: no libunwind; libunwind
|
||
|
|
// under eh/; libunwind in the triple's own directory, which the driver
|
||
|
|
// searches after eh/. The last two pass the check and fail later on the
|
||
|
|
// sysroot's empty std module source, which is not what is under test. The
|
||
|
|
// cache is redirected into the scratch directory as well: the std PCM cache
|
||
|
|
// is not keyed on the sysroot, and a PCM built from the fake source must not
|
||
|
|
// land where the real wasm tests read theirs.
|
||
|
|
|
||
|
|
extern "C" int setenv(const char* name, const char* value, int overwrite);
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
std::int32_t failures = 0;
|
||
|
|
|
||
|
|
void Check(bool cond, std::string_view msg) {
|
||
|
|
if (!cond) {
|
||
|
|
std::println(std::cerr, "FAIL: {}", msg);
|
||
|
|
++failures;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
BuildResult BuildAgainst(const fs::path& project, const fs::path& sysroot) {
|
||
|
|
Configuration app;
|
||
|
|
app.path = project;
|
||
|
|
app.name = "hello";
|
||
|
|
app.outputName = "hello";
|
||
|
|
app.target = "wasm32-wasip1";
|
||
|
|
app.sysroot = sysroot.string();
|
||
|
|
app.type = ConfigurationType::Executable;
|
||
|
|
std::array<fs::path, 0> ifaces = {};
|
||
|
|
std::array<fs::path, 1> impls = { "main" };
|
||
|
|
app.GetInterfacesAndImplementations(ifaces, impls);
|
||
|
|
std::unordered_map<fs::path, std::shared_future<BuildResult>> depResults;
|
||
|
|
std::mutex depMutex;
|
||
|
|
return Build(app, depResults, depMutex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int main() {
|
||
|
|
// Build() reports a missing compiler before it gets to the sysroot.
|
||
|
|
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-wasi-sysroot-without-exceptions";
|
||
|
|
fs::remove_all(scratch);
|
||
|
|
fs::create_directories(scratch);
|
||
|
|
setenv("XDG_CACHE_HOME", (scratch / "cache").c_str(), 1);
|
||
|
|
const fs::path project = scratch / "project";
|
||
|
|
fs::copy(fs::current_path() / "tests" / "WasiSysrootWithoutExceptions" / "fixture", project, fs::copy_options::recursive);
|
||
|
|
|
||
|
|
const fs::path sysroot = scratch / "sysroot";
|
||
|
|
const fs::path libDir = sysroot / "lib" / "wasm32-wasip1";
|
||
|
|
fs::create_directories(libDir / "eh");
|
||
|
|
fs::create_directories(sysroot / "share" / "libc++" / "v1");
|
||
|
|
std::ofstream(sysroot / "share" / "libc++" / "v1" / "std.cppm");
|
||
|
|
|
||
|
|
constexpr std::string_view missing = "has no C++ exception support";
|
||
|
|
{
|
||
|
|
const BuildResult r = BuildAgainst(project, sysroot);
|
||
|
|
Check(r.result.contains(missing), std::format("without libunwind the build stops on the sysroot: {}", r.result));
|
||
|
|
Check(r.result.contains((libDir / "eh" / "libunwind.a").string()), std::format("the message names the library it looked for: {}", r.result));
|
||
|
|
}
|
||
|
|
|
||
|
|
std::ofstream(libDir / "eh" / "libunwind.a");
|
||
|
|
Check(!BuildAgainst(project, sysroot).result.contains(missing), "libunwind under eh/ passes the check");
|
||
|
|
|
||
|
|
fs::remove(libDir / "eh" / "libunwind.a");
|
||
|
|
std::ofstream(libDir / "libunwind.a");
|
||
|
|
Check(!BuildAgainst(project, sysroot).result.contains(missing), "libunwind in the triple's directory passes the check");
|
||
|
|
|
||
|
|
fs::remove_all(scratch);
|
||
|
|
if (failures > 0) {
|
||
|
|
std::println(std::cerr, "{} assertions failed", failures);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|