mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This refactoring allows us to drop ModuleInterfaceLoader when explicit modules are enabled. Before this change, the dependencies scanner needs the loader to be present to access functionalities like collecting prebuilt module candidates.
465 lines
21 KiB
C++
465 lines
21 KiB
C++
//===-------- ModuleInterfaceLoader.h - Loads .swiftinterface files -------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2019 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file This implements the logic for loading and building module
|
|
/// interfaces.
|
|
///
|
|
/// === Loading Module Interfaces ===
|
|
///
|
|
/// If there is a .swiftinterface file corresponding to a given module name
|
|
/// present in the frontend's search paths, then this module loader will look in
|
|
/// the following places for a module:
|
|
///
|
|
/// - First, look in the module cache (specified by -module-cache-path)
|
|
/// - We check here first because an existing .swiftmodule might be
|
|
/// out-of-date, necessitating a rebuild. If a cached module is out-of-date,
|
|
/// it's simply rebuilt.
|
|
/// - Next, look adjacent to the .swiftinterface. If we find a module that's
|
|
/// either loadable by this compiler, valid, and up-to-date, or totally
|
|
/// unreadable, then delegate to the serialized module loader to either load
|
|
/// or diagnose it.
|
|
/// - Finally, look in the prebuilt module cache (specified
|
|
/// by -prebuilt-module-cache-path)
|
|
///
|
|
/// If we can't find an appropriate module to load, we can always fall back and
|
|
/// recompile the .swiftinterface file.
|
|
///
|
|
/// === Dependency Checking ===
|
|
///
|
|
/// Cached modules keep track of their dependencies' last modification time and
|
|
/// file size. This means that checking if a module is up-to-date requires
|
|
/// `stat`ing the dependencies and comparing the results from the filesystem
|
|
/// with the results in the module.
|
|
///
|
|
/// Prebuilt modules, on the other hand, won't have a reliable modification
|
|
/// time, as their dependencies live in the SDK. Prebuilt modules will instead
|
|
/// keep track of the size and content hash of their dependencies.
|
|
/// In order to avoid constantly re-hashing the dependencies, however, we will
|
|
/// install a "forwarding module" in the regular cache.
|
|
/// This "forwarding module"
|
|
/// - Points to the prebuilt module on disk, and
|
|
/// - Lists modification times from the last time we verified the content
|
|
///
|
|
/// So, to recap, there are 4 kinds of modules:
|
|
/// ┌───────────────────────────────┐
|
|
/// │ ┌───┐ ┌───┐ │
|
|
/// │ │ I │ │ M │ │
|
|
/// │ └───┘ └───┘ │
|
|
/// │ .swiftinterface .swiftmodule │
|
|
/// │ ┌───┐ ┌───┐ │
|
|
/// │ │ P │ │ F │ │
|
|
/// │ └───┘ └───┘ │
|
|
/// │ Prebuilt Forwarding │
|
|
/// │ .swiftmodule .swiftmodule │
|
|
/// └───────────────────────────────┘
|
|
///
|
|
/// - Prebuilt modules have hash-based dependencies, cached modules have
|
|
/// mod-time-based dependencies
|
|
/// - Forwarding modules point to prebuilt modules and augment them with
|
|
/// modification times
|
|
///
|
|
/// === Example Cache ===
|
|
///
|
|
/// Here's an example of what's in a prebuilt cache or module cache.
|
|
///
|
|
/// Say there are 4 frameworks, each exposing a .swiftinterface file.
|
|
/// Then, we pre-build 2 of those frameworks and put them in the prebuilt cache.
|
|
/// Finally, we import all 4 of those frameworks while building a project.
|
|
///
|
|
/// For the 2 frameworks with modules in the prebuilt cache, we'll have
|
|
/// installed 2 forwarding modules. For the other 2 frameworks, we'll have
|
|
/// compiled the interfaces and put them in the module cache.
|
|
///
|
|
/// ┌─────┐
|
|
/// ┌────────────────┤ SDK ├───────────────┐
|
|
/// │ └─────┘ │
|
|
/// │ ┌────────────────┐ │ ┌────────────────┐
|
|
/// │ ┌───────┤ Framework Dirs ├────────┐ │ ┌┤ Prebuilt Cache ├┐
|
|
/// │ │ └────────────────┘ │ │ │└────────────────┘│
|
|
/// │ │ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │ │ │ ┌───┐ ┌───┐ │
|
|
/// │ │ │ I │ │ I │ │ I │ │ I │◀─┼─┼─┼───│ P │ │ P │◀═╗│
|
|
/// │ │ └───┘ └───┘ └───┘ └───┘ │ │ │ └───┘ └───┘ ║│
|
|
/// │ │ ▲ ▲ ▲ │ │ │ ▲ │ ║│
|
|
/// │ └────┼───────┼───────┼────────────┘ │ └─────╫──────┼────╫┘
|
|
/// │ │ │ └──────────────┼───────╫──────┘ ║
|
|
/// └───────┼───────┼──────────────────────┘ ║ ║
|
|
/// │ │ ┌───────────────┐ ║ ║
|
|
/// │ ┌────┼───┤ Module Cache ├────────┐ ║ ║
|
|
/// │ │ │ └───────────────┘ │ ║ ║
|
|
/// │ │ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │ ║ ║
|
|
/// │ │ │ M │ │ M │ │ F │ │ F │ │ ║ ║
|
|
/// │ │ └───┘ └───┘ └───┘ └───┘ │ ║ ║
|
|
/// │ │ │ ║ ╚════╪═╝ ║
|
|
/// │ └────────────┼───────╫────────────┘ ║
|
|
/// └───────────────┘ ╚══════════════════════════╝
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_FRONTEND_MODULEINTERFACELOADER_H
|
|
#define SWIFT_FRONTEND_MODULEINTERFACELOADER_H
|
|
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Frontend/Frontend.h"
|
|
#include "swift/Frontend/ModuleInterfaceSupport.h"
|
|
#include "swift/Serialization/SerializedModuleLoader.h"
|
|
#include "llvm/Support/StringSaver.h"
|
|
|
|
namespace clang {
|
|
class CompilerInstance;
|
|
}
|
|
|
|
namespace unittest {
|
|
class ModuleInterfaceLoaderTest;
|
|
}
|
|
|
|
namespace swift {
|
|
|
|
class LangOptions;
|
|
class SearchPathOptions;
|
|
class CompilerInvocation;
|
|
|
|
/// A ModuleLoader that loads explicitly built Swift modules specified via
|
|
/// -swift-module-file
|
|
class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase {
|
|
explicit ExplicitSwiftModuleLoader(ASTContext &ctx, DependencyTracker *tracker,
|
|
ModuleLoadingMode loadMode,
|
|
bool IgnoreSwiftSourceInfoFile);
|
|
|
|
bool findModule(ImportPath::Element moduleID,
|
|
SmallVectorImpl<char> *moduleInterfacePath,
|
|
std::unique_ptr<llvm::MemoryBuffer> *moduleBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> *moduleDocBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> *moduleSourceInfoBuffer,
|
|
bool &isFramework, bool &isSystemModule) override;
|
|
|
|
std::error_code findModuleFilesInDirectory(
|
|
ImportPath::Element ModuleID,
|
|
const SerializedModuleBaseName &BaseName,
|
|
SmallVectorImpl<char> *ModuleInterfacePath,
|
|
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> *ModuleSourceInfoBuffer,
|
|
bool IsFramework) override;
|
|
|
|
bool canImportModule(ImportPath::Element mID) override;
|
|
|
|
bool isCached(StringRef DepPath) override { return false; };
|
|
|
|
struct Implementation;
|
|
Implementation &Impl;
|
|
public:
|
|
static std::unique_ptr<ExplicitSwiftModuleLoader>
|
|
create(ASTContext &ctx,
|
|
DependencyTracker *tracker, ModuleLoadingMode loadMode,
|
|
ArrayRef<std::string> ExplicitModulePaths,
|
|
StringRef ExplicitSwiftModuleMap,
|
|
bool IgnoreSwiftSourceInfoFile);
|
|
|
|
/// Append visible module names to \p names. Note that names are possibly
|
|
/// duplicated, and not guaranteed to be ordered in any way.
|
|
void collectVisibleTopLevelModuleNames(
|
|
SmallVectorImpl<Identifier> &names) const override;
|
|
~ExplicitSwiftModuleLoader();
|
|
};
|
|
|
|
/// Information about explicitly specified Swift module files.
|
|
struct ExplicitModuleInfo {
|
|
// Path of the .swiftmodule file.
|
|
std::string modulePath;
|
|
// Path of the .swiftmoduledoc file.
|
|
std::string moduleDocPath;
|
|
// Path of the .swiftsourceinfo file.
|
|
std::string moduleSourceInfoPath;
|
|
// Opened buffer for the .swiftmodule file.
|
|
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer;
|
|
// A flag that indicates whether this module is a framework
|
|
bool isFramework;
|
|
};
|
|
|
|
/// Parser of explicit module maps passed into the compiler.
|
|
// [
|
|
// {
|
|
// "moduleName": "A",
|
|
// "modulePath": "A.swiftmodule",
|
|
// "docPath": "A.swiftdoc",
|
|
// "sourceInfoPath": "A.swiftsourceinfo"
|
|
// "isFramework": false
|
|
// },
|
|
// {
|
|
// "moduleName": "B",
|
|
// "modulePath": "B.swiftmodule",
|
|
// "docPath": "B.swiftdoc",
|
|
// "sourceInfoPath": "B.swiftsourceinfo"
|
|
// "isFramework": false
|
|
// }
|
|
// ]
|
|
class ExplicitModuleMapParser {
|
|
public:
|
|
ExplicitModuleMapParser(llvm::BumpPtrAllocator &Allocator) : Saver(Allocator) {}
|
|
|
|
std::error_code
|
|
parseSwiftExplicitModuleMap(llvm::StringRef fileName,
|
|
llvm::StringMap<ExplicitModuleInfo> &moduleMap) {
|
|
using namespace llvm::yaml;
|
|
// Load the input file.
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBufOrErr =
|
|
llvm::MemoryBuffer::getFile(fileName);
|
|
if (!fileBufOrErr) {
|
|
return std::make_error_code(std::errc::no_such_file_or_directory);
|
|
}
|
|
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.
|
|
llvm::SourceMgr SM;
|
|
Stream Stream(llvm::MemoryBufferRef(Buffer, fileName), SM);
|
|
for (auto DI = Stream.begin(); DI != Stream.end(); ++DI) {
|
|
assert(DI != Stream.end() && "Failed to read a document");
|
|
if (auto *MN = dyn_cast_or_null<SequenceNode>(DI->getRoot())) {
|
|
for (auto &entry : *MN) {
|
|
if (parseSingleModuleEntry(entry, moduleMap)) {
|
|
return std::make_error_code(std::errc::invalid_argument);
|
|
}
|
|
}
|
|
} else {
|
|
return std::make_error_code(std::errc::invalid_argument);
|
|
}
|
|
}
|
|
return std::error_code{}; // success
|
|
}
|
|
|
|
private:
|
|
StringRef getScalaNodeText(llvm::yaml::Node *N) {
|
|
SmallString<32> Buffer;
|
|
return Saver.save(cast<llvm::yaml::ScalarNode>(N)->getValue(Buffer));
|
|
}
|
|
|
|
bool parseSingleModuleEntry(llvm::yaml::Node &node,
|
|
llvm::StringMap<ExplicitModuleInfo> &moduleMap) {
|
|
using namespace llvm::yaml;
|
|
auto *mapNode = dyn_cast<MappingNode>(&node);
|
|
if (!mapNode)
|
|
return true;
|
|
StringRef moduleName;
|
|
ExplicitModuleInfo result;
|
|
for (auto &entry : *mapNode) {
|
|
auto key = getScalaNodeText(entry.getKey());
|
|
auto val = getScalaNodeText(entry.getValue());
|
|
if (key == "moduleName") {
|
|
moduleName = val;
|
|
} else if (key == "modulePath") {
|
|
result.modulePath = val.str();
|
|
} else if (key == "docPath") {
|
|
result.moduleDocPath = val.str();
|
|
} else if (key == "sourceInfoPath") {
|
|
result.moduleSourceInfoPath = val.str();
|
|
} else if (key == "isFramework") {
|
|
auto valStr = val.str();
|
|
valStr.erase(std::remove(valStr.begin(), valStr.end(), '\n'), valStr.end());
|
|
if (valStr.compare("true") == 0)
|
|
result.isFramework = true;
|
|
else if (valStr.compare("false") == 0)
|
|
result.isFramework = false;
|
|
else
|
|
llvm_unreachable("Unexpected JSON value for isFramework");
|
|
} else {
|
|
// Being forgiving for future fields.
|
|
continue;
|
|
}
|
|
}
|
|
if (moduleName.empty())
|
|
return true;
|
|
moduleMap[moduleName] = std::move(result);
|
|
return false;
|
|
}
|
|
|
|
llvm::StringSaver Saver;
|
|
};
|
|
|
|
struct ModuleInterfaceLoaderOptions {
|
|
FrontendOptions::ActionType requestedAction =
|
|
FrontendOptions::ActionType::EmitModuleOnly;
|
|
bool remarkOnRebuildFromInterface = false;
|
|
bool disableInterfaceLock = false;
|
|
bool disableImplicitSwiftModule = false;
|
|
std::string mainExecutablePath;
|
|
ModuleInterfaceLoaderOptions(const FrontendOptions &Opts):
|
|
remarkOnRebuildFromInterface(Opts.RemarkOnRebuildFromModuleInterface),
|
|
disableInterfaceLock(Opts.DisableInterfaceFileLock),
|
|
disableImplicitSwiftModule(Opts.DisableImplicitModules),
|
|
mainExecutablePath(Opts.MainExecutablePath)
|
|
{
|
|
switch (Opts.RequestedAction) {
|
|
case FrontendOptions::ActionType::TypecheckModuleFromInterface:
|
|
requestedAction = FrontendOptions::ActionType::Typecheck;
|
|
break;
|
|
default:
|
|
requestedAction = FrontendOptions::ActionType::EmitModuleOnly;
|
|
break;
|
|
}
|
|
}
|
|
ModuleInterfaceLoaderOptions() = default;
|
|
};
|
|
|
|
class ModuleInterfaceCheckerImpl: public ModuleInterfaceChecker {
|
|
friend class ModuleInterfaceLoader;
|
|
ASTContext &Ctx;
|
|
std::string CacheDir;
|
|
std::string PrebuiltCacheDir;
|
|
ModuleInterfaceLoaderOptions Opts;
|
|
|
|
public:
|
|
explicit ModuleInterfaceCheckerImpl(ASTContext &Ctx,
|
|
StringRef cacheDir,
|
|
StringRef prebuiltCacheDir,
|
|
ModuleInterfaceLoaderOptions Opts)
|
|
: Ctx(Ctx), CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir),
|
|
Opts(Opts) {}
|
|
|
|
std::vector<std::string>
|
|
getCompiledModuleCandidatesForInterface(StringRef moduleName,
|
|
StringRef interfacePath) override;
|
|
|
|
/// Given a list of potential ready-to-use compiled modules for \p interfacePath,
|
|
/// check if any one of them is up-to-date. If so, emit a forwarding module
|
|
/// to the candidate binary module to \p outPath.
|
|
bool tryEmitForwardingModule(StringRef moduleName,
|
|
StringRef interfacePath,
|
|
ArrayRef<std::string> candidates,
|
|
StringRef outPath) override;
|
|
bool isCached(StringRef DepPath);
|
|
};
|
|
|
|
/// A ModuleLoader that runs a subordinate \c CompilerInvocation and
|
|
/// \c CompilerInstance to convert .swiftinterface files to .swiftmodule
|
|
/// files on the fly, caching the resulting .swiftmodules in the module cache
|
|
/// directory, and loading the serialized .swiftmodules from there.
|
|
class ModuleInterfaceLoader : public SerializedModuleLoaderBase {
|
|
friend class unittest::ModuleInterfaceLoaderTest;
|
|
explicit ModuleInterfaceLoader(
|
|
ASTContext &ctx, ModuleInterfaceCheckerImpl &InterfaceChecker,
|
|
DependencyTracker *tracker, ModuleLoadingMode loadMode,
|
|
ArrayRef<std::string> PreferInterfaceForModules,
|
|
bool IgnoreSwiftSourceInfoFile)
|
|
: SerializedModuleLoaderBase(ctx, tracker, loadMode, IgnoreSwiftSourceInfoFile),
|
|
InterfaceChecker(InterfaceChecker),
|
|
PreferInterfaceForModules(PreferInterfaceForModules){}
|
|
|
|
ModuleInterfaceCheckerImpl &InterfaceChecker;
|
|
ArrayRef<std::string> PreferInterfaceForModules;
|
|
|
|
std::error_code findModuleFilesInDirectory(
|
|
ImportPath::Element ModuleID,
|
|
const SerializedModuleBaseName &BaseName,
|
|
SmallVectorImpl<char> *ModuleInterfacePath,
|
|
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer,
|
|
std::unique_ptr<llvm::MemoryBuffer> *ModuleSourceInfoBuffer,
|
|
bool IsFramework) override;
|
|
|
|
bool isCached(StringRef DepPath) override;
|
|
public:
|
|
static std::unique_ptr<ModuleInterfaceLoader>
|
|
create(ASTContext &ctx, ModuleInterfaceCheckerImpl &InterfaceChecker,
|
|
DependencyTracker *tracker, ModuleLoadingMode loadMode,
|
|
ArrayRef<std::string> PreferInterfaceForModules = {},
|
|
bool IgnoreSwiftSourceInfoFile = false) {
|
|
return std::unique_ptr<ModuleInterfaceLoader>(
|
|
new ModuleInterfaceLoader(ctx, InterfaceChecker, tracker, loadMode,
|
|
PreferInterfaceForModules,
|
|
IgnoreSwiftSourceInfoFile));
|
|
}
|
|
|
|
/// Append visible module names to \p names. Note that names are possibly
|
|
/// duplicated, and not guaranteed to be ordered in any way.
|
|
void collectVisibleTopLevelModuleNames(
|
|
SmallVectorImpl<Identifier> &names) const override;
|
|
|
|
/// Unconditionally build \p InPath (a swiftinterface file) to \p OutPath (as
|
|
/// a swiftmodule file).
|
|
///
|
|
/// A simplified version of the core logic in #openModuleFiles.
|
|
static bool buildSwiftModuleFromSwiftInterface(
|
|
SourceManager &SourceMgr, DiagnosticEngine &Diags,
|
|
const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts,
|
|
const ClangImporterOptions &ClangOpts,
|
|
StringRef CacheDir, StringRef PrebuiltCacheDir,
|
|
StringRef ModuleName, StringRef InPath, StringRef OutPath,
|
|
bool SerializeDependencyHashes, bool TrackSystemDependencies,
|
|
ModuleInterfaceLoaderOptions Opts);
|
|
};
|
|
|
|
struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate {
|
|
private:
|
|
SourceManager &SM;
|
|
DiagnosticEngine &Diags;
|
|
llvm::BumpPtrAllocator Allocator;
|
|
llvm::StringSaver ArgSaver;
|
|
std::vector<StringRef> GenericArgs;
|
|
CompilerInvocation genericSubInvocation;
|
|
|
|
template<typename ...ArgTypes>
|
|
InFlightDiagnostic diagnose(StringRef interfacePath,
|
|
SourceLoc diagnosticLoc,
|
|
Diag<ArgTypes...> ID,
|
|
typename detail::PassArgument<ArgTypes>::type... Args) {
|
|
SourceLoc loc = diagnosticLoc;
|
|
if (diagnosticLoc.isInvalid()) {
|
|
// Diagnose this inside the interface file, if possible.
|
|
loc = SM.getLocFromExternalSource(interfacePath, 1, 1);
|
|
}
|
|
return Diags.diagnose(loc, ID, std::move(Args)...);
|
|
}
|
|
void inheritOptionsForBuildingInterface(const SearchPathOptions &SearchPathOpts,
|
|
const LangOptions &LangOpts);
|
|
bool extractSwiftInterfaceVersionAndArgs(CompilerInvocation &subInvocation,
|
|
SmallVectorImpl<const char *> &SubArgs,
|
|
std::string &CompilerVersion,
|
|
StringRef interfacePath,
|
|
SourceLoc diagnosticLoc);
|
|
public:
|
|
InterfaceSubContextDelegateImpl(SourceManager &SM,
|
|
DiagnosticEngine &Diags,
|
|
const SearchPathOptions &searchPathOpts,
|
|
const LangOptions &langOpts,
|
|
const ClangImporterOptions &clangImporterOpts,
|
|
ModuleInterfaceLoaderOptions LoaderOpts,
|
|
bool buildModuleCacheDirIfAbsent,
|
|
StringRef moduleCachePath,
|
|
StringRef prebuiltCachePath,
|
|
bool serializeDependencyHashes,
|
|
bool trackSystemDependencies);
|
|
std::error_code runInSubContext(StringRef moduleName,
|
|
StringRef interfacePath,
|
|
StringRef outputPath,
|
|
SourceLoc diagLoc,
|
|
llvm::function_ref<std::error_code(ASTContext&, ModuleDecl*,
|
|
ArrayRef<StringRef>, ArrayRef<StringRef>,
|
|
StringRef)> action) override;
|
|
std::error_code runInSubCompilerInstance(StringRef moduleName,
|
|
StringRef interfacePath,
|
|
StringRef outputPath,
|
|
SourceLoc diagLoc,
|
|
llvm::function_ref<std::error_code(SubCompilerInstanceInfo&)> action) override;
|
|
|
|
~InterfaceSubContextDelegateImpl() = default;
|
|
|
|
/// includes a hash of relevant key data.
|
|
StringRef computeCachedOutputPath(StringRef moduleName,
|
|
StringRef UseInterfacePath,
|
|
llvm::SmallString<256> &OutPath,
|
|
StringRef &CacheHash);
|
|
std::string getCacheHash(StringRef useInterfacePath);
|
|
};
|
|
}
|
|
|
|
#endif
|