|
36 | 36 | #include "swift/Strings.h"
|
37 | 37 | #include "clang/Basic/Module.h"
|
38 | 38 | #include "llvm/ADT/SetVector.h"
|
| 39 | +#include "llvm/ADT/SetOperations.h" |
39 | 40 | #include "llvm/ADT/StringMap.h"
|
40 | 41 | #include "llvm/ADT/StringRef.h"
|
41 | 42 | #include "llvm/ADT/StringSet.h"
|
|
47 | 48 | #include <set>
|
48 | 49 | #include <string>
|
49 | 50 | #include <sstream>
|
| 51 | +#include <algorithm> |
50 | 52 |
|
51 | 53 | using namespace swift;
|
52 | 54 | using namespace swift::dependencies;
|
@@ -157,6 +159,152 @@ static void findAllImportedClangModules(ASTContext &ctx, StringRef moduleName,
|
157 | 159 | }
|
158 | 160 | }
|
159 | 161 |
|
| 162 | +// Get all dependencies's IDs of this module from its cached |
| 163 | +// ModuleDependencyInfo |
| 164 | +static ArrayRef<ModuleDependencyID> |
| 165 | +getDependencies(const ModuleDependencyID &moduleID, |
| 166 | + const ModuleDependenciesCache &cache) { |
| 167 | + const auto &optionalModuleInfo = |
| 168 | + cache.findDependency(moduleID.first, moduleID.second); |
| 169 | + assert(optionalModuleInfo.has_value()); |
| 170 | + return optionalModuleInfo.value()->getModuleDependencies(); |
| 171 | +} |
| 172 | + |
| 173 | +/// Implements a topological sort via recursion and reverse postorder DFS. |
| 174 | +/// Does not bother handling cycles, relying on a DAG guarantee by the client. |
| 175 | +static std::vector<ModuleDependencyID> |
| 176 | +computeTopologicalSortOfExplicitDependencies( |
| 177 | + const ModuleDependencyIDSetVector &allModules, |
| 178 | + const ModuleDependenciesCache &cache) { |
| 179 | + // Must be explicitly-typed to allow recursion |
| 180 | + std::function<void(const ModuleDependencyID &, |
| 181 | + std::stack<ModuleDependencyID> &, |
| 182 | + std::unordered_set<ModuleDependencyID> &, |
| 183 | + std::vector<ModuleDependencyID> &)> |
| 184 | + visit; |
| 185 | + visit = [&visit, &cache](const ModuleDependencyID &moduleID, |
| 186 | + std::stack<ModuleDependencyID> &stack, |
| 187 | + std::unordered_set<ModuleDependencyID> &visited, |
| 188 | + std::vector<ModuleDependencyID> &result) { |
| 189 | + // Mark this node as visited -- we are done if it already was. |
| 190 | + if (!visited.insert(moduleID).second) |
| 191 | + return; |
| 192 | + |
| 193 | + // Otherwise, visit each adjacent node. |
| 194 | + for (const auto &succID : getDependencies(moduleID, cache)) { |
| 195 | + // We don't worry if successor is already in this current stack, |
| 196 | + // since that would mean we have found a cycle, which should not |
| 197 | + // be possible because we checked for cycles earlier. |
| 198 | + stack.push(succID); |
| 199 | + visit(succID, stack, visited, result); |
| 200 | + auto top = stack.top(); |
| 201 | + stack.pop(); |
| 202 | + assert(top == succID); |
| 203 | + } |
| 204 | + |
| 205 | + // Add to the result. |
| 206 | + result.push_back(moduleID); |
| 207 | + }; |
| 208 | + |
| 209 | + std::unordered_set<ModuleDependencyID> visited; |
| 210 | + std::vector<ModuleDependencyID> result; |
| 211 | + std::stack<ModuleDependencyID> stack; |
| 212 | + for (const auto &modID : allModules) { |
| 213 | + assert(stack.empty()); |
| 214 | + stack.push(modID); |
| 215 | + visit(modID, stack, visited, result); |
| 216 | + auto top = stack.top(); |
| 217 | + stack.pop(); |
| 218 | + assert(top == modID); |
| 219 | + } |
| 220 | + |
| 221 | + std::reverse(result.begin(), result.end()); |
| 222 | + return result; |
| 223 | +} |
| 224 | + |
| 225 | +/// For each module in the graph, compute a set of all its dependencies |
| 226 | +/// direct *and* transitive. |
| 227 | +static std::unordered_map<ModuleDependencyID, |
| 228 | + std::set<ModuleDependencyID>> |
| 229 | +computeTransitiveClosureOfExplicitDependencies( |
| 230 | + const std::vector<ModuleDependencyID> &topologicallySortedModuleList, |
| 231 | + const ModuleDependenciesCache &cache) { |
| 232 | + std::unordered_map<ModuleDependencyID, std::set<ModuleDependencyID>> |
| 233 | + result; |
| 234 | + for (const auto &modID : topologicallySortedModuleList) |
| 235 | + result[modID] = {modID}; |
| 236 | + |
| 237 | + // Traverse the set of modules in reverse topological order, assimilating |
| 238 | + // transitive closures |
| 239 | + for (auto it = topologicallySortedModuleList.rbegin(), |
| 240 | + end = topologicallySortedModuleList.rend(); |
| 241 | + it != end; ++it) { |
| 242 | + const auto &modID = *it; |
| 243 | + auto &modReachableSet = result[modID]; |
| 244 | + for (const auto &succID : getDependencies(modID, cache)) { |
| 245 | + const auto &succReachableSet = result[succID]; |
| 246 | + llvm::set_union(modReachableSet, succReachableSet); |
| 247 | + } |
| 248 | + } |
| 249 | + return result; |
| 250 | +} |
| 251 | + |
| 252 | +static void |
| 253 | +resolveExplicitModuleInputs(ModuleDependencyID moduleID, |
| 254 | + const ModuleDependencyInfo &resolvingDepInfo, |
| 255 | + const std::set<ModuleDependencyID> &dependencies, |
| 256 | + ModuleDependenciesCache &cache) { |
| 257 | + auto resolvingInterfaceDepDetails = |
| 258 | + resolvingDepInfo.getAsSwiftInterfaceModule(); |
| 259 | + assert(resolvingInterfaceDepDetails && |
| 260 | + "Expected Swift Interface dependency."); |
| 261 | + |
| 262 | + auto commandLine = resolvingInterfaceDepDetails->buildCommandLine; |
| 263 | + for (const auto &depModuleID : dependencies) { |
| 264 | + const auto optionalDepInfo = |
| 265 | + cache.findDependency(depModuleID.first, depModuleID.second); |
| 266 | + assert(optionalDepInfo.has_value()); |
| 267 | + const auto depInfo = optionalDepInfo.value(); |
| 268 | + switch (depModuleID.second) { |
| 269 | + case swift::ModuleDependencyKind::SwiftInterface: { |
| 270 | + auto interfaceDepDetails = depInfo->getAsSwiftInterfaceModule(); |
| 271 | + assert(interfaceDepDetails && "Expected Swift Interface dependency."); |
| 272 | + commandLine.push_back("-swift-module-file=" + depModuleID.first + "=" + |
| 273 | + interfaceDepDetails->moduleOutputPath); |
| 274 | + } break; |
| 275 | + case swift::ModuleDependencyKind::SwiftBinary: { |
| 276 | + auto binaryDepDetails = depInfo->getAsSwiftBinaryModule(); |
| 277 | + assert(binaryDepDetails && "Expected Swift Binary Module dependency."); |
| 278 | + commandLine.push_back("-swift-module-file=" + depModuleID.first + "=" + |
| 279 | + binaryDepDetails->compiledModulePath); |
| 280 | + } break; |
| 281 | + case swift::ModuleDependencyKind::SwiftPlaceholder: { |
| 282 | + auto placeholderDetails = depInfo->getAsPlaceholderDependencyModule(); |
| 283 | + assert(placeholderDetails && "Expected Swift Placeholder dependency."); |
| 284 | + commandLine.push_back("-swift-module-file=" + depModuleID.first + "=" + |
| 285 | + placeholderDetails->compiledModulePath); |
| 286 | + } break; |
| 287 | + case swift::ModuleDependencyKind::Clang: { |
| 288 | + auto clangDepDetails = depInfo->getAsClangModule(); |
| 289 | + assert(binaryDepDetails && "Expected Clang Module dependency."); |
| 290 | + commandLine.push_back("-Xcc"); |
| 291 | + commandLine.push_back("-fmodule-file=" + depModuleID.first + "=" + |
| 292 | + clangDepDetails->pcmOutputPath); |
| 293 | + commandLine.push_back("-Xcc"); |
| 294 | + commandLine.push_back("-fmodule-map-file=" + |
| 295 | + clangDepDetails->moduleMapFile); |
| 296 | + } break; |
| 297 | + default: |
| 298 | + llvm_unreachable("Unhandled dependency kind."); |
| 299 | + } |
| 300 | + } |
| 301 | + |
| 302 | + // Update the dependency in the cache with the modified command-line. |
| 303 | + auto dependencyInfoCopy = resolvingDepInfo; |
| 304 | + dependencyInfoCopy.updateCommandLine(commandLine); |
| 305 | + cache.updateDependency(moduleID, dependencyInfoCopy); |
| 306 | +} |
| 307 | + |
160 | 308 | /// Resolve the direct dependencies of the given module.
|
161 | 309 | static ArrayRef<ModuleDependencyID>
|
162 | 310 | resolveDirectDependencies(CompilerInstance &instance, ModuleDependencyID module,
|
@@ -1475,8 +1623,29 @@ swift::dependencies::performModuleScan(CompilerInstance &instance,
|
1475 | 1623 | if (diagnoseCycle(instance, cache, mainModuleID, ASTDelegate))
|
1476 | 1624 | return std::make_error_code(std::errc::not_supported);
|
1477 | 1625 |
|
| 1626 | + // Resolve Swift dependency command-line arguments |
| 1627 | + // Must happen after cycle detection, because it relies on |
| 1628 | + // the DAG property of the dependency graph. |
| 1629 | + auto topoSortedModuleList = |
| 1630 | + computeTopologicalSortOfExplicitDependencies(allModules, cache); |
| 1631 | + auto moduleTransitiveClosures = |
| 1632 | + computeTransitiveClosureOfExplicitDependencies(topoSortedModuleList, |
| 1633 | + cache); |
| 1634 | + for (const auto &dependencyClosure : moduleTransitiveClosures) { |
| 1635 | + auto &modID = dependencyClosure.first; |
| 1636 | + // For main module or binary modules, no command-line to resolve. |
| 1637 | + // For Clang modules, their dependencies are resolved by the clang Scanner |
| 1638 | + // itself for us. |
| 1639 | + if (modID.second != ModuleDependencyKind::SwiftInterface) |
| 1640 | + continue; |
| 1641 | + auto optionalDeps = cache.findDependency(modID.first, modID.second); |
| 1642 | + assert(optionalDeps.has_value()); |
| 1643 | + auto deps = optionalDeps.value(); |
| 1644 | + resolveExplicitModuleInputs(modID, *deps, dependencyClosure.second, cache); |
| 1645 | + } |
| 1646 | + |
1478 | 1647 | auto dependencyGraph = generateFullDependencyGraph(
|
1479 |
| - instance, cache, ASTDelegate, allModules.getArrayRef()); |
| 1648 | + instance, cache, ASTDelegate, topoSortedModuleList); |
1480 | 1649 | // Update the dependency tracker.
|
1481 | 1650 | if (auto depTracker = instance.getDependencyTracker()) {
|
1482 | 1651 | for (auto module : allModules) {
|
|
0 commit comments