//===--- ModuleDependencies.h - Module Dependencies -------------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// // // This file defines data structures for capturing all of the source files // and modules on which a given module depends, forming a graph of all of the // modules that need to be present for a given module to be built. // //===----------------------------------------------------------------------===// #ifndef SWIFT_AST_MODULE_DEPENDENCIES_H #define SWIFT_AST_MODULE_DEPENDENCIES_H #include "swift/Basic/LLVM.h" #include "swift/AST/Import.h" #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSet.h" #include #include #include namespace swift { class ClangModuleDependenciesCacheImpl; class SourceFile; class ASTContext; class Identifier; class CompilerInstance; /// Which kind of module dependencies we are looking for. enum class ModuleDependencyKind : int8_t { FirstKind, // Textual Swift dependency SwiftInterface = FirstKind, // Binary module Swift dependency SwiftBinary, // Clang module dependency Clang, // Used to model the translation unit's source module SwiftSource, // Placeholder dependencies are a kind of dependencies used only by the // dependency scanner. They are swift modules that the scanner will not be // able to locate in its search paths and which are the responsibility of the // scanner's client to ensure are provided. // // Placeholder dependencies will be specified in the scanner's output // dependency graph where it is the responsibility of the scanner's client to // ensure required post-processing takes place to "resolve" them. In order to // do so, the client (swift driver, or any other client build system) is // expected to have access to a full dependency graph of all placeholder // dependencies and be able to replace placeholder nodes in the dependency // graph with their full dependency trees, `uniquing` common dependency module // nodes in the process. // // One example where placeholder dependencies are employed is when using // SwiftPM in Explicit Module Build mode. SwiftPM constructs a build plan for // all targets ahead-of-time. When planning a build for a target that depends // on other targets, the dependency scanning action is not able to locate // dependency target modules, because they have not yet been built. Instead, // the build system treats them as placeholder dependencies and resolves them // with `actual` dependencies in a post-processing step once dependency graphs // of all targets, individually, have been computed. SwiftPlaceholder, LastKind = SwiftPlaceholder + 1 }; struct ModuleDependencyKindHash { std::size_t operator()(ModuleDependencyKind k) const { using UnderlyingType = std::underlying_type::type; return std::hash{}(static_cast(k)); } }; /// Base class for the variant storage of ModuleDependencyInfo. /// /// This class is mostly an implementation detail for \c ModuleDependencyInfo. class ModuleDependencyInfoStorageBase { public: const ModuleDependencyKind dependencyKind; ModuleDependencyInfoStorageBase(ModuleDependencyKind dependencyKind) : dependencyKind(dependencyKind) { } virtual ModuleDependencyInfoStorageBase *clone() const = 0; virtual ~ModuleDependencyInfoStorageBase(); /// The set of modules on which this module depends. std::vector moduleDependencies; }; struct CommonSwiftTextualModuleDependencyDetails { CommonSwiftTextualModuleDependencyDetails(ArrayRef extraPCMArgs) : extraPCMArgs(extraPCMArgs.begin(), extraPCMArgs.end()) {} /// To build a PCM to be used by this Swift module, we need to append these /// arguments to the generic PCM build arguments reported from the dependency /// graph. const std::vector extraPCMArgs; /// Bridging header file, if there is one. Optional bridgingHeaderFile; /// Source files on which the bridging header depends. std::vector bridgingSourceFiles; /// (Clang) modules on which the bridging header depends. std::vector bridgingModuleDependencies; }; /// Describes the dependencies of a Swift module described by an Swift interface file. /// /// This class is mostly an implementation detail for \c ModuleDependencyInfo. class SwiftInterfaceModuleDependenciesStorage : public ModuleDependencyInfoStorageBase { public: /// Destination output path const std::string moduleOutputPath; /// The Swift interface file to be used to generate the module file. const std::string swiftInterfaceFile; /// Potentially ready-to-use compiled modules for the interface file. const std::vector compiledModuleCandidates; /// The Swift frontend invocation arguments to build the Swift module from the /// interface. const std::vector buildCommandLine; /// The hash value that will be used for the generated module const std::string contextHash; /// A flag that indicates this dependency is a framework const bool isFramework; /// Details common to Swift textual (interface or source) modules CommonSwiftTextualModuleDependencyDetails textualModuleDetails; SwiftInterfaceModuleDependenciesStorage( const std::string moduleOutputPath, const std::string swiftInterfaceFile, ArrayRef compiledModuleCandidates, ArrayRef buildCommandLine, ArrayRef extraPCMArgs, StringRef contextHash, bool isFramework ) : ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftInterface), moduleOutputPath(moduleOutputPath), swiftInterfaceFile(swiftInterfaceFile), compiledModuleCandidates(compiledModuleCandidates.begin(), compiledModuleCandidates.end()), buildCommandLine(buildCommandLine.begin(), buildCommandLine.end()), contextHash(contextHash), isFramework(isFramework), textualModuleDetails(extraPCMArgs) {} ModuleDependencyInfoStorageBase *clone() const override { return new SwiftInterfaceModuleDependenciesStorage(*this); } static bool classof(const ModuleDependencyInfoStorageBase *base) { return base->dependencyKind == ModuleDependencyKind::SwiftInterface; } }; /// Describes the dependencies of a Swift module /// /// This class is mostly an implementation detail for \c ModuleDependencyInfo. class SwiftSourceModuleDependenciesStorage : public ModuleDependencyInfoStorageBase { public: /// Swift source files that are part of the Swift module, when known. std::vector sourceFiles; /// Details common to Swift textual (interface or source) modules CommonSwiftTextualModuleDependencyDetails textualModuleDetails; SwiftSourceModuleDependenciesStorage( ArrayRef extraPCMArgs ) : ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftSource), textualModuleDetails(extraPCMArgs) {} ModuleDependencyInfoStorageBase *clone() const override { return new SwiftSourceModuleDependenciesStorage(*this); } static bool classof(const ModuleDependencyInfoStorageBase *base) { return base->dependencyKind == ModuleDependencyKind::SwiftSource; } }; /// Describes the dependencies of a pre-built Swift module (with no .swiftinterface). /// /// This class is mostly an implementation detail for \c ModuleDependencyInfo. class SwiftBinaryModuleDependencyStorage : public ModuleDependencyInfoStorageBase { public: SwiftBinaryModuleDependencyStorage(const std::string &compiledModulePath, const std::string &moduleDocPath, const std::string &sourceInfoPath, const bool isFramework) : ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftBinary), compiledModulePath(compiledModulePath), moduleDocPath(moduleDocPath), sourceInfoPath(sourceInfoPath), isFramework(isFramework) {} ModuleDependencyInfoStorageBase *clone() const override { return new SwiftBinaryModuleDependencyStorage(*this); } /// The path to the .swiftmodule file. const std::string compiledModulePath; /// The path to the .swiftModuleDoc file. const std::string moduleDocPath; /// The path to the .swiftSourceInfo file. const std::string sourceInfoPath; /// A flag that indicates this dependency is a framework const bool isFramework; static bool classof(const ModuleDependencyInfoStorageBase *base) { return base->dependencyKind == ModuleDependencyKind::SwiftBinary; } }; /// Describes the dependencies of a Clang module. /// /// This class is mostly an implementation detail for \c ModuleDependencyInfo. class ClangModuleDependencyStorage : public ModuleDependencyInfoStorageBase { public: /// Destination output path const std::string pcmOutputPath; /// The module map file used to generate the Clang module. const std::string moduleMapFile; /// The context hash describing the configuration options for this module. const std::string contextHash; /// Partial (Clang) command line that can be used to build this module. const std::vector nonPathCommandLine; /// The file dependencies const std::vector fileDependencies; /// The swift-specific PCM arguments captured by this dependencies object /// as found by the scanning action that discovered it const std::vector capturedPCMArgs; ClangModuleDependencyStorage( const std::string &pcmOutputPath, const std::string &moduleMapFile, const std::string &contextHash, const std::vector &nonPathCommandLine, const std::vector &fileDependencies, const std::vector &capturedPCMArgs ) : ModuleDependencyInfoStorageBase(ModuleDependencyKind::Clang), pcmOutputPath(pcmOutputPath), moduleMapFile(moduleMapFile), contextHash(contextHash), nonPathCommandLine(nonPathCommandLine), fileDependencies(fileDependencies), capturedPCMArgs(capturedPCMArgs) {} ModuleDependencyInfoStorageBase *clone() const override { return new ClangModuleDependencyStorage(*this); } static bool classof(const ModuleDependencyInfoStorageBase *base) { return base->dependencyKind == ModuleDependencyKind::Clang; } }; /// Describes an placeholder Swift module dependency module stub. /// /// This class is mostly an implementation detail for \c ModuleDependencyInfo. class SwiftPlaceholderModuleDependencyStorage : public ModuleDependencyInfoStorageBase { public: SwiftPlaceholderModuleDependencyStorage(const std::string &compiledModulePath, const std::string &moduleDocPath, const std::string &sourceInfoPath) : ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftPlaceholder), compiledModulePath(compiledModulePath), moduleDocPath(moduleDocPath), sourceInfoPath(sourceInfoPath) {} ModuleDependencyInfoStorageBase *clone() const override { return new SwiftPlaceholderModuleDependencyStorage(*this); } /// The path to the .swiftmodule file. const std::string compiledModulePath; /// The path to the .swiftModuleDoc file. const std::string moduleDocPath; /// The path to the .swiftSourceInfo file. const std::string sourceInfoPath; static bool classof(const ModuleDependencyInfoStorageBase *base) { return base->dependencyKind == ModuleDependencyKind::SwiftPlaceholder; } }; // MARK: Module Dependency Info /// Describes the dependencies of a given module. /// /// The dependencies of a module include all of the source files that go /// into that module, as well as any modules that are directly imported /// into the module. class ModuleDependencyInfo { private: std::unique_ptr storage; ModuleDependencyInfo(std::unique_ptr &&storage) : storage(std::move(storage)) { } public: ModuleDependencyInfo() = default; ModuleDependencyInfo(const ModuleDependencyInfo &other) : storage(other.storage->clone()) { } ModuleDependencyInfo(ModuleDependencyInfo &&other) = default; ModuleDependencyInfo &operator=(const ModuleDependencyInfo &other) { storage.reset(other.storage->clone()); return *this; } ModuleDependencyInfo &operator=(ModuleDependencyInfo &&other) = default; /// Describe the module dependencies for a Swift module that can be /// built from a Swift interface file (\c .swiftinterface). static ModuleDependencyInfo forSwiftInterfaceModule( const std::string &moduleOutputPath, const std::string &swiftInterfaceFile, ArrayRef compiledCandidates, ArrayRef buildCommands, ArrayRef extraPCMArgs, StringRef contextHash, bool isFramework) { return ModuleDependencyInfo( std::make_unique( moduleOutputPath, swiftInterfaceFile, compiledCandidates, buildCommands, extraPCMArgs, contextHash, isFramework)); } /// Describe the module dependencies for a serialized or parsed Swift module. static ModuleDependencyInfo forSwiftBinaryModule( const std::string &compiledModulePath, const std::string &moduleDocPath, const std::string &sourceInfoPath, bool isFramework) { return ModuleDependencyInfo( std::make_unique( compiledModulePath, moduleDocPath, sourceInfoPath, isFramework)); } /// Describe the main Swift module. static ModuleDependencyInfo forSwiftSourceModule(ArrayRef extraPCMArgs) { return ModuleDependencyInfo( std::make_unique(extraPCMArgs)); } /// Describe the module dependencies for a Clang module that can be /// built from a module map and headers. static ModuleDependencyInfo forClangModule( const std::string &pcmOutputPath, const std::string &moduleMapFile, const std::string &contextHash, const std::vector &nonPathCommandLine, const std::vector &fileDependencies, const std::vector &capturedPCMArgs) { return ModuleDependencyInfo( std::make_unique( pcmOutputPath, moduleMapFile, contextHash, nonPathCommandLine, fileDependencies, capturedPCMArgs)); } /// Describe a placeholder dependency swift module. static ModuleDependencyInfo forPlaceholderSwiftModuleStub( const std::string &compiledModulePath, const std::string &moduleDocPath, const std::string &sourceInfoPath) { return ModuleDependencyInfo( std::make_unique( compiledModulePath, moduleDocPath, sourceInfoPath)); } /// Retrieve the module-level dependencies. ArrayRef getModuleDependencies() const { return storage->moduleDependencies; } /// Whether the dependencies are for a Swift module: either Textual, Source, Binary, or Placeholder. bool isSwiftModule() const; /// Whether the dependencies are for a textual Swift module. bool isSwiftInterfaceModule() const; /// Whether the dependencies are for a textual Swift module. bool isSwiftSourceModule() const; /// Whether the dependencies are for a binary Swift module. bool isSwiftBinaryModule() const; /// Whether this represents a placeholder module stub bool isSwiftPlaceholderModule() const; /// Whether the dependencies are for a Clang module. bool isClangModule() const; ModuleDependencyKind getKind() const { return storage->dependencyKind; } /// Retrieve the dependencies for a Swift textual-interface module. const SwiftInterfaceModuleDependenciesStorage *getAsSwiftInterfaceModule() const; /// Retrieve the dependencies for a Swift module. const SwiftSourceModuleDependenciesStorage *getAsSwiftSourceModule() const; /// Retrieve the dependencies for a binary Swift module. const SwiftBinaryModuleDependencyStorage *getAsSwiftBinaryModule() const; /// Retrieve the dependencies for a Clang module. const ClangModuleDependencyStorage *getAsClangModule() const; /// Retrieve the dependencies for a placeholder dependency module stub. const SwiftPlaceholderModuleDependencyStorage * getAsPlaceholderDependencyModule() const; /// Add a dependency on the given module, if it was not already in the set. void addModuleDependency(StringRef module, llvm::StringSet<> *alreadyAddedModules = nullptr); /// Add a dependency on the given module, if it was not already in the set. void addModuleDependency(ImportPath::Module module, llvm::StringSet<> *alreadyAddedModules = nullptr) { addModuleDependency(module.front().Item.str(), alreadyAddedModules); } /// Add all of the module dependencies for the imports in the given source /// file to the set of module dependencies. void addModuleDependencies(const SourceFile &sf, llvm::StringSet<> &alreadyAddedModules); /// Get the bridging header. Optional getBridgingHeader() const; /// Add a bridging header to a Swift module's dependencies. void addBridgingHeader(StringRef bridgingHeader); /// Add source files void addSourceFile(StringRef sourceFile); /// Add source files that the bridging header depends on. void addBridgingSourceFile(StringRef bridgingSourceFile); /// Add (Clang) module on which the bridging header depends. void addBridgingModuleDependency(StringRef module, llvm::StringSet<> &alreadyAddedModules); /// Collect a map from a secondary module name to a list of cross-import /// overlays, when this current module serves as the primary module. llvm::StringMap> collectCrossImportOverlayNames(ASTContext &ctx, StringRef moduleName); }; using ModuleDependencyID = std::pair; using ModuleDependenciesVector = llvm::SmallVector; using ModuleNameToDependencyMap = llvm::StringMap; using ModuleDependenciesKindMap = std::unordered_map; using ModuleDependenciesKindRefMap = std::unordered_map, ModuleDependencyKindHash>; // MARK: SwiftDependencyScanningService /// A carrier of state shared among possibly multiple invocations of the dependency /// scanner. Acts as a global cache of discovered module dependencies and /// filesystem state. It is not to be queried directly, but is rather /// meant to be wrapped in an instance of `ModuleDependenciesCache`, responsible /// for recording new dependencies and answering cache queries in a given scan. class SwiftDependencyScanningService { /// Global cache contents specific to a specific scanner invocation context struct ContextSpecificGlobalCacheState { /// All cached module dependencies, in the order in which they were /// encountered. std::vector AllModules; /// Dependencies for modules that have already been computed. /// This maps a dependency kind to a map of a module's name to a Dependency /// object ModuleDependenciesKindMap ModuleDependenciesMap; }; /// The persistent Clang dependency scanner service clang::tooling::dependencies::DependencyScanningService ClangScanningService; /// The global file system cache. Optional< clang::tooling::dependencies::DependencyScanningFilesystemSharedCache> SharedFilesystemCache; /// All cached Swift source module dependencies, in the order in which they /// were encountered std::vector AllSourceModules; /// Dependencies for all Swift source-based modules discovered. Each one is /// the main module of a prior invocation of the scanner. ModuleNameToDependencyMap SwiftSourceModuleDependenciesMap; /// A map from a String representing the target triple of a scanner invocation /// to the corresponding cached dependencies discovered so far when using this /// triple. llvm::StringMap> ContextSpecificCacheMap; /// The current context hash configuration Optional CurrentContextHash; /// The context hashes used by scanners using this cache, in the order in /// which they were used std::vector AllContextHashes; /// Retrieve the dependencies map that corresponds to the given dependency /// kind. ModuleNameToDependencyMap &getDependenciesMap(ModuleDependencyKind kind); const ModuleNameToDependencyMap & getDependenciesMap(ModuleDependencyKind kind) const; public: SwiftDependencyScanningService(); SwiftDependencyScanningService(const SwiftDependencyScanningService &) = delete; SwiftDependencyScanningService & operator=(const SwiftDependencyScanningService &) = delete; virtual ~SwiftDependencyScanningService() {} /// Query the service's filesystem cache clang::tooling::dependencies::DependencyScanningFilesystemSharedCache &getSharedCache() { assert(SharedFilesystemCache && "Expected a shared cache"); return *SharedFilesystemCache; } /// Query the service's filesystem cache clang::tooling::dependencies::DependencyScanningFilesystemSharedCache & getSharedFilesystemCache() { assert(SharedFilesystemCache && "Expected a shared cache"); return *SharedFilesystemCache; } /// Wrap the filesystem on the specified `CompilerInstance` with a /// caching `DependencyScanningWorkerFilesystem` void overlaySharedFilesystemCacheForCompilation(CompilerInstance &Instance); private: /// Enforce clients not being allowed to query this cache directly, it must be /// wrapped in an instance of `ModuleDependenciesCache`. friend class ModuleDependenciesCache; friend class ModuleDependenciesCacheDeserializer; friend class ModuleDependenciesCacheSerializer; friend class DependencyScanningTool; /// Configure the current state of the cache to respond to queries /// for the specified scanning context hash. void configureForContextHash(std::string scanningContextHash); /// Return context hashes of all scanner invocations that have used /// this cache instance. const std::vector &getAllContextHashes() const { return AllContextHashes; } /// Whether we have cached dependency information for the given module. bool hasDependencies(StringRef moduleName, Optional kind) const; /// Return a pointer to the context-specific cache state of the current /// scanning action. ContextSpecificGlobalCacheState *getCurrentCache() const; /// Return a pointer to the cache state of the specified context hash. ContextSpecificGlobalCacheState * getCacheForScanningContextHash(StringRef scanningContextHash) const; /// Look for source-based module dependency details Optional findSourceModuleDependency(StringRef moduleName) const; /// Look for module dependencies for a module with the given name /// /// \returns the cached result, or \c None if there is no cached entry. Optional findDependencies(StringRef moduleName, Optional kind) const; /// Record dependencies for the given module. const ModuleDependencyInfo *recordDependencies(StringRef moduleName, ModuleDependencyInfo dependencies); /// Update stored dependencies for the given module. const ModuleDependencyInfo *updateDependencies(ModuleDependencyID moduleID, ModuleDependencyInfo dependencies); /// Reference the list of all module dependencies that are not source-based /// modules (i.e. interface dependencies, binary dependencies, clang /// dependencies). const std::vector & getAllNonSourceModules(StringRef scanningContextHash) const { auto contextSpecificCache = getCacheForScanningContextHash(scanningContextHash); return contextSpecificCache->AllModules; } /// Return the list of all source-based modules discovered by this cache const std::vector &getAllSourceModules() const { return AllSourceModules; } }; // MARK: ModuleDependenciesCache /// This "local" dependencies cache persists only for the duration of a given /// scanning action, and wraps an instance of a `SwiftDependencyScanningService` /// which may carry cached scanning information from prior scanning actions. /// This cache maintains a store of references to all dependencies found within /// the current scanning action (with their values stored in the global Cache). class ModuleDependenciesCache { private: SwiftDependencyScanningService &globalScanningService; /// References to data in `globalCache` for Swift dependencies and /// `clangModuleDependencies` for Clang dependencies accimulated during /// the current scanning action. ModuleDependenciesKindRefMap ModuleDependenciesMap; /// Name of the module under scan std::string mainScanModuleName; /// The context hash of the current scanning invocation std::string scannerContextHash; /// Set containing all of the Clang modules that have already been seen. llvm::StringSet<> alreadySeenClangModules; /// The Clang dependency scanner tool clang::tooling::dependencies::DependencyScanningTool clangScanningTool; /// Discovered Clang modules are only cached locally. llvm::StringMap clangModuleDependencies; /// Retrieve the dependencies map that corresponds to the given dependency /// kind. llvm::StringMap & getDependencyReferencesMap(ModuleDependencyKind kind); const llvm::StringMap & getDependencyReferencesMap(ModuleDependencyKind kind) const; /// Local cache results lookup, only for modules which were discovered during /// the current scanner invocation. bool hasDependenciesLocalOnly(StringRef moduleName, Optional kind) const; /// Local cache results lookup, only for modules which were discovered during /// the current scanner invocation. Optional findDependenciesLocalOnly(StringRef moduleName, Optional kind) const; public: ModuleDependenciesCache(SwiftDependencyScanningService &globalScanningService, std::string mainScanModuleName, std::string scanningContextHash); ModuleDependenciesCache(const ModuleDependenciesCache &) = delete; ModuleDependenciesCache &operator=(const ModuleDependenciesCache &) = delete; public: /// Whether we have cached dependency information for the given module. bool hasDependencies(StringRef moduleName, Optional kind) const; /// Produce a reference to the Clang scanner tool associated with this cache clang::tooling::dependencies::DependencyScanningTool& getClangScannerTool() { return clangScanningTool; } llvm::StringSet<>& getAlreadySeenClangModules() { return alreadySeenClangModules; } /// Look for module dependencies for a module with the given name /// /// \returns the cached result, or \c None if there is no cached entry. Optional findDependencies(StringRef moduleName, Optional kind) const; /// Record dependencies for the given module. void recordDependencies(StringRef moduleName, ModuleDependencyInfo dependencies); /// Update stored dependencies for the given module. void updateDependencies(ModuleDependencyID moduleID, ModuleDependencyInfo dependencies); const std::vector &getAllSourceModules() const { return globalScanningService.getAllSourceModules(); } StringRef getMainModuleName() const { return mainScanModuleName; } }; } // namespace swift #endif /* SWIFT_AST_MODULE_DEPENDENCIES_H */