Files
swift-mirror/include/swift/AST/SearchPathOptions.h
Adrian Prantl c91211a5d2 Serialize explicit module dependencies in swift module files
For clients, such as the debugger, who do not have access the full
output of the dependency scanner, it is a huger performance and
correctness improvement if each explicitly built Swift module not just
serialized all its Clang .pcm dependencies (via the serialized Clang
compiler invocation) but also its direct Swift module dependencies.

This patch changes the Swift module format to store the absolute path
or cas cache key for each dependency in the INPUT block, and makes
sure the deserialization makes these available to the ESML.

rdar://150969755
2025-10-21 09:08:58 -07:00

624 lines
22 KiB
C++

//===--- SearchPathOptions.h ------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_SEARCHPATHOPTIONS_H
#define SWIFT_AST_SEARCHPATHOPTIONS_H
#include "swift/Basic/ArrayRefView.h"
#include "swift/Basic/ExternalUnion.h"
#include "swift/Basic/PathRemapper.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/VersionTuple.h"
#include "llvm/Support/VirtualFileSystem.h"
#include <optional>
#include <string>
#include <vector>
namespace swift {
/// The kind of a module search path. The order of this enum is important
/// because import search paths should be considered before framework search
/// paths etc.
enum class ModuleSearchPathKind {
Import,
Framework,
ImplicitFramework,
RuntimeLibrary,
};
/// Specifies how to load modules when both a module interface and serialized
/// AST are present, or whether to disallow one format or the other altogether.
enum class ModuleLoadingMode {
PreferInterface,
PreferSerialized,
OnlyInterface,
OnlySerialized,
};
/// A single module search path that can come from different sources, e.g.
/// framework search paths, import search path etc.
class ModuleSearchPath : public llvm::RefCountedBase<ModuleSearchPath> {
/// The actual path of the module search path.
std::string Path;
/// The kind of the search path.
ModuleSearchPathKind Kind;
bool IsSystem;
/// An index that describes the order this search path should be considered
/// in within its \c ModuleSearchPathKind. This allows us to reconstruct the
/// user-defined search path order when merging search paths containing
/// different file names in \c searchPathsContainingFile.
unsigned Index;
public:
ModuleSearchPath(StringRef Path, ModuleSearchPathKind Kind, bool IsSystem,
unsigned Index)
: Path(Path), Kind(Kind), IsSystem(IsSystem), Index(Index) {}
StringRef getPath() const { return Path; }
ModuleSearchPathKind getKind() const { return Kind; }
bool isSystem() const { return IsSystem; }
unsigned getIndex() const { return Index; }
bool operator<(const ModuleSearchPath &Other) const {
if (this->Kind == Other.Kind) {
return this->Index < Other.Index;
} else {
return this->Kind < Other.Kind;
}
}
};
using ModuleSearchPathPtr = llvm::IntrusiveRefCntPtr<ModuleSearchPath>;
class SearchPathOptions;
/// Maintains a mapping of filenames to search paths that contain a file with
/// this name (non-recursively). E.g. if we have a directory structure as
/// follows.
///
/// \code
/// searchPath1/
/// Module1.framework
///
/// searchPath2/
/// Module1.framework
/// Module2.swiftmodule
/// \endcode
///
/// We have the following lookup table
///
/// \code
/// Module1.framework -> [searchPath1, searchPath2]
/// Module2.swiftmodule -> [searchPath2]
/// \endcode
///
/// When searching for a module this allows an efficient search of only those
/// search paths that are relevant. In a naive implementation, we would need
/// to scan all search paths for every module we import.
class ModuleSearchPathLookup {
/// Parameters for which the \c LookupTable has been built. If one if these
/// changes, the lookup table needs to be rebuilt. It is not expected that any
/// of these change frequently.
struct {
llvm::vfs::FileSystem *FileSystem;
bool IsOSDarwin;
bool IsPopulated;
const SearchPathOptions *Opts;
} State;
llvm::StringMap<SmallVector<ModuleSearchPathPtr, 4>> LookupTable;
/// Scan the directory at \p SearchPath for files and add those files to the
/// lookup table. \p Kind specifies the search path kind and \p Index the
/// index of \p SearchPath within that search path kind. Search paths with
/// lower indices are considered first.
/// The \p SearchPath is stored by as a \c StringRef, so the string backing it
/// must be alive as long as this lookup table is alive and not cleared.
void addFilesInPathToLookupTable(llvm::vfs::FileSystem *FS,
StringRef SearchPath,
ModuleSearchPathKind Kind, bool IsSystem,
unsigned Index);
/// Discard the current lookup table and rebuild a new one.
void rebuildLookupTable(const SearchPathOptions *Opts,
llvm::vfs::FileSystem *FS, bool IsOsDarwin);
/// Discard the current lookup table.
void clearLookupTable() {
LookupTable.clear();
State.IsPopulated = false;
State.FileSystem = nullptr;
State.IsOSDarwin = false;
State.Opts = nullptr;
}
public:
/// Called by \p SearchPathOptions when search paths indexed by this \c
/// SearchPathLookup have changed in an unknown way. Causes the lookup table
/// to be rebuilt at the next request.
void searchPathsDidChange() { clearLookupTable(); }
/// Called by \p SearchPathOptions when an import or framework search path has
/// been added.
/// \p Index is the index of the search path within its kind and is used to
/// make sure this search path is considered last (within its kind).
void searchPathAdded(llvm::vfs::FileSystem *FS, StringRef SearchPath,
ModuleSearchPathKind Kind, bool IsSystem,
unsigned Index) {
if (!State.IsPopulated) {
// If the lookup table hasn't been built yet, we will scan the search path
// once the lookup table is requested. Nothing to do yet.
return;
}
if (State.FileSystem != FS) {
// We would be using a different file system to augment the lookup table
// than we initially used to build it. Discard everything to be safe.
clearLookupTable();
return;
}
addFilesInPathToLookupTable(FS, SearchPath, Kind, IsSystem, Index);
}
/// Returns all search paths that non-recursively contain a file whose name
/// is in \p Filenames.
SmallVector<const ModuleSearchPath *, 4>
searchPathsContainingFile(const SearchPathOptions *Opts,
llvm::ArrayRef<std::string> Filenames,
llvm::vfs::FileSystem *FS, bool IsOSDarwin);
};
/// Pair of a plugin path and the module name that the plugin provides.
struct PluginExecutablePathAndModuleNames {
std::string ExecutablePath;
std::vector<std::string> ModuleNames;
};
/// Pair of a plugin search path and the corresponding plugin server executable
/// path.
struct ExternalPluginSearchPathAndServerPath {
std::string SearchPath;
std::string ServerPath;
};
class PluginSearchOption {
public:
struct LoadPluginLibrary {
std::string LibraryPath;
};
struct LoadPluginExecutable {
std::string ExecutablePath;
std::vector<std::string> ModuleNames;
};
struct PluginPath {
std::string SearchPath;
};
struct ExternalPluginPath {
std::string SearchPath;
std::string ServerPath;
};
struct ResolvedPluginConfig {
std::string LibraryPath;
std::string ExecutablePath;
std::vector<std::string> ModuleNames;
};
enum class Kind : uint8_t {
LoadPluginLibrary,
LoadPluginExecutable,
PluginPath,
ExternalPluginPath,
ResolvedPluginConfig,
};
private:
using Members =
ExternalUnionMembers<LoadPluginLibrary, LoadPluginExecutable, PluginPath,
ExternalPluginPath, ResolvedPluginConfig>;
static Members::Index getIndexForKind(Kind kind) {
switch (kind) {
case Kind::LoadPluginLibrary:
return Members::indexOf<LoadPluginLibrary>();
case Kind::LoadPluginExecutable:
return Members::indexOf<LoadPluginExecutable>();
case Kind::PluginPath:
return Members::indexOf<PluginPath>();
case Kind::ExternalPluginPath:
return Members::indexOf<ExternalPluginPath>();
case Kind::ResolvedPluginConfig:
return Members::indexOf<ResolvedPluginConfig>();
}
};
using Storage = ExternalUnion<Kind, Members, getIndexForKind>;
Kind kind;
Storage storage;
public:
PluginSearchOption(const LoadPluginLibrary &v)
: kind(Kind::LoadPluginLibrary) {
storage.emplace<LoadPluginLibrary>(kind, v);
}
PluginSearchOption(const LoadPluginExecutable &v)
: kind(Kind::LoadPluginExecutable) {
storage.emplace<LoadPluginExecutable>(kind, v);
}
PluginSearchOption(const PluginPath &v) : kind(Kind::PluginPath) {
storage.emplace<PluginPath>(kind, v);
}
PluginSearchOption(const ExternalPluginPath &v)
: kind(Kind::ExternalPluginPath) {
storage.emplace<ExternalPluginPath>(kind, v);
}
PluginSearchOption(const ResolvedPluginConfig &v)
: kind(Kind::ResolvedPluginConfig) {
storage.emplace<ResolvedPluginConfig>(kind, v);
}
PluginSearchOption(const PluginSearchOption &o) : kind(o.kind) {
storage.copyConstruct(o.kind, o.storage);
}
PluginSearchOption(PluginSearchOption &&o) : kind(o.kind) {
storage.moveConstruct(o.kind, std::move(o.storage));
}
~PluginSearchOption() { storage.destruct(kind); }
PluginSearchOption &operator=(const PluginSearchOption &o) {
storage.copyAssign(kind, o.kind, o.storage);
kind = o.kind;
return *this;
}
PluginSearchOption &operator=(PluginSearchOption &&o) {
storage.moveAssign(kind, o.kind, std::move(o.storage));
kind = o.kind;
return *this;
}
Kind getKind() const { return kind; }
template <typename T>
const T *dyn_cast() const {
if (Members::indexOf<T>() != getIndexForKind(kind))
return nullptr;
return &storage.get<T>(kind);
}
template <typename T>
const T &get() const {
return storage.get<T>(kind);
}
};
/// Options for controlling search path behavior.
class SearchPathOptions {
/// To call \c addImportSearchPath and \c addFrameworkSearchPath from
/// \c ASTContext::addSearchPath.
friend class ASTContext;
public:
struct SearchPath {
std::string Path;
bool IsSystem = false;
SearchPath(StringRef path, bool isSystem)
: Path(path), IsSystem(isSystem) {}
friend bool operator==(const SearchPath &LHS, const SearchPath &RHS) {
return LHS.Path == RHS.Path && LHS.IsSystem == RHS.IsSystem;
}
friend bool operator!=(const SearchPath &LHS, const SearchPath &RHS) {
return !(LHS == RHS);
}
friend llvm::hash_code
hash_value(const SearchPath &searchPath) {
return llvm::hash_combine(searchPath.Path, searchPath.IsSystem);
}
};
private:
ModuleSearchPathLookup Lookup;
/// Path to the SDK which is being built against.
///
/// Must be modified through setter to keep \c Lookup in sync.
std::string SDKPath;
/// Path(s) which should be searched for modules.
///
/// Must be modified through setter to keep \c Lookup in sync.
std::vector<SearchPath> ImportSearchPaths;
/// Path(s) which should be searched for frameworks.
///
/// Must be modified through setter to keep \c Lookup in sync.
std::vector<SearchPath> FrameworkSearchPaths;
/// Paths to search for stdlib modules. One of these will be
/// compiler-relative.
///
/// Must be modified through setter to keep \c Lookup in sync.
std::vector<std::string> RuntimeLibraryImportPaths;
/// When on Darwin the framework paths that are implicitly imported.
/// $SDKROOT/System/Library/Frameworks/ and $SDKROOT/Library/Frameworks/.
///
/// Must be modified through setter to keep \c Lookup in sync.
std::vector<std::string> ImplicitFrameworkSearchPaths;
/// Compiler plugin library search paths.
std::vector<std::string> CompilerPluginLibraryPaths;
/// Compiler plugin executable paths and providing module names.
std::vector<PluginExecutablePathAndModuleNames> CompilerPluginExecutablePaths;
/// Add a single import search path. Must only be called from
/// \c ASTContext::addSearchPath.
void addImportSearchPath(SearchPath Path, llvm::vfs::FileSystem *FS) {
ImportSearchPaths.push_back(Path);
Lookup.searchPathAdded(FS, ImportSearchPaths.back().Path,
ModuleSearchPathKind::Import, Path.IsSystem,
ImportSearchPaths.size() - 1);
}
/// Add a single framework search path. Must only be called from
/// \c ASTContext::addSearchPath.
void addFrameworkSearchPath(SearchPath NewPath, llvm::vfs::FileSystem *FS) {
FrameworkSearchPaths.push_back(NewPath);
Lookup.searchPathAdded(FS, FrameworkSearchPaths.back().Path,
ModuleSearchPathKind::Framework, NewPath.IsSystem,
FrameworkSearchPaths.size() - 1);
}
std::optional<std::string> WinSDKRoot = std::nullopt;
std::optional<std::string> WinSDKVersion = std::nullopt;
std::optional<std::string> VCToolsRoot = std::nullopt;
std::optional<std::string> VCToolsVersion = std::nullopt;
std::optional<StringRef> SysRoot = std::nullopt;
mutable std::optional<std::string> SDKPlatformPath = std::nullopt;
public:
StringRef getSDKPath() const { return SDKPath; }
void setSDKPath(std::string NewSDKPath) {
SDKPath = NewSDKPath;
}
/// Retrieves the corresponding parent platform path for the SDK, or
/// \c nullopt if there isn't one.
/// NOTE: This computes and caches the result, and as such will not respect
/// a different FileSystem being passed later.
std::optional<StringRef> getSDKPlatformPath(llvm::vfs::FileSystem *FS) const;
std::optional<StringRef> getWinSDKRoot() const { return WinSDKRoot; }
void setWinSDKRoot(StringRef root) {
WinSDKRoot = root;
}
std::optional<StringRef> getWinSDKVersion() const { return WinSDKVersion; }
void setWinSDKVersion(StringRef version) {
WinSDKVersion = version;
}
std::optional<StringRef> getVCToolsRoot() const { return VCToolsRoot; }
void setVCToolsRoot(StringRef root) {
VCToolsRoot = root;
}
std::optional<StringRef> getVCToolsVersion() const { return VCToolsVersion; }
void setVCToolsVersion(StringRef version) {
VCToolsVersion = version;
}
std::optional<StringRef> getSysRoot() const { return SysRoot; }
void setSysRoot(StringRef sysroot) {
SysRoot = sysroot;
}
ArrayRef<SearchPath> getImportSearchPaths() const {
return ImportSearchPaths;
}
void setImportSearchPaths(std::vector<SearchPath> NewImportSearchPaths) {
ImportSearchPaths = NewImportSearchPaths;
Lookup.searchPathsDidChange();
}
ArrayRef<SearchPath> getFrameworkSearchPaths() const {
return FrameworkSearchPaths;
}
void
setFrameworkSearchPaths(std::vector<SearchPath> NewFrameworkSearchPaths) {
FrameworkSearchPaths = NewFrameworkSearchPaths;
Lookup.searchPathsDidChange();
}
/// The extra implicit framework search paths on Apple platforms:
/// $SDKROOT/System/Library/Frameworks/ and $SDKROOT/Library/Frameworks/.
ArrayRef<std::string> getImplicitFrameworkSearchPaths() const {
return ImplicitFrameworkSearchPaths;
}
void setImplicitFrameworkSearchPaths(
std::vector<std::string> NewImplicitFrameworkSearchPaths) {
ImplicitFrameworkSearchPaths = NewImplicitFrameworkSearchPaths;
Lookup.searchPathsDidChange();
}
ArrayRef<std::string> getRuntimeLibraryImportPaths() const {
return RuntimeLibraryImportPaths;
}
void setRuntimeLibraryImportPaths(
std::vector<std::string> NewRuntimeLibraryImportPaths) {
RuntimeLibraryImportPaths = NewRuntimeLibraryImportPaths;
Lookup.searchPathsDidChange();
}
/// Path(s) to virtual filesystem overlay YAML files.
std::vector<std::string> VFSOverlayFiles;
/// Path(s) which should be searched for libraries.
///
/// This is used in immediate modes. It is safe to add paths to this directly.
std::vector<std::string> LibrarySearchPaths;
/// Path to search for compiler-relative header files.
std::string RuntimeResourcePath;
/// Paths to search for compiler-relative stdlib dylibs, in order of
/// preference.
std::vector<std::string> RuntimeLibraryPaths;
/// Plugin search path options.
std::vector<PluginSearchOption> PluginSearchOpts;
/// Path to in-process plugin server shared library.
std::string InProcessPluginServerPath;
/// Don't automatically add any import paths.
bool SkipAllImplicitImportPaths = false;
/// Don't automatically add any import paths from the SDK.
bool SkipSDKImportPaths = false;
/// Scanner Prefix Mapper.
std::vector<std::pair<std::string, std::string>> ScannerPrefixMapper;
/// Verify resolved plugin is not changed.
bool ResolvedPluginVerification = false;
/// When set, don't validate module system dependencies.
///
/// If a system header is modified and this is not set, the compiler will
/// rebuild PCMs and compiled swiftmodules that depend on them, just like it
/// would for a non-system header.
bool DisableModulesValidateSystemDependencies = false;
/// A set of compiled modules that may be ready to use.
std::vector<std::string> CandidateCompiledModules;
/// A map of explicit Swift module information.
std::string ExplicitSwiftModuleMapPath;
/// Module inputs specified with -swift-module-input,
/// ModuleName: Path to .swiftmodule file
llvm::StringMap<std::string> ExplicitSwiftModuleInputs;
/// A file containing a list of protocols whose conformances require const value extraction.
std::string ConstGatherProtocolListFilePath;
/// Path to the file that defines platform mapping for availability
/// version inheritance.
std::optional<std::string> PlatformAvailabilityInheritanceMapPath;
/// Cross import module information. Map from module name to the list of cross
/// import overlay files that associate with that module.
using CrossImportMap = llvm::StringMap<std::vector<std::string>>;
CrossImportMap CrossImportInfo;
/// CanImport information passed from scanning.
struct CanImportInfo {
std::string ModuleName;
llvm::VersionTuple Version;
llvm::VersionTuple UnderlyingVersion;
};
std::vector<CanImportInfo> CanImportModuleInfo;
/// Whether to search for cross import overlay on file system.
bool DisableCrossImportOverlaySearch = false;
/// Debug path mappings to apply to serialized search paths. These are
/// specified in LLDB from the target.source-map entries.
PathRemapper SearchPathRemapper;
/// Recover the search paths deserialized from .swiftmodule files to their
/// original form.
PathObfuscator DeserializedPathRecoverer;
/// Specify the module loading behavior of the compilation.
ModuleLoadingMode ModuleLoadMode = ModuleLoadingMode::PreferSerialized;
/// New scanner search behavior. Validate up-to-date existing Swift module
/// dependencies in the scanner itself.
bool ScannerModuleValidation = false;
/// Whether this compilation should attempt to resolve in-package
/// imports of its module dependencies.
///
/// Source compilation and 'package' textual interface compilation both
/// require that package-only imports of module dependencies be resolved.
/// Otherwise, compilation of non-package textual interfaces, even if
/// "in-package", must not require package-only module dependencies.
bool ResolveInPackageModuleDependencies = false;
/// Enable auto bridging header chaining.
bool BridgingHeaderChaining = false;
/// Return all module search paths that (non-recursively) contain a file whose
/// name is in \p Filenames.
SmallVector<const ModuleSearchPath *, 4>
moduleSearchPathsContainingFile(llvm::ArrayRef<std::string> Filenames,
llvm::vfs::FileSystem *FS, bool IsOSDarwin) {
return Lookup.searchPathsContainingFile(this, Filenames, FS, IsOSDarwin);
}
/// Creates a filesystem taking into account any overlays specified in
/// \c VFSOverlayFiles. Returns \p BaseFS if there were no overlays and
/// \c FileError(s) if any error occurred while attempting to parse the
/// overlay files.
llvm::Expected<llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>>
makeOverlayFileSystem(
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) const;
public:
/// Return a hash code of any components from these options that should
/// contribute to a Swift Bridging PCH hash.
llvm::hash_code getPCHHashComponents() const {
using llvm::hash_combine;
using llvm::hash_combine_range;
return hash_combine(SDKPath,
hash_combine_range(ImportSearchPaths.begin(), ImportSearchPaths.end()),
hash_combine_range(VFSOverlayFiles.begin(), VFSOverlayFiles.end()),
hash_combine_range(FrameworkSearchPaths.begin(),
FrameworkSearchPaths.end()),
hash_combine_range(LibrarySearchPaths.begin(),
LibrarySearchPaths.end()),
RuntimeResourcePath,
hash_combine_range(RuntimeLibraryImportPaths.begin(),
RuntimeLibraryImportPaths.end()),
hash_combine_range(ImplicitFrameworkSearchPaths.begin(),
ImplicitFrameworkSearchPaths.end()),
DisableModulesValidateSystemDependencies,
ScannerModuleValidation,
ModuleLoadMode);
}
/// Return a hash code of any components from these options that should
/// contribute to a Swift Dependency Scanning hash.
llvm::hash_code getModuleScanningHashComponents() const {
return getPCHHashComponents();
}
void dump(bool isDarwin) const;
};
}
#endif