fix: give the mingw target a sysroot that holds mingw-w64 alone #44
3 changed files with 94 additions and 0 deletions
test: a std module rebuilt in place recompiles the units importing it
StdPcmRebuild builds a project whose main.cpp imports std, then dates the source before the object and the object before std.pcm, as a std module rebuilt after the object leaves them, so that nothing else makes the object stale. The next build has to compile main.cpp again, and the idle build before it must compile nothing. Without -fmodule-file-deps the object was only relinked. The test redirects the cache, since redating a std PCM the real cache shares would rebuild its other users. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
commit
8e759a8410
|
|
@ -247,6 +247,7 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
||||||
cfg.AddTest("IncrementalInterfaceChange").Dependencies({ CrafterBuildLib.get() });
|
cfg.AddTest("IncrementalInterfaceChange").Dependencies({ CrafterBuildLib.get() });
|
||||||
cfg.AddTest("TransitiveInterfaceChange").Dependencies({ CrafterBuildLib.get() });
|
cfg.AddTest("TransitiveInterfaceChange").Dependencies({ CrafterBuildLib.get() });
|
||||||
cfg.AddTest("IncrementalHeaderChange").Dependencies({ CrafterBuildLib.get() });
|
cfg.AddTest("IncrementalHeaderChange").Dependencies({ CrafterBuildLib.get() });
|
||||||
|
cfg.AddTest("StdPcmRebuild").Dependencies({ CrafterBuildLib.get() });
|
||||||
cfg.AddTest("CompilerWarning").Dependencies({ CrafterBuildLib.get() });
|
cfg.AddTest("CompilerWarning").Dependencies({ CrafterBuildLib.get() });
|
||||||
cfg.AddTest("CleanProject").Dependencies({ CrafterBuildLib.get() });
|
cfg.AddTest("CleanProject").Dependencies({ CrafterBuildLib.get() });
|
||||||
cfg.AddTest("ShaderCompile").Dependencies({ CrafterBuildLib.get() });
|
cfg.AddTest("ShaderCompile").Dependencies({ CrafterBuildLib.get() });
|
||||||
|
|
|
||||||
8
tests/StdPcmRebuild/fixture/main.cpp
Normal file
8
tests/StdPcmRebuild/fixture/main.cpp
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::println("rebuilt");
|
||||||
|
}
|
||||||
85
tests/StdPcmRebuild/main.cpp
Normal file
85
tests/StdPcmRebuild/main.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
// 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 std module rebuilt in place has to recompile the units that import it.
|
||||||
|
// The std PCM is rebuilt in its directory whenever its source changes under
|
||||||
|
// the same compiler and flags: a libc++ update, or mingw-w64's under the
|
||||||
|
// same prefix. A .cpp unit's depfile listed only its source and headers,
|
||||||
|
// because the driver records imported BMIs for --precompile alone, so such
|
||||||
|
// a unit kept its object and the next build only relinked it.
|
||||||
|
//
|
||||||
|
// The project is one main.cpp that imports std. Its object is dated before
|
||||||
|
// std.pcm and after its source, as a std module rebuilt after the object
|
||||||
|
// leaves them, and the next build has to compile it again. XDG_CACHE_HOME keeps the std PCM out of the real
|
||||||
|
// cache, whose other users the change of date would rebuild.
|
||||||
|
|
||||||
|
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 BuildOnce(Configuration& cfg) {
|
||||||
|
std::unordered_map<fs::path, std::shared_future<BuildResult>> depResults;
|
||||||
|
std::mutex depMutex;
|
||||||
|
return Build(cfg, depResults, depMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const fs::path scratch = fs::temp_directory_path() / "crafter-build-std-pcm-rebuild";
|
||||||
|
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" / "StdPcmRebuild" / "fixture", project, fs::copy_options::recursive);
|
||||||
|
setenv("XDG_CACHE_HOME", (scratch / "cache").c_str(), 1);
|
||||||
|
|
||||||
|
Configuration cfg;
|
||||||
|
cfg.path = project;
|
||||||
|
cfg.name = "rebuilt";
|
||||||
|
cfg.outputName = "rebuilt";
|
||||||
|
cfg.type = ConfigurationType::Executable;
|
||||||
|
std::array<fs::path, 0> ifaces = {};
|
||||||
|
std::array<fs::path, 1> impls = { "main" };
|
||||||
|
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||||
|
|
||||||
|
BuildResult built = BuildOnce(cfg);
|
||||||
|
Check(built.result.empty(), std::format("the project builds: {}", built.result));
|
||||||
|
const fs::path object = cfg.BuildDir() / "main_impl.o";
|
||||||
|
const fs::path stdPcm = StdPcmDir(cfg) / "std.pcm";
|
||||||
|
Check(fs::exists(object) && fs::exists(stdPcm), std::format("{} and {} exist", object.string(), stdPcm.string()));
|
||||||
|
if (!fs::exists(object) || !fs::exists(stdPcm)) return 1;
|
||||||
|
|
||||||
|
const fs::file_time_type first = fs::last_write_time(object);
|
||||||
|
built = BuildOnce(cfg);
|
||||||
|
Check(built.result.empty(), std::format("an idle build succeeds: {}", built.result));
|
||||||
|
Check(fs::last_write_time(object) == first, "and compiles nothing again");
|
||||||
|
|
||||||
|
// The source before the object and the object before std.pcm, so that
|
||||||
|
// nothing but the std module makes the object stale.
|
||||||
|
const fs::file_time_type older = fs::last_write_time(stdPcm) - std::chrono::seconds(10);
|
||||||
|
fs::last_write_time(project / "main.cpp", older - std::chrono::seconds(10));
|
||||||
|
fs::last_write_time(object, older);
|
||||||
|
built = BuildOnce(cfg);
|
||||||
|
Check(built.result.empty(), std::format("the build after the std module succeeds: {}", built.result));
|
||||||
|
Check(fs::last_write_time(object) > older, "a unit importing a std module newer than its object is compiled again");
|
||||||
|
|
||||||
|
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