//===--- ScanDependencies.cpp -- Scans the dependencies of a module -------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift-c/DependencyScan/DependencyScan.h" #include "swift/Basic/PrettyStackTrace.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsDriver.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/FileSystem.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleDependencies.h" #include "swift/AST/ModuleLoader.h" #include "swift/AST/SourceFile.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Defer.h" #include "swift/Basic/FileTypes.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/STLExtras.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/DependencyScan/DependencyScanImpl.h" #include "swift/DependencyScan/DependencyScanJSON.h" #include "swift/DependencyScan/DependencyScanningTool.h" #include "swift/DependencyScan/ModuleDependencyScanner.h" #include "swift/DependencyScan/ScanDependencies.h" #include "swift/DependencyScan/SerializedModuleDependencyCacheFormat.h" #include "swift/DependencyScan/StringUtils.h" #include "swift/Frontend/CachingUtils.h" #include "swift/Frontend/CompileJobCacheKey.h" #include "swift/Frontend/CompileJobCacheResult.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/FrontendOptions.h" #include "swift/Frontend/ModuleInterfaceLoader.h" #include "swift/Strings.h" #include "clang/Basic/Module.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/CAS/ActionCache.h" #include "llvm/CAS/CASReference.h" #include "llvm/CAS/ObjectStore.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/VirtualOutputBackend.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include using namespace swift; using namespace swift::dependencies; using namespace swift::c_string_utils; using namespace llvm::yaml; namespace { static std::string getScalaNodeText(Node *N) { SmallString<32> Buffer; return cast(N)->getValue(Buffer).str(); } /// Parse an entry like this, where the "platforms" key-value pair is optional: /// { /// "swiftModuleName": "Foo", /// "arguments": "-target 10.15", /// "output": "../Foo.json" /// }, static bool parseBatchInputEntries(ASTContext &Ctx, llvm::StringSaver &saver, Node *Node, std::vector &result) { auto *SN = cast(Node); if (!SN) return true; for (auto It = SN->begin(); It != SN->end(); ++It) { auto *MN = cast(&*It); BatchScanInput entry; std::optional> Platforms; for (auto &Pair : *MN) { auto Key = getScalaNodeText(Pair.getKey()); auto *Value = Pair.getValue(); if (Key == "clangModuleName") { entry.moduleName = saver.save(getScalaNodeText(Value)); entry.isSwift = false; } else if (Key == "swiftModuleName") { entry.moduleName = saver.save(getScalaNodeText(Value)); entry.isSwift = true; } else if (Key == "arguments") { entry.arguments = saver.save(getScalaNodeText(Value)); } else if (Key == "output") { entry.outputPath = saver.save(getScalaNodeText(Value)); } else { // Future proof. continue; } } if (entry.moduleName.empty()) return true; if (entry.outputPath.empty()) return true; result.emplace_back(std::move(entry)); } return false; } static std::optional> parseBatchScanInputFile(ASTContext &ctx, StringRef batchInputPath, llvm::StringSaver &saver) { assert(!batchInputPath.empty()); namespace yaml = llvm::yaml; std::vector result; // Load the input file. llvm::ErrorOr> FileBufOrErr = llvm::MemoryBuffer::getFile(batchInputPath); if (!FileBufOrErr) { ctx.Diags.diagnose(SourceLoc(), diag::batch_scan_input_file_missing, batchInputPath); return std::nullopt; } StringRef Buffer = FileBufOrErr->get()->getBuffer(); // Use a new source manager instead of the one from ASTContext because we // don't want the Json file to be persistent. SourceManager SM; yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, batchInputPath), SM.getLLVMSourceMgr()); for (auto DI = Stream.begin(); DI != Stream.end(); ++DI) { assert(DI != Stream.end() && "Failed to read a document"); yaml::Node *N = DI->getRoot(); assert(N && "Failed to find a root"); if (parseBatchInputEntries(ctx, saver, N, result)) { ctx.Diags.diagnose(SourceLoc(), diag::batch_scan_input_file_corrupted, batchInputPath); return std::nullopt; } } return result; } class ExplicitModuleDependencyResolver { public: ExplicitModuleDependencyResolver( ModuleDependencyID moduleID, ModuleDependenciesCache &cache, CompilerInstance &instance, std::optional tracker) : moduleID(moduleID), cache(cache), instance(instance), resolvingDepInfo(cache.findKnownDependency(moduleID)), tracker(std::move(tracker)) { // Copy commandline. commandline = resolvingDepInfo.getCommandline(); } llvm::Error resolve(const std::set &dependencies, std::optional> bridgingHeaderDeps) { // No need to resolve dependency for placeholder. if (moduleID.Kind == ModuleDependencyKind::SwiftPlaceholder) return llvm::Error::success(); // If the dependency is already finalized, nothing needs to be done. if (resolvingDepInfo.isFinalized()) return llvm::Error::success(); if (auto ID = resolvingDepInfo.getClangIncludeTree()) includeTrees.push_back(*ID); for (const auto &depModuleID : dependencies) { const auto &depInfo = cache.findKnownDependency(depModuleID); switch (depModuleID.Kind) { case swift::ModuleDependencyKind::SwiftInterface: { auto interfaceDepDetails = depInfo.getAsSwiftInterfaceModule(); assert(interfaceDepDetails && "Expected Swift Interface dependency."); if (auto err = handleSwiftInterfaceModuleDependency( depModuleID, *interfaceDepDetails)) return err; } break; case swift::ModuleDependencyKind::SwiftBinary: { auto binaryDepDetails = depInfo.getAsSwiftBinaryModule(); assert(binaryDepDetails && "Expected Swift Binary Module dependency."); if (auto err = handleSwiftBinaryModuleDependency(depModuleID, *binaryDepDetails)) return err; } break; case swift::ModuleDependencyKind::SwiftPlaceholder: { auto placeholderDetails = depInfo.getAsPlaceholderDependencyModule(); assert(placeholderDetails && "Expected Swift Placeholder dependency."); if (auto err = handleSwiftPlaceholderModuleDependency( depModuleID, *placeholderDetails)) return err; } break; case swift::ModuleDependencyKind::Clang: { auto clangDepDetails = depInfo.getAsClangModule(); assert(clangDepDetails && "Expected Clang Module dependency."); if (auto err = handleClangModuleDependency(depModuleID, *clangDepDetails)) return err; } break; case swift::ModuleDependencyKind::SwiftSource: { auto sourceDepDetails = depInfo.getAsSwiftSourceModule(); assert(sourceDepDetails && "Expected Swift Source Module dependency."); if (auto err = handleSwiftSourceModuleDependency(depModuleID, *sourceDepDetails)) return err; } break; default: llvm_unreachable("Unhandled dependency kind."); } } // Update bridging header build command if there is a bridging header // dependency. if (auto E = addBridgingHeaderDeps(resolvingDepInfo)) return E; if (bridgingHeaderDeps) { bridgingHeaderBuildCmd = resolvingDepInfo.getBridgingHeaderCommandline(); for (auto bridgingDep : *bridgingHeaderDeps) { auto &dep = cache.findKnownDependency(bridgingDep); auto *clangDep = dep.getAsClangModule(); assert(clangDep && "wrong module dependency kind"); if (!clangDep->moduleCacheKey.empty()) { bridgingHeaderBuildCmd.push_back("-Xcc"); bridgingHeaderBuildCmd.push_back("-fmodule-file-cache-key"); bridgingHeaderBuildCmd.push_back("-Xcc"); bridgingHeaderBuildCmd.push_back(clangDep->mappedPCMPath); bridgingHeaderBuildCmd.push_back("-Xcc"); bridgingHeaderBuildCmd.push_back(clangDep->moduleCacheKey); } } } pruneUnusedVFSOverlay(); // Update the dependency in the cache with the modified command-line. if (resolvingDepInfo.isSwiftInterfaceModule() || resolvingDepInfo.isClangModule()) { if (cache.getScanService().hasPathMapping()) commandline = remapPathsFromCommandLine(commandline, [&](StringRef path) { return cache.getScanService().remapPath(path); }); } auto dependencyInfoCopy = resolvingDepInfo; if (auto err = finalize(dependencyInfoCopy)) return err; dependencyInfoCopy.setIsFinalized(true); cache.updateDependency(moduleID, dependencyInfoCopy); return llvm::Error::success(); } private: // Finalize the resolving dependency info. llvm::Error finalize(ModuleDependencyInfo &depInfo) { if (resolvingDepInfo.isSwiftPlaceholderModule()) return llvm::Error::success(); // Add macros. for (auto ¯o : macros) depInfo.addMacroDependency( macro.first(), macro.second.LibraryPath, macro.second.ExecutablePath); for (auto ¯o : depInfo.getMacroDependencies()) { std::string arg = macro.second.LibraryPath + "#" + macro.second.ExecutablePath + "#" + macro.first; commandline.push_back("-load-resolved-plugin"); commandline.push_back(arg); } // Update CAS dependencies. if (auto err = collectCASDependencies(depInfo)) return err; if (!bridgingHeaderBuildCmd.empty()) depInfo.updateBridgingHeaderCommandLine( bridgingHeaderBuildCmd); if (!resolvingDepInfo.isSwiftBinaryModule()) { depInfo.updateCommandLine(commandline); if (auto err = updateModuleCacheKey(depInfo)) return err; } return llvm::Error::success(); } llvm::Error handleSwiftInterfaceModuleDependency( ModuleDependencyID depModuleID, const SwiftInterfaceModuleDependenciesStorage &interfaceDepDetails) { if (!resolvingDepInfo.isSwiftSourceModule()) { auto &path = interfaceDepDetails.moduleCacheKey.empty() ? interfaceDepDetails.moduleOutputPath : interfaceDepDetails.moduleCacheKey; commandline.push_back("-swift-module-file=" + depModuleID.ModuleName + "=" + path); } addMacroDependencies(depModuleID, interfaceDepDetails); return llvm::Error::success(); } llvm::Error handleSwiftBinaryModuleDependency( ModuleDependencyID depModuleID, const SwiftBinaryModuleDependencyStorage &binaryDepDetails) { if (!resolvingDepInfo.isSwiftSourceModule()) { auto &path = binaryDepDetails.moduleCacheKey.empty() ? binaryDepDetails.compiledModulePath : binaryDepDetails.moduleCacheKey; commandline.push_back("-swift-module-file=" + depModuleID.ModuleName + "=" + path); // If this binary module was built with a header, the header's module // dependencies must also specify a .modulemap to the compilation, in // order to resolve the header's own header include directives. for (const auto &bridgingHeaderDepID : binaryDepDetails.headerModuleDependencies) { auto optionalBridgingHeaderDepModuleInfo = cache.findKnownDependency(bridgingHeaderDepID); const auto bridgingHeaderDepModuleDetails = optionalBridgingHeaderDepModuleInfo.getAsClangModule(); commandline.push_back("-Xcc"); commandline.push_back( "-fmodule-map-file=" + cache.getScanService().remapPath( bridgingHeaderDepModuleDetails->moduleMapFile)); } } addMacroDependencies(depModuleID, binaryDepDetails); return llvm::Error::success(); } llvm::Error handleSwiftPlaceholderModuleDependency( ModuleDependencyID depModuleID, const SwiftPlaceholderModuleDependencyStorage &placeholderDetails) { if (!resolvingDepInfo.isSwiftSourceModule()) commandline.push_back("-swift-module-file=" + depModuleID.ModuleName + "=" + placeholderDetails.compiledModulePath); return llvm::Error::success(); } llvm::Error handleClangModuleDependency( ModuleDependencyID depModuleID, const ClangModuleDependencyStorage &clangDepDetails) { if (!resolvingDepInfo.isSwiftSourceModule()) { if (!resolvingDepInfo.isClangModule()) { commandline.push_back("-Xcc"); commandline.push_back("-fmodule-file=" + depModuleID.ModuleName + "=" + clangDepDetails.mappedPCMPath); } if (!clangDepDetails.moduleCacheKey.empty()) { commandline.push_back("-Xcc"); commandline.push_back("-fmodule-file-cache-key"); commandline.push_back("-Xcc"); commandline.push_back(clangDepDetails.mappedPCMPath); commandline.push_back("-Xcc"); commandline.push_back(clangDepDetails.moduleCacheKey); } } // Collect CAS deppendencies from clang modules. if (!clangDepDetails.CASFileSystemRootID.empty()) rootIDs.push_back(clangDepDetails.CASFileSystemRootID); if (!clangDepDetails.CASClangIncludeTreeRootID.empty()) includeTrees.push_back(clangDepDetails.CASClangIncludeTreeRootID); collectUsedVFSOverlay(clangDepDetails); return llvm::Error::success(); } llvm::Error handleSwiftSourceModuleDependency( ModuleDependencyID depModuleID, const SwiftSourceModuleDependenciesStorage &sourceDepDetails) { addMacroDependencies(depModuleID, sourceDepDetails); return addBridgingHeaderDeps(sourceDepDetails); } llvm::Error addBridgingHeaderDeps(const ModuleDependencyInfo &depInfo) { auto sourceDepDetails = depInfo.getAsSwiftSourceModule(); if (!sourceDepDetails) return llvm::Error::success(); return addBridgingHeaderDeps(*sourceDepDetails); } llvm::Error addBridgingHeaderDeps( const SwiftSourceModuleDependenciesStorage &sourceDepDetails) { if (sourceDepDetails.textualModuleDetails.CASBridgingHeaderIncludeTreeRootID .empty()) { if (!sourceDepDetails.textualModuleDetails.bridgingSourceFiles.empty()) { if (tracker) { tracker->startTracking(/*includeCommonDeps*/ false); for (auto &file : sourceDepDetails.textualModuleDetails.bridgingSourceFiles) tracker->trackFile(file); auto bridgeRoot = tracker->createTreeFromDependencies(); if (!bridgeRoot) return bridgeRoot.takeError(); fileListIDs.push_back(bridgeRoot->getID().toString()); } } } else includeTrees.push_back(sourceDepDetails.textualModuleDetails .CASBridgingHeaderIncludeTreeRootID); return llvm::Error::success(); }; void addMacroDependencies(ModuleDependencyID moduleID, const ModuleDependencyInfoStorageBase &dep) { auto directDeps = cache.getAllDependencies(this->moduleID); if (llvm::find(directDeps, moduleID) == directDeps.end()) return; for (auto &entry : dep.macroDependencies) macros.insert({entry.first, {entry.second.LibraryPath, entry.second.ExecutablePath}}); } static bool isVFSOverlayFlag(StringRef arg) { return arg == "-ivfsoverlay" || arg == "-vfsoverlay"; }; static bool isXCCArg(StringRef arg) { return arg == "-Xcc"; }; void collectUsedVFSOverlay(const ClangModuleDependencyStorage &clangDepDetails) { // true if the previous argument was the dash-option of an option pair bool getNext = false; for (const auto &A : clangDepDetails.buildCommandLine) { StringRef arg(A); if (isXCCArg(arg)) continue; if (getNext) { getNext = false; usedVFSOverlayPaths.insert(arg); } else if (isVFSOverlayFlag(arg)) getNext = true; } } void pruneUnusedVFSOverlay() { // Pruning of unused VFS overlay options for Clang dependencies is performed // by the Clang dependency scanner. if (moduleID.Kind == ModuleDependencyKind::Clang) return; std::vector resolvedCommandLine; size_t skip = 0; for (auto it = commandline.begin(), end = commandline.end(); it != end; it++) { if (skip) { skip--; continue; } // If this VFS overlay was not used across any of the dependencies, skip // it. if ((it + 1) != end && isXCCArg(*it) && isVFSOverlayFlag(*(it + 1))) { assert(it + 2 != end); // Extra -Xcc assert(it + 3 != end); // Actual VFS overlay path argument if (!usedVFSOverlayPaths.contains(*(it + 3))) { skip = 3; continue; } } resolvedCommandLine.push_back(*it); } commandline = std::move(resolvedCommandLine); } llvm::Error collectCASDependencies(ModuleDependencyInfo &dependencyInfoCopy) { if (!instance.getInvocation().getCASOptions().EnableCaching) return llvm::Error::success(); // Collect CAS info from current resolving module. if (auto *sourceDep = resolvingDepInfo.getAsSwiftSourceModule()) { tracker->startTracking(); llvm::for_each( sourceDep->sourceFiles, [this](const std::string &file) { tracker->trackFile(file); }); llvm::for_each( sourceDep->auxiliaryFiles, [this](const std::string &file) { tracker->trackFile(file); }); llvm::for_each(sourceDep->macroDependencies, [this](const auto &entry) { tracker->trackFile(entry.second.LibraryPath); }); auto root = tracker->createTreeFromDependencies(); if (!root) return root.takeError(); auto rootID = root->getID().toString(); dependencyInfoCopy.updateCASFileSystemRootID(rootID); fileListIDs.push_back(rootID); } else if (auto *textualDep = resolvingDepInfo.getAsSwiftInterfaceModule()) { tracker->startTracking(); tracker->trackFile(textualDep->swiftInterfaceFile); llvm::for_each( textualDep->auxiliaryFiles, [this](const std::string &file) { tracker->trackFile(file); }); llvm::for_each(textualDep->macroDependencies, [this](const auto &entry) { tracker->trackFile(entry.second.LibraryPath); }); auto root = tracker->createTreeFromDependencies(); if (!root) return root.takeError(); auto rootID = root->getID().toString(); dependencyInfoCopy.updateCASFileSystemRootID(rootID); fileListIDs.push_back(rootID); } // Update build command line. if (resolvingDepInfo.isSwiftInterfaceModule() || resolvingDepInfo.isSwiftSourceModule()) { // Update with casfs option. for (auto rootID : rootIDs) { commandline.push_back("-cas-fs"); commandline.push_back(rootID); } for (auto tree : includeTrees) { commandline.push_back("-clang-include-tree-root"); commandline.push_back(tree); } for (auto list : fileListIDs) { commandline.push_back("-clang-include-tree-filelist"); commandline.push_back(list); } } // Compute and update module cache key. if (auto *binaryDep = dependencyInfoCopy.getAsSwiftBinaryModule()) { if (auto E = setupBinaryCacheKey(binaryDep->compiledModulePath, dependencyInfoCopy)) return E; } return llvm::Error::success(); } llvm::Error updateModuleCacheKey(ModuleDependencyInfo &depInfo) { if (!instance.getInvocation().getCASOptions().EnableCaching) return llvm::Error::success(); auto &CAS = cache.getScanService().getCAS(); auto commandLine = depInfo.getCommandline(); std::vector Args; if (commandLine.size() > 1) for (auto &c : ArrayRef(commandLine).drop_front(1)) Args.push_back(c.c_str()); auto base = createCompileJobBaseCacheKey(CAS, Args); if (!base) return base.takeError(); // Module compilation commands always have only one input and the input // index is always 0. auto key = createCompileJobCacheKeyForOutput(CAS, *base, /*InputIndex=*/0); if (!key) return key.takeError(); depInfo.updateModuleCacheKey(CAS.getID(*key).toString()); return llvm::Error::success(); } llvm::Error setupBinaryCacheKey(StringRef path, ModuleDependencyInfo &depInfo) { auto &CASFS = cache.getScanService().getSharedCachingFS(); auto &CAS = cache.getScanService().getCAS(); // For binary module, we need to make sure the lookup key is setup here in // action cache. We just use the CASID of the binary module itself as key. auto Ref = CASFS.getObjectRefForFileContent(path); if (!Ref) return llvm::errorCodeToError(Ref.getError()); assert(*Ref && "Binary module should be loaded into CASFS already"); depInfo.updateModuleCacheKey(CAS.getID(**Ref).toString()); swift::cas::CompileJobCacheResult::Builder Builder; Builder.addOutput(file_types::ID::TY_SwiftModuleFile, **Ref); auto Result = Builder.build(CAS); if (!Result) return Result.takeError(); if (auto E = instance.getActionCache().put(CAS.getID(**Ref), CAS.getID(*Result))) return E; return llvm::Error::success(); } private: ModuleDependencyID moduleID; ModuleDependenciesCache &cache; CompilerInstance &instance; const ModuleDependencyInfo &resolvingDepInfo; std::optional tracker; std::vector rootIDs; std::vector includeTrees; std::vector fileListIDs; std::vector commandline; std::vector bridgingHeaderBuildCmd; llvm::StringMap macros; llvm::StringSet<> usedVFSOverlayPaths; }; static llvm::Error resolveExplicitModuleInputs( ModuleDependencyID moduleID, const std::set &dependencies, ModuleDependenciesCache &cache, CompilerInstance &instance, std::optional> bridgingHeaderDeps, std::optional tracker) { ExplicitModuleDependencyResolver resolver(moduleID, cache, instance, std::move(tracker)); return resolver.resolve(dependencies, bridgingHeaderDeps); } static bool writePrescanJSONToOutput(DiagnosticEngine &diags, llvm::vfs::OutputBackend &backend, StringRef path, swiftscan_import_set_t importSet) { return withOutputPath(diags, backend, path, [&](llvm::raw_pwrite_stream &os) { writePrescanJSON(os, importSet); return false; }); } static bool writeJSONToOutput(DiagnosticEngine &diags, llvm::vfs::OutputBackend &backend, StringRef path, swiftscan_dependency_graph_t dependencies) { return withOutputPath(diags, backend, path, [&](llvm::raw_pwrite_stream &os) { writeJSON(os, dependencies); return false; }); } static void bridgeDependencyIDs(const ArrayRef dependencies, std::vector &bridgedDependencyNames) { for (const auto &dep : dependencies) { std::string dependencyKindAndName; switch (dep.Kind) { case ModuleDependencyKind::SwiftInterface: case ModuleDependencyKind::SwiftSource: dependencyKindAndName = "swiftTextual"; break; case ModuleDependencyKind::SwiftBinary: dependencyKindAndName = "swiftBinary"; break; case ModuleDependencyKind::SwiftPlaceholder: dependencyKindAndName = "swiftPlaceholder"; break; case ModuleDependencyKind::Clang: dependencyKindAndName = "clang"; break; default: llvm_unreachable("Unhandled dependency kind."); } dependencyKindAndName += ":"; dependencyKindAndName += dep.ModuleName; bridgedDependencyNames.push_back(dependencyKindAndName); } } static swiftscan_macro_dependency_set_t *createMacroDependencySet( const std::map ¯oDeps) { if (macroDeps.empty()) return nullptr; swiftscan_macro_dependency_set_t *set = new swiftscan_macro_dependency_set_t; set->count = macroDeps.size(); set->macro_dependencies = new swiftscan_macro_dependency_t[set->count]; unsigned SI = 0; for (auto &entry : macroDeps) { set->macro_dependencies[SI] = new swiftscan_macro_dependency_s; set->macro_dependencies[SI]->moduleName = create_clone(entry.first.c_str()); set->macro_dependencies[SI]->libraryPath = create_clone(entry.second.LibraryPath.c_str()); set->macro_dependencies[SI]->executablePath = create_clone(entry.second.ExecutablePath.c_str()); ++ SI; } return set; } static swiftscan_dependency_graph_t generateFullDependencyGraph(const CompilerInstance &instance, const DependencyScanDiagnosticCollector *diagnosticCollector, const ModuleDependenciesCache &cache, const ArrayRef allModules) { if (allModules.empty()) { return nullptr; } std::string mainModuleName = allModules.front().ModuleName; swiftscan_dependency_set_t *dependencySet = new swiftscan_dependency_set_t; dependencySet->count = allModules.size(); dependencySet->modules = new swiftscan_dependency_info_t[dependencySet->count]; for (size_t i = 0; i < allModules.size(); ++i) { const auto &moduleID = allModules[i]; auto &moduleDependencyInfo = cache.findKnownDependency(moduleID); // Collect all the required pieces to build a ModuleInfo auto swiftPlaceholderDeps = moduleDependencyInfo.getAsPlaceholderDependencyModule(); auto swiftTextualDeps = moduleDependencyInfo.getAsSwiftInterfaceModule(); auto swiftSourceDeps = moduleDependencyInfo.getAsSwiftSourceModule(); auto swiftBinaryDeps = moduleDependencyInfo.getAsSwiftBinaryModule(); auto clangDeps = moduleDependencyInfo.getAsClangModule(); // ModulePath const char *modulePathSuffix = moduleDependencyInfo.isSwiftModule() ? ".swiftmodule" : ".pcm"; std::string modulePath; if (swiftTextualDeps) modulePath = swiftTextualDeps->moduleOutputPath; else if (swiftPlaceholderDeps) modulePath = swiftPlaceholderDeps->compiledModulePath; else if (swiftBinaryDeps) modulePath = swiftBinaryDeps->compiledModulePath; else if (clangDeps) modulePath = clangDeps->pcmOutputPath; else modulePath = moduleID.ModuleName + modulePathSuffix; // SourceFiles std::vector sourceFiles; if (swiftSourceDeps) { sourceFiles = swiftSourceDeps->sourceFiles; } else if (clangDeps) { sourceFiles = clangDeps->fileDependencies; } auto directDependencies = cache.getAllDependencies(moduleID); std::vector clangHeaderDependencyNames; for (const auto &headerDepID : moduleDependencyInfo.getHeaderClangDependencies()) clangHeaderDependencyNames.push_back(headerDepID.ModuleName); // Generate a swiftscan_clang_details_t object based on the dependency kind auto getModuleDetails = [&]() -> swiftscan_module_details_t { swiftscan_module_details_s *details = new swiftscan_module_details_s; if (swiftTextualDeps) { swiftscan_string_ref_t moduleInterfacePath = create_clone(swiftTextualDeps->swiftInterfaceFile.c_str()); swiftscan_string_ref_t bridgingHeaderPath = swiftTextualDeps->textualModuleDetails.bridgingHeaderFile .has_value() ? create_clone( swiftTextualDeps->textualModuleDetails.bridgingHeaderFile .value() .c_str()) : create_null(); details->kind = SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL; // Create an overlay dependencies set according to the output format std::vector bridgedOverlayDependencyNames; bridgeDependencyIDs(swiftTextualDeps->swiftOverlayDependencies, bridgedOverlayDependencyNames); details->swift_textual_details = { moduleInterfacePath, create_set(swiftTextualDeps->compiledModuleCandidates), bridgingHeaderPath, create_set( swiftTextualDeps->textualModuleDetails.bridgingSourceFiles), create_set(clangHeaderDependencyNames), create_set(bridgedOverlayDependencyNames), create_set(swiftTextualDeps->textualModuleDetails.buildCommandLine), /*bridgingHeaderBuildCommand*/ create_set({}), create_set(swiftTextualDeps->textualModuleDetails.extraPCMArgs), create_clone(swiftTextualDeps->contextHash.c_str()), swiftTextualDeps->isFramework, swiftTextualDeps->isStatic, create_clone(swiftTextualDeps->textualModuleDetails .CASFileSystemRootID.c_str()), create_clone(swiftTextualDeps->textualModuleDetails .CASBridgingHeaderIncludeTreeRootID.c_str()), create_clone(swiftTextualDeps->moduleCacheKey.c_str()), createMacroDependencySet( swiftTextualDeps->macroDependencies), create_clone(swiftTextualDeps->userModuleVersion.c_str())}; } else if (swiftSourceDeps) { swiftscan_string_ref_t moduleInterfacePath = create_null(); swiftscan_string_ref_t bridgingHeaderPath = swiftSourceDeps->textualModuleDetails.bridgingHeaderFile.has_value() ? create_clone( swiftSourceDeps->textualModuleDetails.bridgingHeaderFile.value().c_str()) : create_null(); details->kind = SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL; // Create an overlay dependencies set according to the output format std::vector bridgedOverlayDependencyNames; bridgeDependencyIDs(swiftSourceDeps->swiftOverlayDependencies, bridgedOverlayDependencyNames); details->swift_textual_details = { moduleInterfacePath, create_empty_set(), bridgingHeaderPath, create_set( swiftSourceDeps->textualModuleDetails.bridgingSourceFiles), create_set(clangHeaderDependencyNames), create_set(bridgedOverlayDependencyNames), create_set(swiftSourceDeps->textualModuleDetails.buildCommandLine), create_set(swiftSourceDeps->bridgingHeaderBuildCommandLine), create_set(swiftSourceDeps->textualModuleDetails.extraPCMArgs), /*contextHash*/ create_clone( instance.getInvocation().getModuleScanningHash().c_str()), /*isFramework*/ false, /*isStatic*/ false, /*CASFS*/ create_clone(swiftSourceDeps->textualModuleDetails .CASFileSystemRootID.c_str()), /*IncludeTree*/ create_clone(swiftSourceDeps->textualModuleDetails .CASBridgingHeaderIncludeTreeRootID.c_str()), /*CacheKey*/ create_clone(""), createMacroDependencySet( swiftSourceDeps->macroDependencies), /*userModuleVersion*/ create_clone("")}; } else if (swiftPlaceholderDeps) { details->kind = SWIFTSCAN_DEPENDENCY_INFO_SWIFT_PLACEHOLDER; details->swift_placeholder_details = { create_clone(swiftPlaceholderDeps->compiledModulePath.c_str()), create_clone(swiftPlaceholderDeps->moduleDocPath.c_str()), create_clone(swiftPlaceholderDeps->sourceInfoPath.c_str())}; } else if (swiftBinaryDeps) { details->kind = SWIFTSCAN_DEPENDENCY_INFO_SWIFT_BINARY; // Create an overlay dependencies set according to the output format std::vector bridgedOverlayDependencyNames; bridgeDependencyIDs(swiftBinaryDeps->swiftOverlayDependencies, bridgedOverlayDependencyNames); details->swift_binary_details = { create_clone(swiftBinaryDeps->compiledModulePath.c_str()), create_clone(swiftBinaryDeps->moduleDocPath.c_str()), create_clone(swiftBinaryDeps->sourceInfoPath.c_str()), create_set(bridgedOverlayDependencyNames), create_clone(swiftBinaryDeps->headerImport.c_str()), create_set(clangHeaderDependencyNames), create_set(swiftBinaryDeps->headerSourceFiles), swiftBinaryDeps->isFramework, swiftBinaryDeps->isStatic, createMacroDependencySet(swiftBinaryDeps->macroDependencies), create_clone(swiftBinaryDeps->moduleCacheKey.c_str()), create_clone(swiftBinaryDeps->userModuleVersion.c_str())}; } else { // Clang module details details->kind = SWIFTSCAN_DEPENDENCY_INFO_CLANG; details->clang_details = { create_clone(clangDeps->moduleMapFile.c_str()), create_clone(clangDeps->contextHash.c_str()), create_set(clangDeps->buildCommandLine), create_set(clangDeps->capturedPCMArgs), create_clone(clangDeps->CASFileSystemRootID.c_str()), create_clone(clangDeps->CASClangIncludeTreeRootID.c_str()), create_clone(clangDeps->moduleCacheKey.c_str())}; } return details; }; swiftscan_dependency_info_s *moduleInfo = new swiftscan_dependency_info_s; dependencySet->modules[i] = moduleInfo; std::string encodedModuleName = createEncodedModuleKindAndName(moduleID); auto ttt = create_clone(encodedModuleName.c_str()); moduleInfo->module_name = ttt; moduleInfo->module_path = create_clone(modulePath.c_str()); moduleInfo->source_files = create_set(sourceFiles); // Create a direct dependencies set according to the output format std::vector bridgedDependencyNames; bridgeDependencyIDs(directDependencies.getArrayRef(), bridgedDependencyNames); moduleInfo->direct_dependencies = create_set(bridgedDependencyNames); moduleInfo->details = getModuleDetails(); // Create a link libraries set for this module auto &linkLibraries = moduleDependencyInfo.getLinkLibraries(); swiftscan_link_library_set_t *linkLibrarySet = new swiftscan_link_library_set_t; linkLibrarySet->count = linkLibraries.size(); linkLibrarySet->link_libraries = new swiftscan_link_library_info_t[linkLibrarySet->count]; for (size_t i = 0; i < linkLibraries.size(); ++i) { const auto &ll = linkLibraries[i]; swiftscan_link_library_info_s *llInfo = new swiftscan_link_library_info_s; llInfo->name = create_clone(ll.getName().str().c_str()); llInfo->isFramework = ll.getKind() == LibraryKind::Framework; llInfo->forceLoad = ll.shouldForceLoad(); linkLibrarySet->link_libraries[i] = llInfo; } moduleInfo->link_libraries = linkLibrarySet; } swiftscan_dependency_graph_t result = new swiftscan_dependency_graph_s; result->main_module_name = create_clone(mainModuleName.c_str()); result->dependencies = dependencySet; result->diagnostics = diagnosticCollector ? mapCollectedDiagnosticsForOutput(diagnosticCollector) : nullptr; return result; } /// Implements a topological sort via recursion and reverse postorder DFS. /// Does not bother handling cycles, relying on a DAG guarantee by the client. static std::vector computeTopologicalSortOfExplicitDependencies( const std::vector &allModules, const ModuleDependenciesCache &cache) { std::unordered_set visited; std::vector result; std::stack stack; // Must be explicitly-typed to allow recursion std::function visit; visit = [&visit, &cache, &visited, &result, &stack](const ModuleDependencyID &moduleID) { // Mark this node as visited -- we are done if it already was. if (!visited.insert(moduleID).second) return; // Otherwise, visit each adjacent node. for (const auto &succID : cache.getAllDependencies(moduleID)) { // We don't worry if successor is already in this current stack, // since that would mean we have found a cycle, which should not // be possible because we checked for cycles earlier. stack.push(succID); visit(succID); auto top = stack.top(); stack.pop(); assert(top == succID); } // Add to the result. result.push_back(moduleID); }; for (const auto &modID : allModules) { assert(stack.empty()); stack.push(modID); visit(modID); auto top = stack.top(); stack.pop(); assert(top == modID); } std::reverse(result.begin(), result.end()); return result; } /// For each module in the graph, compute a set of all its dependencies, /// direct *and* transitive. static std::unordered_map> computeTransitiveClosureOfExplicitDependencies( const std::vector &topologicallySortedModuleList, const ModuleDependenciesCache &cache) { // The usage of an ordered ::set is important to ensure the // dependencies are listed in a deterministic order. std::unordered_map> result; for (const auto &modID : topologicallySortedModuleList) result[modID] = {modID}; // Traverse the set of modules in reverse topological order, assimilating // transitive closures for (auto it = topologicallySortedModuleList.rbegin(), end = topologicallySortedModuleList.rend(); it != end; ++it) { const auto &modID = *it; auto &modReachableSet = result[modID]; for (const auto &succID : cache.getAllDependencies(modID)) { const auto &succReachableSet = result[succID]; llvm::set_union(modReachableSet, succReachableSet); } } // For ease of use down-the-line, remove the node's self from its set of // reachable nodes for (const auto &modID : topologicallySortedModuleList) result[modID].erase(modID); return result; } static std::set computeBridgingHeaderTransitiveDependencies( const ModuleDependencyInfo &dep, const std::unordered_map> &transitiveClosures, const ModuleDependenciesCache &cache) { std::set result; if (!dep.isSwiftSourceModule()) return result; for (auto &depID : dep.getHeaderClangDependencies()) { result.insert(depID); auto succDeps = transitiveClosures.find(depID); assert(succDeps != transitiveClosures.end() && "unknown dependency"); llvm::set_union(result, succDeps->second); } return result; } static std::vector findClangDepPath(const ModuleDependencyID &from, const ModuleDependencyID &to, const ModuleDependenciesCache &cache) { std::unordered_set visited; std::vector result; std::stack> stack; // Must be explicitly-typed to allow recursion std::function visit; visit = [&visit, &cache, &visited, &result, &stack, to](const ModuleDependencyID &moduleID) { if (!visited.insert(moduleID).second) return; if (moduleID == to) { // Copy stack contents to the result auto end = &stack.top() + 1; auto begin = end - stack.size(); result.assign(begin, end); return; } // Otherwise, visit each child node. for (const auto &succID : cache.getAllDependencies(moduleID)) { stack.push(succID); visit(succID); stack.pop(); } }; stack.push(from); visit(from); return result; } static bool diagnoseCycle(const CompilerInstance &instance, const ModuleDependenciesCache &cache, ModuleDependencyID mainId) { ModuleDependencyIDSetVector openSet; ModuleDependencyIDSetVector closeSet; auto kindIsSwiftDependency = [&](const ModuleDependencyID &ID) { return ID.Kind == swift::ModuleDependencyKind::SwiftInterface || ID.Kind == swift::ModuleDependencyKind::SwiftBinary || ID.Kind == swift::ModuleDependencyKind::SwiftSource; }; auto emitModulePath = [&](const std::vector path, llvm::SmallString<64> &buffer) { llvm::interleave( path, [&buffer](const ModuleDependencyID &id) { buffer.append(id.ModuleName); switch (id.Kind) { case swift::ModuleDependencyKind::SwiftSource: buffer.append(" (Source Target)"); break; case swift::ModuleDependencyKind::SwiftInterface: buffer.append(".swiftinterface"); break; case swift::ModuleDependencyKind::SwiftBinary: buffer.append(".swiftmodule"); break; case swift::ModuleDependencyKind::Clang: buffer.append(".pcm"); break; default: llvm::report_fatal_error( Twine("Invalid Module Dependency Kind in cycle: ") + id.ModuleName); break; } }, [&buffer] { buffer.append(" -> "); }); }; auto emitCycleDiagnostic = [&](const ModuleDependencyID &sourceId, const ModuleDependencyID &sinkId) { auto startIt = std::find(openSet.begin(), openSet.end(), sourceId); assert(startIt != openSet.end()); std::vector cycleNodes(startIt, openSet.end()); cycleNodes.push_back(sinkId); llvm::SmallString<64> errorBuffer; emitModulePath(cycleNodes, errorBuffer); instance.getASTContext().Diags.diagnose( SourceLoc(), diag::scanner_find_cycle, errorBuffer.str()); // TODO: for (std::tuple v : cycleNodes | std::views::adjacent<2>) for (auto it = cycleNodes.begin(), end = cycleNodes.end(); it != end; it++) { if (it + 1 == cycleNodes.end()) continue; const auto &thisID = *it; const auto &nextID = *(it + 1); if (kindIsSwiftDependency(thisID) && kindIsSwiftDependency(nextID) && llvm::any_of( cache.getSwiftOverlayDependencies(thisID), [&](const ModuleDependencyID id) { return id == nextID; })) { llvm::SmallString<64> noteBuffer; auto clangDepPath = findClangDepPath( thisID, ModuleDependencyID{nextID.ModuleName, ModuleDependencyKind::Clang}, cache); emitModulePath(clangDepPath, noteBuffer); instance.getASTContext().Diags.diagnose( SourceLoc(), diag::scanner_find_cycle_swift_overlay_path, thisID.ModuleName, nextID.ModuleName, noteBuffer.str()); } } }; // Start from the main module and check direct and overlay dependencies openSet.insert(mainId); while (!openSet.empty()) { auto lastOpen = openSet.back(); auto beforeSize = openSet.size(); assert(cache.findDependency(lastOpen).has_value() && "Missing dependency info during cycle diagnosis."); for (const auto &depId : cache.getAllDependencies(lastOpen)) { if (closeSet.count(depId)) continue; // Ensure we detect dependency of the Source target // on an existing Swift module with the same name if (kindIsSwiftDependency(depId) && depId.ModuleName == mainId.ModuleName && openSet.contains(mainId)) { emitCycleDiagnostic(mainId, depId); return true; } if (openSet.insert(depId)) { break; } else { emitCycleDiagnostic(depId, depId); return true; } } // No new node added. We can close this node if (openSet.size() == beforeSize) { closeSet.insert(openSet.back()); openSet.pop_back(); } else { assert(openSet.size() == beforeSize + 1); } } assert(openSet.empty()); closeSet.clear(); return false; } static void updateCachedInstanceOpts(CompilerInstance &cachedInstance, const CompilerInstance &invocationInstance, llvm::StringRef entryArguments) { cachedInstance.getASTContext().SearchPathOpts = invocationInstance.getASTContext().SearchPathOpts; // The Clang Importer arguments must consist of a combination of // Clang Importer arguments of the current invocation to inherit its Clang-specific // search path options, followed by the options specific to the given batch-entry, // which may overload some of the invocation's options (e.g. target) cachedInstance.getASTContext().ClangImporterOpts = invocationInstance.getASTContext().ClangImporterOpts; std::istringstream iss(entryArguments.str()); std::vector splitArguments( std::istream_iterator{iss}, std::istream_iterator()); for (auto it = splitArguments.begin(), end = splitArguments.end(); it != end; ++it) { if ((*it) == "-Xcc") { assert((it + 1 != end) && "Expected option following '-Xcc'"); cachedInstance.getASTContext().ClangImporterOpts.ExtraArgs.push_back( *(it + 1)); } } } static bool forEachBatchEntry(CompilerInstance &invocationInstance, ModuleDependenciesCache &invocationCache, CompilerArgInstanceCacheMap *versionedPCMInstanceCache, llvm::StringSaver &saver, const std::vector &batchInput, llvm::function_ref scanningAction) { const CompilerInvocation &invoke = invocationInstance.getInvocation(); bool localSubInstanceMap = false; CompilerArgInstanceCacheMap *subInstanceMap; if (versionedPCMInstanceCache) subInstanceMap = versionedPCMInstanceCache; else { subInstanceMap = new CompilerArgInstanceCacheMap; localSubInstanceMap = true; } SWIFT_DEFER { if (localSubInstanceMap) delete subInstanceMap; }; auto &diags = invocationInstance.getDiags(); ForwardingDiagnosticConsumer FDC(invocationInstance.getDiags()); for (auto &entry : batchInput) { CompilerInstance *pInstance = nullptr; ModuleDependenciesCache *pCache = nullptr; if (entry.arguments.empty()) { // Use the compiler's instance if no arguments are specified. pInstance = &invocationInstance; pCache = &invocationCache; } else if (subInstanceMap->count(entry.arguments)) { // Use the previously created instance if we've seen the arguments // before. pInstance = std::get<0>((*subInstanceMap)[entry.arguments]).get(); pCache = std::get<2>((*subInstanceMap)[entry.arguments]).get(); // We must update the search paths of this instance to instead reflect // those of the current scanner invocation. updateCachedInstanceOpts(*pInstance, invocationInstance, entry.arguments); } else { // Create a new instance by the arguments and save it in the map. auto newService = std::make_unique(); auto newInstance = std::make_unique(); SmallVector args; llvm::cl::TokenizeGNUCommandLine(entry.arguments, saver, args); CompilerInvocation subInvoke = invoke; newInstance->addDiagnosticConsumer(&FDC); if (subInvoke.parseArgs(args, diags)) { invocationInstance.getDiags().diagnose( SourceLoc(), diag::scanner_arguments_invalid, entry.arguments); return true; } std::string InstanceSetupError; if (newInstance->setup(subInvoke, InstanceSetupError)) { invocationInstance.getDiags().diagnose( SourceLoc(), diag::scanner_arguments_invalid, entry.arguments); return true; } auto mainModuleName = newInstance->getMainModule()->getNameStr(); auto scanContextHash = newInstance->getInvocation().getModuleScanningHash(); auto moduleOutputPath = newInstance->getInvocation() .getFrontendOptions() .ExplicitModulesOutputPath; auto newLocalCache = std::make_unique( *newService, mainModuleName.str(), moduleOutputPath, scanContextHash); pInstance = newInstance.get(); pCache = newLocalCache.get(); subInstanceMap->insert( {entry.arguments, std::make_tuple(std::move(newInstance), std::move(newService), std::move(newLocalCache))}); } assert(pInstance); assert(pCache); scanningAction(entry, *pInstance, *pCache); } return false; } } // namespace bool swift::dependencies::scanDependencies(CompilerInstance &CI) { ASTContext &ctx = CI.getASTContext(); const FrontendOptions &opts = CI.getInvocation().getFrontendOptions(); std::string depGraphOutputPath = opts.InputsAndOutputs.getSingleOutputFilename(); // `-scan-dependencies` invocations use a single new instance // of a module cache SwiftDependencyScanningService *service = ctx.Allocate(); ModuleDependenciesCache cache( *service, CI.getMainModule()->getNameStr().str(), CI.getInvocation().getFrontendOptions().ExplicitModulesOutputPath, CI.getInvocation().getModuleScanningHash()); // Load the dependency cache if -reuse-dependency-scan-cache // is specified if (opts.ReuseDependencyScannerCache) { auto cachePath = opts.SerializedDependencyScannerCachePath; module_dependency_cache_serialization::readInterModuleDependenciesCache(cachePath, cache); if (opts.EmitDependencyScannerCacheRemarks) ctx.Diags.diagnose(SourceLoc(), diag::remark_reuse_cache, cachePath); } if (service->setupCachingDependencyScanningService(CI)) return true; // Execute scan llvm::ErrorOr dependenciesOrErr = performModuleScan(CI, nullptr, cache); // Serialize the dependency cache if -serialize-dependency-scan-cache // is specified if (opts.SerializeDependencyScannerCache) { auto savePath = opts.SerializedDependencyScannerCachePath; module_dependency_cache_serialization::writeInterModuleDependenciesCache( ctx.Diags, CI.getOutputBackend(), savePath, cache); if (opts.EmitDependencyScannerCacheRemarks) ctx.Diags.diagnose(SourceLoc(), diag::remark_save_cache, savePath); } if (dependenciesOrErr.getError()) return true; auto dependencies = std::move(*dependenciesOrErr); if (writeJSONToOutput(ctx.Diags, CI.getOutputBackend(), depGraphOutputPath, dependencies)) return true; // This process succeeds regardless of whether any errors occurred. // FIXME: We shouldn't need this, but it's masking bugs in our scanning // logic where we don't create a fresh context when scanning Swift interfaces // that includes their own command-line flags. ctx.Diags.resetHadAnyError(); return false; } bool swift::dependencies::prescanDependencies(CompilerInstance &instance) { ASTContext &Context = instance.getASTContext(); const FrontendOptions &opts = instance.getInvocation().getFrontendOptions(); std::string path = opts.InputsAndOutputs.getSingleOutputFilename(); // `-scan-dependencies` invocations use a single new instance // of a module cache SwiftDependencyScanningService *singleUseService = Context.Allocate(); ModuleDependenciesCache cache( *singleUseService, instance.getMainModule()->getNameStr().str(), instance.getInvocation().getFrontendOptions().ExplicitModulesOutputPath, instance.getInvocation().getModuleScanningHash()); // Execute import prescan, and write JSON output to the output stream auto importSetOrErr = performModulePrescan(instance, nullptr, cache); if (importSetOrErr.getError()) return true; auto importSet = std::move(*importSetOrErr); // Serialize and output main module dependencies only and exit. if (writePrescanJSONToOutput(Context.Diags, instance.getOutputBackend(), path, importSet)) return true; // This process succeeds regardless of whether any errors occurred. // FIXME: We shouldn't need this, but it's masking bugs in our scanning // logic where we don't create a fresh context when scanning Swift interfaces // that includes their own command-line flags. Context.Diags.resetHadAnyError(); return false; } bool swift::dependencies::batchScanDependencies( CompilerInstance &instance, llvm::StringRef batchInputFile) { // The primary cache used for scans carried out with the compiler instance // we have created SwiftDependencyScanningService singleUseService; if (singleUseService.setupCachingDependencyScanningService(instance)) return true; ModuleDependenciesCache cache( singleUseService, instance.getMainModule()->getNameStr().str(), instance.getInvocation().getFrontendOptions().ExplicitModulesOutputPath, instance.getInvocation().getModuleScanningHash()); (void)instance.getMainModule(); llvm::BumpPtrAllocator alloc; llvm::StringSaver saver(alloc); auto batchInput = parseBatchScanInputFile(instance.getASTContext(), batchInputFile, saver); if (!batchInput.has_value()) return true; auto batchScanResults = performBatchModuleScan( instance, /*DependencyScanDiagnosticCollector*/ nullptr, cache, /*versionedPCMInstanceCache*/ nullptr, saver, *batchInput); // Write the result JSON to the specified output path, for each entry auto ientries = batchInput->cbegin(); auto iresults = batchScanResults.cbegin(); for (; ientries != batchInput->end() and iresults != batchScanResults.end(); ++ientries, ++iresults) { if ((*iresults).getError()) return true; if (writeJSONToOutput(instance.getASTContext().Diags, instance.getOutputBackend(), (*ientries).outputPath, **iresults)) return true; } return false; } std::string swift::dependencies::createEncodedModuleKindAndName(ModuleDependencyID id) { switch (id.Kind) { case ModuleDependencyKind::SwiftInterface: case ModuleDependencyKind::SwiftSource: return "swiftTextual:" + id.ModuleName; case ModuleDependencyKind::SwiftBinary: return "swiftBinary:" + id.ModuleName; case ModuleDependencyKind::SwiftPlaceholder: return "swiftPlaceholder:" + id.ModuleName; case ModuleDependencyKind::Clang: return "clang:" + id.ModuleName; default: llvm_unreachable("Unhandled dependency kind."); } } static void resolveDependencyCommandLineArguments( CompilerInstance &instance, ModuleDependenciesCache &cache, const std::vector &topoSortedModuleList) { auto moduleTransitiveClosures = computeTransitiveClosureOfExplicitDependencies(topoSortedModuleList, cache); auto tracker = cache.getScanService().createSwiftDependencyTracker( instance.getInvocation()); for (const auto &modID : llvm::reverse(topoSortedModuleList)) { auto dependencyClosure = moduleTransitiveClosures[modID]; // For main module or binary modules, no command-line to resolve. // For Clang modules, their dependencies are resolved by the clang Scanner // itself for us. auto &deps = cache.findKnownDependency(modID); std::optional> bridgingHeaderDeps; if (modID.Kind == ModuleDependencyKind::SwiftSource) bridgingHeaderDeps = computeBridgingHeaderTransitiveDependencies( deps, moduleTransitiveClosures, cache); if (auto E = resolveExplicitModuleInputs(modID, dependencyClosure, cache, instance, bridgingHeaderDeps, tracker)) instance.getDiags().diagnose(SourceLoc(), diag::error_cas, toString(std::move(E))); } } static void updateDependencyTracker(CompilerInstance &instance, ModuleDependenciesCache &cache, const std::vector &allModules) { if (auto depTracker = instance.getDependencyTracker()) { for (auto module : allModules) { auto optionalDeps = cache.findDependency(module); if (!optionalDeps.has_value()) continue; auto deps = optionalDeps.value(); if (auto swiftDeps = deps->getAsSwiftInterfaceModule()) { depTracker->addDependency(swiftDeps->swiftInterfaceFile, /*IsSystem=*/false); for (const auto &bridgingSourceFile : swiftDeps->textualModuleDetails.bridgingSourceFiles) depTracker->addDependency(bridgingSourceFile, /*IsSystem=*/false); } else if (auto swiftSourceDeps = deps->getAsSwiftSourceModule()) { for (const auto &sourceFile : swiftSourceDeps->sourceFiles) depTracker->addDependency(sourceFile, /*IsSystem=*/false); for (const auto &bridgingSourceFile : swiftSourceDeps->textualModuleDetails.bridgingSourceFiles) depTracker->addDependency(bridgingSourceFile, /*IsSystem=*/false); } else if (auto clangDeps = deps->getAsClangModule()) { if (!clangDeps->moduleMapFile.empty()) depTracker->addDependency(clangDeps->moduleMapFile, /*IsSystem=*/false); for (const auto &sourceFile : clangDeps->fileDependencies) depTracker->addDependency(sourceFile, /*IsSystem=*/false); } } } } static void resolveImplicitLinkLibraries(const CompilerInstance &instance, ModuleDependenciesCache &cache) { auto langOpts = instance.getInvocation().getLangOptions(); auto irGenOpts = instance.getInvocation().getIRGenOptions(); auto mainModuleName = instance.getMainModule()->getNameStr(); auto mainModuleID = ModuleDependencyID{mainModuleName.str(), ModuleDependencyKind::SwiftSource}; auto mainModuleDepInfo = cache.findKnownDependency(mainModuleID); std::vector linkLibraries; auto addLinkLibrary = [&linkLibraries](const LinkLibrary &ll) { linkLibraries.push_back(ll); }; if (langOpts.EnableObjCInterop) addLinkLibrary({"objc", LibraryKind::Library}); if (langOpts.EnableCXXInterop) { auto OptionalCxxDep = cache.findDependency(CXX_MODULE_NAME); auto OptionalCxxStdLibDep = cache.findDependency("CxxStdlib"); bool hasStaticCxx = OptionalCxxDep.has_value() && OptionalCxxDep.value()->isStaticLibrary(); bool hasStaticCxxStdlib = OptionalCxxStdLibDep.has_value() && OptionalCxxStdLibDep.value()->isStaticLibrary(); registerCxxInteropLibraries(langOpts.Target, mainModuleName, hasStaticCxx, hasStaticCxxStdlib, langOpts.CXXStdlib, addLinkLibrary); } if (!irGenOpts.UseJIT && !langOpts.hasFeature(Feature::Embedded)) registerBackDeployLibraries(irGenOpts, addLinkLibrary); mainModuleDepInfo.setLinkLibraries(linkLibraries); cache.updateDependency(mainModuleID, mainModuleDepInfo); } llvm::ErrorOr swift::dependencies::performModuleScan( CompilerInstance &instance, DependencyScanDiagnosticCollector *diagnosticCollector, ModuleDependenciesCache &cache) { auto scanner = ModuleDependencyScanner( cache.getScanService(), instance.getInvocation(), instance.getSILOptions(), instance.getASTContext(), *instance.getDependencyTracker(), instance.getDiags(), instance.getInvocation().getFrontendOptions().ParallelDependencyScan); // Identify imports of the main module and add an entry for it // to the dependency graph. auto mainModuleName = instance.getMainModule()->getNameStr(); auto mainModuleID = ModuleDependencyID{mainModuleName.str(), ModuleDependencyKind::SwiftSource}; if (!cache.hasDependency(mainModuleID)) cache.recordDependency(mainModuleName, *scanner.getMainModuleDependencyInfo(instance.getMainModule())); // Perform the full module scan starting at the main module. auto allModules = scanner.performDependencyScan(mainModuleID, cache); if (diagnoseCycle(instance, cache, mainModuleID)) return std::make_error_code(std::errc::not_supported); auto topologicallySortedModuleList = computeTopologicalSortOfExplicitDependencies(allModules, cache); resolveDependencyCommandLineArguments(instance, cache, topologicallySortedModuleList); resolveImplicitLinkLibraries(instance, cache); updateDependencyTracker(instance, cache, allModules); if (instance.getInvocation().getFrontendOptions().EmitDependencyScannerCacheRemarks) instance.getASTContext().Diags.diagnose(SourceLoc(), diag::remark_scanner_uncached_lookups, scanner.getNumLookups()); return generateFullDependencyGraph(instance, diagnosticCollector, cache, topologicallySortedModuleList); } llvm::ErrorOr swift::dependencies::performModulePrescan(CompilerInstance &instance, DependencyScanDiagnosticCollector *diagnosticCollector, ModuleDependenciesCache &cache) { // Setup the scanner auto scanner = ModuleDependencyScanner( cache.getScanService(), instance.getInvocation(), instance.getSILOptions(), instance.getASTContext(), *instance.getDependencyTracker(), instance.getDiags(), instance.getInvocation().getFrontendOptions().ParallelDependencyScan); // Execute import prescan, and write JSON output to the output stream auto mainDependencies = scanner.getMainModuleDependencyInfo(instance.getMainModule()); if (!mainDependencies) return mainDependencies.getError(); auto *importSet = new swiftscan_import_set_s; std::vector importIdentifiers; importIdentifiers.reserve(mainDependencies->getModuleImports().size()); llvm::transform(mainDependencies->getModuleImports(), std::back_inserter(importIdentifiers), [](const auto &importInfo) -> std::string { return importInfo.importIdentifier; }); importSet->imports = create_set(importIdentifiers); importSet->diagnostics = diagnosticCollector ? mapCollectedDiagnosticsForOutput(diagnosticCollector) : nullptr; importSet->diagnostics = diagnosticCollector ? mapCollectedDiagnosticsForOutput(diagnosticCollector) : nullptr; return importSet; } std::vector> swift::dependencies::performBatchModuleScan( CompilerInstance &invocationInstance, DependencyScanDiagnosticCollector *diagnosticCollector, ModuleDependenciesCache &invocationCache, CompilerArgInstanceCacheMap *versionedPCMInstanceCache, llvm::StringSaver &saver, const std::vector &batchInput) { std::vector> batchScanResult; batchScanResult.reserve(batchInput.size()); // Perform a full dependency scan for each batch entry module forEachBatchEntry( invocationInstance, invocationCache, versionedPCMInstanceCache, saver, batchInput, [&batchScanResult, &diagnosticCollector](BatchScanInput entry, CompilerInstance &instance, ModuleDependenciesCache &cache) { auto scanner = ModuleDependencyScanner( cache.getScanService(), instance.getInvocation(), instance.getSILOptions(), instance.getASTContext(), *instance.getDependencyTracker(), instance.getDiags(), instance.getInvocation().getFrontendOptions().ParallelDependencyScan); StringRef moduleName = entry.moduleName; bool isClang = !entry.isSwift; ModuleDependencyID moduleID{ moduleName.str(), isClang ? ModuleDependencyKind::Clang : ModuleDependencyKind::SwiftInterface}; std::optional rootDeps; std::vector allDependencies; if (isClang) { // Loading the clang module using Clang importer. // This action will populate the cache with the main module's // dependencies. ModuleDependencyIDSetVector allClangModules; rootDeps = scanner.getNamedClangModuleDependencyInfo(moduleName, cache, allClangModules); if (!rootDeps.has_value()) { batchScanResult.push_back( std::make_error_code(std::errc::invalid_argument)); return; } allDependencies = allClangModules.takeVector(); } else { rootDeps = scanner.getNamedSwiftModuleDependencyInfo(moduleName, cache); if (!rootDeps.has_value()) { batchScanResult.push_back( std::make_error_code(std::errc::invalid_argument)); return; } allDependencies = scanner.performDependencyScan(moduleID, cache); } batchScanResult.push_back( generateFullDependencyGraph(instance, diagnosticCollector, cache, allDependencies)); if (diagnosticCollector) diagnosticCollector->reset(); }); return batchScanResult; }