Files
swift-mirror/include/swift/AST/SearchPathOptions.h
Steven Wu 9d59044bb1 [BrdigingHeader] Auto bridging header chaining
Add ability to automatically chaining the bridging headers discovered from all
dependencies module when doing swift caching build. This will eliminate all
implicit bridging header imports from the build and make the bridging header
importing behavior much more reliable, while keep the compatibility at maximum.

For example, if the current module A depends on module B and C, and both B and
C are binary modules that uses bridging header, when building module A,
dependency scanner will construct a new header that chains three bridging
headers together with the option to build a PCH from it. This will make all
importing errors more obvious while improving the performance.
2025-02-05 09:41:04 -08:00

644 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,
DarwinImplicitFramework,
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);
}
};
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/.
///
/// On non-Darwin platforms these are populated, but ignored.
///
/// Computed when the SDK path is set and cached so we can reference the
/// Darwin implicit framework search paths as \c StringRef from
/// \c ModuleSearchPath.
std::vector<std::string> DarwinImplicitFrameworkSearchPaths;
/// 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;
// Compute Darwin implicit framework search paths.
SmallString<128> systemFrameworksScratch(NewSDKPath);
llvm::sys::path::append(systemFrameworksScratch, "System", "Library",
"Frameworks");
SmallString<128> systemSubFrameworksScratch(NewSDKPath);
llvm::sys::path::append(systemSubFrameworksScratch, "System", "Library",
"SubFrameworks");
SmallString<128> frameworksScratch(NewSDKPath);
llvm::sys::path::append(frameworksScratch, "Library", "Frameworks");
DarwinImplicitFrameworkSearchPaths = {systemFrameworksScratch.str().str(),
systemSubFrameworksScratch.str().str(),
frameworksScratch.str().str()};
Lookup.searchPathsDidChange();
}
/// 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> getDarwinImplicitFrameworkSearchPaths() const {
return DarwinImplicitFrameworkSearchPaths;
}
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 look in for compiler-provided modules.
bool SkipRuntimeLibraryImportPaths = false;
/// Don't include SDK paths in the RuntimeLibraryImportPaths
bool ExcludeSDKPathsFromRuntimeLibraryImportPaths = false;
/// Scanner Prefix Mapper.
std::vector<std::string> ScannerPrefixMapper;
/// 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>
std::vector<std::pair<std::string, std::string>> ExplicitSwiftModuleInputs;
/// A map of placeholder Swift module dependency information.
std::string PlaceholderDependencyModuleMap;
/// 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;
private:
static StringRef pathStringFromSearchPath(const SearchPath &next) {
return next.Path;
};
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;
using SearchPathView =
ArrayRefView<SearchPath, StringRef, pathStringFromSearchPath>;
SearchPathView importPathsOnly{ImportSearchPaths};
SearchPathView frameworkPathsOnly{FrameworkSearchPaths};
return hash_combine(SDKPath,
// FIXME: Should we include the system-ness of
// search paths too?
hash_combine_range(importPathsOnly.begin(), importPathsOnly.end()),
hash_combine_range(VFSOverlayFiles.begin(), VFSOverlayFiles.end()),
hash_combine_range(frameworkPathsOnly.begin(),
frameworkPathsOnly.end()),
hash_combine_range(LibrarySearchPaths.begin(),
LibrarySearchPaths.end()),
RuntimeResourcePath,
hash_combine_range(RuntimeLibraryImportPaths.begin(),
RuntimeLibraryImportPaths.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