@@ -464,57 +464,72 @@ int Compilation::performJobsImpl() {
464464 const CommandOutput &Output = FinishedCmd->getOutput ();
465465 StringRef DependenciesFile =
466466 Output.getAdditionalOutputForType (types::TY_SwiftDeps);
467- if (!DependenciesFile.empty () &&
468- (ReturnCode == EXIT_SUCCESS || ReturnCode == EXIT_FAILURE)) {
469- bool wasCascading = DepGraph.isMarked (FinishedCmd);
470467
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- DepGraph.markTransitive (Dependents, FinishedCmd);
487- break ;
488- }
468+ if (DependenciesFile.empty ()) {
469+ // If this job doesn't track dependencies, it must always be run.
470+ // Note: In theory CheckDependencies makes sense as well (for a leaf
471+ // node in the dependency graph), and maybe even NewlyAdded (for very
472+ // coarse dependencies that always affect downstream nodes), but we're
473+ // not using either of those right now, and this logic should probably
474+ // be revisited when we are.
475+ assert (FinishedCmd->getCondition () == Job::Condition::Always);
489476 } else {
490- // If there's a crash, assume the worst.
491- switch (FinishedCmd->getCondition ()) {
492- case Job::Condition::NewlyAdded:
493- // The job won't be treated as newly added next time. Conservatively
494- // mark it as affecting other jobs, because some of them may have
495- // completed already.
496- DepGraph.markTransitive (Dependents, FinishedCmd);
497- break ;
498- case Job::Condition::Always:
499- // This applies to non-incremental tasks as well, but any incremental
500- // task that shows up here has already been marked.
501- break ;
502- case Job::Condition::RunWithoutCascading:
503- // If this file changed, it might have been a non-cascading change and
504- // it might not. Unfortunately, the interface hash has been updated or
505- // compromised, so we don't actually know anymore; we have to
506- // conservatively assume the changes could affect other files.
507- DepGraph.markTransitive (Dependents, FinishedCmd);
508- break ;
509- case Job::Condition::CheckDependencies:
510- // If the only reason we're running this is because something else
511- // changed, then we can trust the dependency graph as to whether it's
512- // a cascading or non-cascading change. That is, if whatever /caused/
513- // the error isn't supposed to affect other files, and whatever
514- // /fixes/ the error isn't supposed to affect other files, then
515- // there's no need to recompile any other inputs. If either of those
516- // are false, we /do/ need to recompile other inputs.
517- break ;
477+ // If we have a dependency file /and/ the frontend task exited normally,
478+ // we can be discerning about what downstream files to rebuild.
479+ if (ReturnCode == EXIT_SUCCESS || ReturnCode == EXIT_FAILURE) {
480+ bool wasCascading = DepGraph.isMarked (FinishedCmd);
481+
482+ switch (DepGraph.loadFromPath (FinishedCmd, DependenciesFile)) {
483+ case DependencyGraphImpl::LoadResult::HadError:
484+ if (ReturnCode == EXIT_SUCCESS) {
485+ disableIncrementalBuild ();
486+ for (const Job *Cmd : DeferredCommands)
487+ scheduleCommandIfNecessaryAndPossible (Cmd);
488+ DeferredCommands.clear ();
489+ Dependents.clear ();
490+ } // else, let the next build handle it.
491+ break ;
492+ case DependencyGraphImpl::LoadResult::UpToDate:
493+ if (!wasCascading)
494+ break ;
495+ SWIFT_FALLTHROUGH;
496+ case DependencyGraphImpl::LoadResult::AffectsDownstream:
497+ DepGraph.markTransitive (Dependents, FinishedCmd);
498+ break ;
499+ }
500+ } else {
501+ // If there's an abnormal exit (a crash), assume the worst.
502+ switch (FinishedCmd->getCondition ()) {
503+ case Job::Condition::NewlyAdded:
504+ // The job won't be treated as newly added next time. Conservatively
505+ // mark it as affecting other jobs, because some of them may have
506+ // completed already.
507+ DepGraph.markTransitive (Dependents, FinishedCmd);
508+ break ;
509+ case Job::Condition::Always:
510+ // Any incremental task that shows up here has already been marked;
511+ // we didn't need to wait for it to finish to start downstream
512+ // tasks.
513+ assert (DepGraph.isMarked (FinishedCmd));
514+ break ;
515+ case Job::Condition::RunWithoutCascading:
516+ // If this file changed, it might have been a non-cascading change
517+ // and it might not. Unfortunately, the interface hash has been
518+ // updated or compromised, so we don't actually know anymore; we
519+ // have to conservatively assume the changes could affect other
520+ // files.
521+ DepGraph.markTransitive (Dependents, FinishedCmd);
522+ break ;
523+ case Job::Condition::CheckDependencies:
524+ // If the only reason we're running this is because something else
525+ // changed, then we can trust the dependency graph as to whether
526+ // it's a cascading or non-cascading change. That is, if whatever
527+ // /caused/ the error isn't supposed to affect other files, and
528+ // whatever /fixes/ the error isn't supposed to affect other files,
529+ // then there's no need to recompile any other inputs. If either of
530+ // those are false, we /do/ need to recompile other inputs.
531+ break ;
532+ }
518533 }
519534 }
520535 }
0 commit comments