Skip to content

Commit 848b3eb

Browse files
authored
[Driver] Make sure to rebuild dependents when a dirty file fails. (#3935)
Otherwise we get into a situation like this: 1. Change made to the interface of file A.swift that also causes an error in A.swift. 2. Fixing the error in A.swift does not affect A.swift's interface. 3. File B.swift that depends on A.swift is not rebuilt, since the most recent change to A.swift did not change its interface. To fix this, mark downstream files as needing to be rebuilt even when a compilation job fails with errors. Additionally, attempt to be extra conservative when there's a crash. rdar://problem/25405605
1 parent 7383696 commit 848b3eb

30 files changed

+533
-79
lines changed

lib/Driver/Compilation.cpp

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,70 @@ int Compilation::performJobsImpl() {
456456
llvm::errs() << Output;
457457
}
458458

459+
// In order to handle both old dependencies that have disappeared and new
460+
// dependencies that have arisen, we need to reload the dependency file.
461+
// Do this whether or not the build succeeded.
462+
SmallVector<const Job *, 16> Dependents;
463+
if (getIncrementalBuildEnabled()) {
464+
const CommandOutput &Output = FinishedCmd->getOutput();
465+
StringRef DependenciesFile =
466+
Output.getAdditionalOutputForType(types::TY_SwiftDeps);
467+
if (!DependenciesFile.empty() &&
468+
(ReturnCode == EXIT_SUCCESS || ReturnCode == EXIT_FAILURE)) {
469+
bool wasCascading = DepGraph.isMarked(FinishedCmd);
470+
471+
switch (DepGraph.loadFromPath(FinishedCmd, DependenciesFile)) {
472+
case DependencyGraphImpl::LoadResult::HadError:
473+
if (ReturnCode == EXIT_SUCCESS) {
474+
disableIncrementalBuild();
475+
for (const Job *Cmd : DeferredCommands)
476+
scheduleCommandIfNecessaryAndPossible(Cmd);
477+
DeferredCommands.clear();
478+
Dependents.clear();
479+
} // else, let the next build handle it.
480+
break;
481+
case DependencyGraphImpl::LoadResult::UpToDate:
482+
if (!wasCascading)
483+
break;
484+
SWIFT_FALLTHROUGH;
485+
case DependencyGraphImpl::LoadResult::AffectsDownstream:
486+
llvm::errs() << "DOWNSTREAM " << ReturnCode << "\n";
487+
DepGraph.markTransitive(Dependents, FinishedCmd);
488+
break;
489+
}
490+
} else {
491+
// If there's a crash, assume the worst.
492+
switch (FinishedCmd->getCondition()) {
493+
case Job::Condition::NewlyAdded:
494+
// The job won't be treated as newly added next time. Conservatively
495+
// mark it as affecting other jobs, because some of them may have
496+
// completed already.
497+
DepGraph.markTransitive(Dependents, FinishedCmd);
498+
break;
499+
case Job::Condition::Always:
500+
// This applies to non-incremental tasks as well, but any incremental
501+
// task that shows up here has already been marked.
502+
break;
503+
case Job::Condition::RunWithoutCascading:
504+
// If this file changed, it might have been a non-cascading change and
505+
// it might not. Unfortunately, the interface hash has been updated or
506+
// compromised, so we don't actually know anymore; we have to
507+
// conservatively assume the changes could affect other files.
508+
DepGraph.markTransitive(Dependents, FinishedCmd);
509+
break;
510+
case Job::Condition::CheckDependencies:
511+
// If the only reason we're running this is because something else
512+
// changed, then we can trust the dependency graph as to whether it's
513+
// a cascading or non-cascading change. That is, if whatever /caused/
514+
// the error isn't supposed to affect other files, and whatever
515+
// /fixes/ the error isn't supposed to affect other files, then
516+
// there's no need to recompile any other inputs. If either of those
517+
// are false, we /do/ need to recompile other inputs.
518+
break;
519+
}
520+
}
521+
}
522+
459523
if (ReturnCode != EXIT_SUCCESS) {
460524
// The task failed, so return true without performing any further
461525
// dependency analysis.
@@ -481,39 +545,10 @@ int Compilation::performJobsImpl() {
481545
// might have been blocked.
482546
markFinished(FinishedCmd);
483547

484-
// In order to handle both old dependencies that have disappeared and new
485-
// dependencies that have arisen, we need to reload the dependency file.
486-
if (getIncrementalBuildEnabled()) {
487-
const CommandOutput &Output = FinishedCmd->getOutput();
488-
StringRef DependenciesFile =
489-
Output.getAdditionalOutputForType(types::TY_SwiftDeps);
490-
if (!DependenciesFile.empty()) {
491-
SmallVector<const Job *, 16> Dependents;
492-
bool wasCascading = DepGraph.isMarked(FinishedCmd);
493-
494-
switch (DepGraph.loadFromPath(FinishedCmd, DependenciesFile)) {
495-
case DependencyGraphImpl::LoadResult::HadError:
496-
disableIncrementalBuild();
497-
for (const Job *Cmd : DeferredCommands)
498-
scheduleCommandIfNecessaryAndPossible(Cmd);
499-
DeferredCommands.clear();
500-
Dependents.clear();
501-
break;
502-
case DependencyGraphImpl::LoadResult::UpToDate:
503-
if (!wasCascading)
504-
break;
505-
SWIFT_FALLTHROUGH;
506-
case DependencyGraphImpl::LoadResult::AffectsDownstream:
507-
DepGraph.markTransitive(Dependents, FinishedCmd);
508-
break;
509-
}
510-
511-
for (const Job *Cmd : Dependents) {
512-
DeferredCommands.erase(Cmd);
513-
noteBuilding(Cmd, "because of dependencies discovered later");
514-
scheduleCommandIfNecessaryAndPossible(Cmd);
515-
}
516-
}
548+
for (const Job *Cmd : Dependents) {
549+
DeferredCommands.erase(Cmd);
550+
noteBuilding(Cmd, "because of dependencies discovered later");
551+
scheduleCommandIfNecessaryAndPossible(Cmd);
517552
}
518553

519554
return TaskFinishedResponse::ContinueExecution;

lib/Driver/Driver.cpp

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,36 +1604,40 @@ handleCompileJobCondition(Job *J, CompileJobAction::InputInfo inputInfo,
16041604
return;
16051605
}
16061606

1607-
if (!alwaysRebuildDependents) {
1608-
// Default all non-newly added files to being rebuilt without cascading.
1609-
J->setCondition(Job::Condition::RunWithoutCascading);
1610-
}
1611-
1607+
bool hasValidModTime = false;
16121608
llvm::sys::fs::file_status inputStatus;
1613-
if (llvm::sys::fs::status(input, inputStatus))
1614-
return;
1615-
1616-
J->setInputModTime(inputStatus.getLastModificationTime());
1617-
if (J->getInputModTime() != inputInfo.previousModTime)
1618-
return;
1609+
if (!llvm::sys::fs::status(input, inputStatus)) {
1610+
J->setInputModTime(inputStatus.getLastModificationTime());
1611+
hasValidModTime = true;
1612+
}
16191613

16201614
Job::Condition condition;
1621-
switch (inputInfo.status) {
1622-
case CompileJobAction::InputInfo::UpToDate:
1623-
if (!llvm::sys::fs::exists(J->getOutput().getPrimaryOutputFilename()))
1615+
if (!hasValidModTime || J->getInputModTime() != inputInfo.previousModTime) {
1616+
if (alwaysRebuildDependents ||
1617+
inputInfo.status == CompileJobAction::InputInfo::NeedsCascadingBuild) {
1618+
condition = Job::Condition::Always;
1619+
} else {
16241620
condition = Job::Condition::RunWithoutCascading;
1625-
else
1626-
condition = Job::Condition::CheckDependencies;
1627-
break;
1628-
case CompileJobAction::InputInfo::NeedsCascadingBuild:
1629-
condition = Job::Condition::Always;
1630-
break;
1631-
case CompileJobAction::InputInfo::NeedsNonCascadingBuild:
1632-
condition = Job::Condition::RunWithoutCascading;
1633-
break;
1634-
case CompileJobAction::InputInfo::NewlyAdded:
1635-
llvm_unreachable("handled above");
1621+
}
1622+
} else {
1623+
switch (inputInfo.status) {
1624+
case CompileJobAction::InputInfo::UpToDate:
1625+
if (!llvm::sys::fs::exists(J->getOutput().getPrimaryOutputFilename()))
1626+
condition = Job::Condition::RunWithoutCascading;
1627+
else
1628+
condition = Job::Condition::CheckDependencies;
1629+
break;
1630+
case CompileJobAction::InputInfo::NeedsCascadingBuild:
1631+
condition = Job::Condition::Always;
1632+
break;
1633+
case CompileJobAction::InputInfo::NeedsNonCascadingBuild:
1634+
condition = Job::Condition::RunWithoutCascading;
1635+
break;
1636+
case CompileJobAction::InputInfo::NewlyAdded:
1637+
llvm_unreachable("handled above");
1638+
}
16361639
}
1640+
16371641
J->setCondition(condition);
16381642
}
16391643

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Dependencies after compilation:
2+
provides-top-level: [a]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Dependencies after compilation:
2+
depends-top-level: [a]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Dependencies after compilation:
2+
depends-top-level: [!private a]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"./main.swift": {
3+
"object": "./main.o",
4+
"swift-dependencies": "./main.swiftdeps"
5+
},
6+
"./crash.swift": {
7+
"object": "./crash.o",
8+
"swift-dependencies": "./crash.swiftdeps"
9+
},
10+
"./other.swift": {
11+
"object": "./other.o",
12+
"swift-dependencies": "./other.swiftdeps"
13+
},
14+
"": {
15+
"swift-dependencies": "./main~buildrecord.swiftdeps"
16+
}
17+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Dependencies after compilation:
2+
provides-top-level: [bad]
3+
interface-hash: "after"
4+
garbage: ""
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Dependencies before compilation:
2+
provides-top-level: [bad]
3+
interface-hash: "before"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Dependencies after compilation:
2+
depends-top-level: [bad]
3+
interface-hash: "after"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Dependencies before compilation:
2+
depends-top-level: [bad]
3+
interface-hash: "before"

0 commit comments

Comments
 (0)