feat(incremental): rebuild when an #included header changes
The staleness check compared an artifact against its own source and its module imports. Headers were in neither set: the scanner reads `import` lines, and a .cppm or .cpp keeps its mtime when a header it includes is edited — so the build reported nothing to do and left objects compiled against the previous contents. Same silent mixed-layout binary as issue 27, reached through #include instead of import. Ask the compiler what it actually opened. Every C++ and C compile now passes -MD -MF <artifact>.d, and Check reads that depfile back through NewestPrerequisite, comparing every prerequisite's mtime against the artifact. A missing depfile (an object from a crafter-build that wrote none) or a prerequisite that no longer exists reads as "rebuild": neither is evidence of freshness. The parser unescapes make syntax rather than splitting on whitespace, since clang wraps depfiles onto continuation lines and escapes spaces in filenames.
This commit is contained in:
parent
672aabf215
commit
1d0d8e1e28
6 changed files with 111 additions and 8 deletions
|
|
@ -15,7 +15,13 @@ namespace Crafter {
|
|||
checked = true;
|
||||
std::string pcmPath = std::format("{}.pcm", (pcmDir/path.filename()).generic_string());
|
||||
std::string cppmPath = std::format("{}.cppm", path.generic_string());
|
||||
if(fs::exists(pcmPath) && std::max(fs::last_write_time(cppmPath), sourceFloor) < fs::last_write_time(pcmPath)) {
|
||||
// NewestPrerequisite covers the headers this partition #includes —
|
||||
// they are inputs to the BMI just as much as the .cppm is, and the
|
||||
// .cppm's own mtime says nothing about them. It only ever bounds
|
||||
// *this* artifact, so it goes in the comparison below and not into
|
||||
// the `sourceFloor` handed to the recursive Checks: each of those
|
||||
// reads its own depfile.
|
||||
if(fs::exists(pcmPath) && std::max({fs::last_write_time(cppmPath), sourceFloor, NewestPrerequisite(std::format("{}.d", pcmPath))}) < fs::last_write_time(pcmPath)) {
|
||||
fs::file_time_type pcmTime = fs::last_write_time(pcmPath);
|
||||
for(ModulePartition* dependency : partitionDependencies) {
|
||||
if(dependency->Check(pcmDir, sourceFloor)) {
|
||||
|
|
@ -71,7 +77,9 @@ namespace Crafter {
|
|||
return;
|
||||
}
|
||||
|
||||
std::string result = RunCommand(std::format("{} {}.cppm --precompile -o {}.pcm", clang, path.string(), (pcmDir/path.filename()).string()));
|
||||
// -MD records every header the preamble pulled in, next to the BMI as
|
||||
// <name>.pcm.d, so the next Check can see an edit to one of them.
|
||||
std::string result = RunCommand(std::format("{0} {1}.cppm --precompile -MD -MF {2}.pcm.d -o {2}.pcm", clang, path.string(), (pcmDir/path.filename()).string()));
|
||||
|
||||
if (!result.empty()) {
|
||||
bool expected = false;
|
||||
|
|
@ -103,7 +111,9 @@ namespace Crafter {
|
|||
checked = true;
|
||||
std::string pcmPath = std::format("{}.pcm", (pcmDir/path.filename()).generic_string());
|
||||
std::string cppmPath = std::format("{}.cppm", path.generic_string());
|
||||
if(fs::exists(pcmPath) && std::max(fs::last_write_time(cppmPath), sourceFloor) < fs::last_write_time(pcmPath)) {
|
||||
// See ModulePartition::Check — the depfile clang wrote beside the BMI
|
||||
// is the only record of which headers this interface unit included.
|
||||
if(fs::exists(pcmPath) && std::max({fs::last_write_time(cppmPath), sourceFloor, NewestPrerequisite(std::format("{}.d", pcmPath))}) < fs::last_write_time(pcmPath)) {
|
||||
fs::file_time_type pcmTime = fs::last_write_time(pcmPath);
|
||||
// Every partition gets Check()ed even once one is known stale:
|
||||
// Compile() drives partition rebuilds off their own
|
||||
|
|
@ -191,7 +201,9 @@ namespace Crafter {
|
|||
return;
|
||||
}
|
||||
|
||||
std::string result = RunCommand(std::format("{} {}.cppm --precompile -o {}.pcm", clang, path.string(), (pcmDir/path.filename()).string()));
|
||||
// -MD records every header the preamble pulled in, next to the BMI as
|
||||
// <name>.pcm.d, so the next Check can see an edit to one of them.
|
||||
std::string result = RunCommand(std::format("{0} {1}.cppm --precompile -MD -MF {2}.pcm.d -o {2}.pcm", clang, path.string(), (pcmDir/path.filename()).string()));
|
||||
|
||||
if (!result.empty()) {
|
||||
bool expected = false;
|
||||
|
|
|
|||
Loading…
Reference in a new issue