test: a mingw build sees no header from its prefix's include/
MingwSysroot stages a prefix shaped like a Linux system's /usr with a mingw-w64 cross toolchain in it, without needing a real one: glibc's <sys/single_threaded.h> in include/, gcc's per-target directory, and a stand-in mingw-w64 tree whose libstdc++ has an empty std module. A static library for x86_64-w64-mingw32, so that nothing is linked, compiles a C++ and a C source that stop with #error when they see the prefix's include/ or miss mingw-w64's or libstdc++'s headers. Against the library before the fix, the C++ source stopped on <sys/single_threaded.h>. The test also checks that the std module is not cached in the directory older releases built it in against the prefix itself. It needs only clang, so it runs in CI too, where no mingw-w64 is installed. The stand-in std module's source is written through std::format: the build's import scanner reads string literals as code, and the literal "export module std;" made the test itself a unit of a module std. VariantId checks that a prefix gives a mingw configuration a variant of its own, that two prefixes give two, and that other targets ignore it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ab998081e1
commit
b162cc0552
5 changed files with 146 additions and 0 deletions
13
tests/MingwSysroot/fixture/cprobe.c
Normal file
13
tests/MingwSysroot/fixture/cprobe.c
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
// probe.cpp's checks for the C sources, which are compiled by a command of
|
||||
// their own.
|
||||
#if __has_include(<sys/single_threaded.h>)
|
||||
#error the include/ of the prefix is on the C include path: a Windows build sees the headers of the Linux system
|
||||
#endif
|
||||
#if !__has_include(<_mingw.h>)
|
||||
#error the mingw-w64 headers are not on the C include path
|
||||
#endif
|
||||
|
||||
void CProbe(void) {}
|
||||
16
tests/MingwSysroot/fixture/probe.cpp
Normal file
16
tests/MingwSysroot/fixture/probe.cpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
// Built for x86_64-w64-mingw32 against the prefix tests/MingwSysroot stages:
|
||||
// a Linux system's /usr with a mingw-w64 cross toolchain in it.
|
||||
#if __has_include(<sys/single_threaded.h>)
|
||||
#error the include/ of the prefix is on the include path: a Windows build sees the headers of the Linux system
|
||||
#endif
|
||||
#if !__has_include(<_mingw.h>)
|
||||
#error the mingw-w64 headers are not on the include path
|
||||
#endif
|
||||
#if !__has_include(<memory>)
|
||||
#error the libstdc++ headers are not on the include path: clang found no gcc version under lib/gcc of the prefix
|
||||
#endif
|
||||
|
||||
void Probe() {}
|
||||
97
tests/MingwSysroot/main.cpp
Normal file
97
tests/MingwSysroot/main.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// 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 mingw-w64 prefix that is a whole Linux system's /usr must not hand that
|
||||
// system's headers to a Windows build. Inside the Flatpak the prefix is the
|
||||
// host's /usr, and clang got it as the --sysroot. clang's mingw driver takes
|
||||
// a sysroot for a tree that holds mingw-w64 alone and searches its include/
|
||||
// after mingw-w64's own headers, so every header mingw-w64 lacks came from
|
||||
// glibc: libstdc++ found <sys/single_threaded.h> and had shared_ptr read
|
||||
// __libc_single_threaded, which nothing on Windows defines, and every
|
||||
// program that used one failed to link (issue #43). CRAFTER_MINGW_DIR=/usr
|
||||
// did the same outside the Flatpak.
|
||||
//
|
||||
// The prefix staged here has the shape of such a /usr and needs no real
|
||||
// mingw-w64: glibc's <sys/single_threaded.h> in include/, gcc's per-target
|
||||
// directory, which clang derives libstdc++'s header directory from, and a
|
||||
// stand-in mingw-w64 tree whose libstdc++ has a std module with nothing in
|
||||
// it. The fixture is a static library, so nothing is linked, and its C++ and
|
||||
// C sources stop the compile when they see the prefix's include/ or miss
|
||||
// mingw-w64's or libstdc++'s headers.
|
||||
//
|
||||
// The std module such a build compiles is not the one releases before the
|
||||
// fix compiled against the prefix itself, so it has to live in a directory
|
||||
// of its own: the Flatpak's branches share one cache, and two releases
|
||||
// taking turns rebuilding one PCM do it under the PCMs built against it.
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void Write(const fs::path& file, std::string_view text) {
|
||||
fs::create_directories(file.parent_path());
|
||||
std::ofstream(file, std::ios::binary | std::ios::trunc) << text;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
const fs::path scratch = fs::temp_directory_path() / "crafter-build-mingw-sysroot";
|
||||
std::error_code ec;
|
||||
fs::remove_all(scratch, ec);
|
||||
fs::create_directories(scratch);
|
||||
const fs::path project = scratch / "project";
|
||||
fs::copy(fs::current_path() / "tests" / "MingwSysroot" / "fixture", project, fs::copy_options::recursive);
|
||||
|
||||
const fs::path prefix = scratch / "usr";
|
||||
Write(prefix / "include" / "sys" / "single_threaded.h", "extern char __libc_single_threaded;\n");
|
||||
fs::create_directories(prefix / "lib" / "gcc" / "x86_64-w64-mingw32" / "15.1.0");
|
||||
const fs::path mingw = prefix / "x86_64-w64-mingw32";
|
||||
Write(mingw / "include" / "_mingw.h", "");
|
||||
Write(mingw / "include" / "c++" / "15.1.0" / "memory", "");
|
||||
// Through std::format because the build's import scanner reads string
|
||||
// literals as code: one holding "module std;" made this file a unit of a
|
||||
// module std, and the test did not build.
|
||||
Write(mingw / "include" / "c++" / "15.1.0" / "bits" / "std.cc", std::format("export module {};\n", "std"));
|
||||
Write(mingw / "lib" / "libstdc++.modules.json", R"({"version": 1, "modules": [{"logical-name": "std", "source-path": "../include/c++/15.1.0/bits/std.cc", "is-std-library": true}]})");
|
||||
// Before anything resolves the toolchain, which reads both once. The
|
||||
// std module cache goes to the scratch directory too.
|
||||
setenv("CRAFTER_MINGW_DIR", prefix.c_str(), 1);
|
||||
setenv("XDG_CACHE_HOME", (scratch / "cache").c_str(), 1);
|
||||
|
||||
Configuration lib;
|
||||
lib.path = project;
|
||||
lib.name = "probe";
|
||||
lib.outputName = "probe";
|
||||
lib.target = "x86_64-w64-mingw32";
|
||||
lib.type = ConfigurationType::LibraryStatic;
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "probe" };
|
||||
lib.GetInterfacesAndImplementations(ifaces, impls);
|
||||
lib.cFiles = { project / "cprobe" };
|
||||
std::unordered_map<fs::path, std::shared_future<BuildResult>> depResults;
|
||||
std::mutex depMutex;
|
||||
const BuildResult built = Build(lib, depResults, depMutex);
|
||||
Check(built.result.empty(), std::format("a Windows build sees mingw-w64's and libstdc++'s headers and not the prefix's own: {}", built.result));
|
||||
const std::string unkeyed = std::format("{}-{}-{}", lib.target, lib.march, GetToolchain().Key());
|
||||
Check(StdPcmDir(lib).filename() != unkeyed, std::format("the std module of a mingw build with a prefix is not cached where older releases built it against the prefix itself: {}", StdPcmDir(lib).string()));
|
||||
|
||||
fs::remove_all(scratch, ec);
|
||||
if (failures > 0) {
|
||||
std::println(std::cerr, "{} assertions failed", failures);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in a new issue