1515#include " swift/AST/DiagnosticEngine.h"
1616#include " swift/AST/DiagnosticsDriver.h"
1717#include " swift/AST/FineGrainedDependencies.h"
18+ #include " swift/AST/FineGrainedDependencyFormat.h"
1819#include " swift/Basic/OutputFileMap.h"
1920#include " swift/Basic/Program.h"
2021#include " swift/Basic/STLExtras.h"
@@ -1185,6 +1186,30 @@ namespace driver {
11851186 return ExternallyDependentJobs;
11861187 }
11871188
1189+ using ChangeSet = fine_grained_dependencies::ModuleDepGraph::Changes::value_type;
1190+ static void
1191+ pruneChangeSetFromExternalDependency (ChangeSet &changes) {
1192+ // The changeset includes detritus from the graph that gets consed up
1193+ // in \c writePriorDependencyGraph. We need to ignore the fake
1194+ // source file provides nodes and the fake incremental external
1195+ // dependencies linked to them.
1196+ swift::erase_if (
1197+ changes, [&](fine_grained_dependencies::ModuleDepGraphNode *node) {
1198+ if (node->getKey ().getKind () ==
1199+ fine_grained_dependencies::NodeKind::sourceFileProvide ||
1200+ node->getKey ().getKind () ==
1201+ fine_grained_dependencies::NodeKind::
1202+ incrementalExternalDepend) {
1203+ return true ;
1204+ }
1205+ if (node->getKey ().getAspect () ==
1206+ fine_grained_dependencies::DeclAspect::implementation) {
1207+ return true ;
1208+ }
1209+ return !node->getIsProvides ();
1210+ });
1211+ }
1212+
11881213 SmallVector<const Job *, 16 >
11891214 collectIncrementalExternallyDependentJobsFromDependencyGraph (
11901215 const bool forRanges) {
@@ -1196,6 +1221,17 @@ namespace driver {
11961221 }
11971222 };
11981223
1224+ // Load our priors, which are always adjacent to the build record. We
1225+ // don't care if this load succeeds or not. If it fails, and we succeed at
1226+ // integrating one of the external files below, the changeset will be the
1227+ // entire module!
1228+ const auto *externalPriorJob = Comp.addExternalJob (
1229+ std::make_unique<Job>(Comp.getDerivedOutputFileMap (),
1230+ Comp.getExternalSwiftDepsFilePath ()));
1231+ getFineGrainedDepGraph (forRanges).loadFromPath (
1232+ externalPriorJob, Comp.getExternalSwiftDepsFilePath (),
1233+ Comp.getDiags ());
1234+
11991235 for (auto external : getFineGrainedDepGraph (forRanges)
12001236 .getIncrementalExternalDependencies ()) {
12011237 llvm::sys::fs::file_status depStatus;
@@ -1231,20 +1267,23 @@ namespace driver {
12311267 // code's internal invariants.
12321268 const auto *externalJob = Comp.addExternalJob (
12331269 std::make_unique<Job>(Comp.getDerivedOutputFileMap (), external));
1234- auto subChanges =
1270+ auto maybeChanges =
12351271 getFineGrainedDepGraph (forRanges).loadFromSwiftModuleBuffer (
12361272 externalJob, *buffer.get (), Comp.getDiags ());
12371273
12381274 // If the incremental dependency graph failed to load, fall back to
12391275 // treating this as plain external job.
1240- if (!subChanges .hasValue ()) {
1276+ if (!maybeChanges .hasValue ()) {
12411277 fallbackToExternalBehavior (external);
12421278 continue ;
12431279 }
12441280
1245- for (auto *CMD :
1246- getFineGrainedDepGraph (forRanges)
1247- .findJobsToRecompileWhenNodesChange (subChanges.getValue ())) {
1281+ // Prune away the detritus from the build record.
1282+ auto &changes = maybeChanges.getValue ();
1283+ pruneChangeSetFromExternalDependency (changes);
1284+
1285+ for (auto *CMD : getFineGrainedDepGraph (forRanges)
1286+ .findJobsToRecompileWhenNodesChange (changes)) {
12481287 if (CMD == externalJob) {
12491288 continue ;
12501289 }
@@ -1861,6 +1900,76 @@ static void writeCompilationRecord(StringRef path, StringRef argsHash,
18611900 }
18621901}
18631902
1903+ using SourceFileDepGraph = swift::fine_grained_dependencies::SourceFileDepGraph;
1904+
1905+ // / Render out the unified module dependency graph to the given \p path, which
1906+ // / is expected to be a path relative to the build record.
1907+ static void withPriorDependencyGraph (StringRef path,
1908+ const Compilation::Result &result,
1909+ llvm::function_ref<void (SourceFileDepGraph &&)> cont) {
1910+ // Building a source file dependency graph from the module dependency graph
1911+ // is a strange task on its face because a source file dependency graph is
1912+ // usually built for exactly one file. However, the driver is going to use
1913+ // some encoding tricks to get the dependencies for each incremental external
1914+ // dependency into one big file. Note that these tricks
1915+ // are undone in \c pruneChangeSetFromExternalDependency, so if you modify
1916+ // this you need to go fix that algorithm up as well. This is a diagrammatic
1917+ // view of the structure of the dependencies this function builds:
1918+ //
1919+ // SourceFile => interface <BUILD_RECORD>.external
1920+ // | - Incremetal External Dependency => interface <MODULE_1>.swiftmodule
1921+ // | | - <dependency> ...
1922+ // | | - <dependency> ...
1923+ // | | - <dependency> ...
1924+ // | - Incremetal External Dependency => interface <MODULE_2>.swiftmodule
1925+ // | | - <dependency> ...
1926+ // | | - <dependency> ...
1927+ // | - Incremetal External Dependency => interface <MODULE_3>.swiftmodule
1928+ // | - ...
1929+ //
1930+ // Where each <dependency> node has an arc back to its parent swiftmodule.
1931+ // That swiftmodule, in turn, takes the form of as an incremental external
1932+ // dependency. This formulation allows us to easily discern the original
1933+ // swiftmodule that a <dependency> came from just by examining that arc. This
1934+ // is used in integrate to "move" the <dependency> from the build record to
1935+ // the swiftmodule by swapping the key it uses.
1936+ using namespace swift ::fine_grained_dependencies;
1937+ SourceFileDepGraph g;
1938+ const auto &resultModuleGraph = result.depGraph ;
1939+ // Create the key for the entire external build record.
1940+ auto fileKey =
1941+ DependencyKey::createKeyForWholeSourceFile (DeclAspect::interface, path);
1942+ auto fileNodePair = g.findExistingNodePairOrCreateAndAddIfNew (fileKey, None);
1943+ for (StringRef incrExternalDep :
1944+ resultModuleGraph.getIncrementalExternalDependencies ()) {
1945+ // Now make a node for each incremental external dependency.
1946+ auto interfaceKey =
1947+ DependencyKey (NodeKind::incrementalExternalDepend,
1948+ DeclAspect::interface, " " , incrExternalDep.str ());
1949+ auto ifaceNode = g.findExistingNodeOrCreateIfNew (interfaceKey, None,
1950+ false /* = !isProvides */ );
1951+ resultModuleGraph.forEachNodeInJob (incrExternalDep, [&](const auto *node) {
1952+ // Reject
1953+ // 1) Implementation nodes: We don't care about the interface nodes
1954+ // for cross-module dependencies because the client cannot observe it
1955+ // by definition.
1956+ // 2) Source file nodes: we're about to define our own.
1957+ if (!node->getKey ().isInterface () ||
1958+ node->getKey ().getKind () == NodeKind::sourceFileProvide) {
1959+ return ;
1960+ }
1961+ assert (node->getIsProvides () &&
1962+ " Found a node in module depdendencies that is not a provides!" );
1963+ auto *newNode = new SourceFileDepGraphNode (
1964+ node->getKey (), node->getFingerprint (), /* isProvides*/ true );
1965+ g.addNode (newNode);
1966+ g.addArc (ifaceNode, newNode);
1967+ });
1968+ g.addArc (fileNodePair.getInterface (), ifaceNode);
1969+ }
1970+ return cont (std::move (g));
1971+ }
1972+
18641973static void writeInputJobsToFilelist (llvm::raw_fd_ostream &out, const Job *job,
18651974 const file_types::ID infoType) {
18661975 // FIXME: Duplicated from ToolChains.cpp.
@@ -1960,6 +2069,15 @@ Compilation::performJobsImpl(std::unique_ptr<TaskQueue> &&TQ) {
19602069 auto result = std::move (State).takeResult ();
19612070 writeCompilationRecord (CompilationRecordPath, ArgsHash, BuildStartTime,
19622071 InputInfo);
2072+ if (EnableCrossModuleIncrementalBuild) {
2073+ // Write out our priors adjacent to the build record so we can pick
2074+ // the up in a subsequent build.
2075+ withPriorDependencyGraph (getExternalSwiftDepsFilePath (), result,
2076+ [&](SourceFileDepGraph &&g) {
2077+ writeFineGrainedDependencyGraphToPath (
2078+ Diags, getExternalSwiftDepsFilePath (), g);
2079+ });
2080+ }
19632081 return result;
19642082 } else {
19652083 return std::move (State).takeResult ();
0 commit comments