85 lines
3.6 KiB
C++
85 lines
3.6 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 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;
|
||
|
|
}
|