mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[ModuleInterface] Add TextualInterfaceModuleLoader sketch.
This commit is contained in:
@@ -10,19 +10,218 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define DEBUG_TYPE "textual-module-interface"
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/Decl.h"
|
||||
#include "swift/AST/DiagnosticsFrontend.h"
|
||||
#include "swift/AST/Module.h"
|
||||
#include "swift/Frontend/Frontend.h"
|
||||
#include "swift/Frontend/ParseableInterfaceSupport.h"
|
||||
#include "swift/SILOptimizer/PassManager/Passes.h"
|
||||
#include "swift/Serialization/SerializationOptions.h"
|
||||
#include "clang/Basic/Module.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/CrashRecoveryContext.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Regex.h"
|
||||
#include "llvm/Support/StringSaver.h"
|
||||
|
||||
using namespace swift;
|
||||
|
||||
#define SWIFT_TOOLS_VERSION_KEY "swift-tools-version"
|
||||
#define SWIFT_MODULE_FLAGS_KEY "swift-module-flags"
|
||||
|
||||
static bool
|
||||
extractSwiftInterfaceVersionAndArgs(DiagnosticEngine &Diags,
|
||||
clang::vfs::FileSystem &FS,
|
||||
StringRef SwiftInterfacePathIn,
|
||||
swift::version::Version &Vers,
|
||||
llvm::StringSaver &SubArgSaver,
|
||||
SmallVectorImpl<const char *> &SubArgs) {
|
||||
auto FileOrError = swift::vfs::getFileOrSTDIN(FS, SwiftInterfacePathIn);
|
||||
if (!FileOrError) {
|
||||
Diags.diagnose(SourceLoc(), diag::error_open_input_file,
|
||||
SwiftInterfacePathIn, FileOrError.getError().message());
|
||||
return true;
|
||||
}
|
||||
auto SB = FileOrError.get()->getBuffer();
|
||||
auto VersRe = getSwiftInterfaceToolsVersionRegex();
|
||||
auto FlagRe = getSwiftInterfaceModuleFlagsRegex();
|
||||
SmallVector<StringRef, 1> VersMatches, FlagMatches;
|
||||
if (!VersRe.match(SB, &VersMatches)) {
|
||||
Diags.diagnose(SourceLoc(),
|
||||
diag::error_extracting_version_from_textual_interface);
|
||||
return true;
|
||||
}
|
||||
if (!FlagRe.match(SB, &FlagMatches)) {
|
||||
Diags.diagnose(SourceLoc(),
|
||||
diag::error_extracting_flags_from_textual_interface);
|
||||
return true;
|
||||
}
|
||||
assert(VersMatches.size() == 2);
|
||||
assert(FlagMatches.size() == 2);
|
||||
Vers = swift::version::Version(VersMatches[1], SourceLoc(), &Diags);
|
||||
llvm::cl::TokenizeGNUCommandLine(FlagMatches[1], SubArgSaver, SubArgs);
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextualInterfaceModuleLoader::configureSubInvocationAndOutputPath(
|
||||
CompilerInvocation &SubInvocation,
|
||||
StringRef InPath,
|
||||
llvm::SmallString<128> &OutPath) {
|
||||
|
||||
auto &SearchPathOpts = Ctx.SearchPathOpts;
|
||||
auto &LangOpts = Ctx.LangOpts;
|
||||
|
||||
// Start with a SubInvocation that copies various state from our
|
||||
// invoking ASTContext.
|
||||
SubInvocation.setImportSearchPaths(SearchPathOpts.ImportSearchPaths);
|
||||
SubInvocation.setFrameworkSearchPaths(SearchPathOpts.FrameworkSearchPaths);
|
||||
SubInvocation.setSDKPath(SearchPathOpts.SDKPath);
|
||||
SubInvocation.setInputKind(InputFileKind::SwiftModuleInterface);
|
||||
SubInvocation.setRuntimeResourcePath(SearchPathOpts.RuntimeResourcePath);
|
||||
SubInvocation.setTargetTriple(LangOpts.Target);
|
||||
|
||||
// Calculate an output filename based on the SubInvocation hash, and
|
||||
// wire up the SubInvocation's InputsAndOutputs to contain both
|
||||
// input and output filenames.
|
||||
OutPath = CacheDir;
|
||||
llvm::sys::path::append(OutPath, llvm::sys::path::stem(InPath));
|
||||
OutPath.append("-");
|
||||
OutPath.append(SubInvocation.getPCHHash());
|
||||
OutPath.append(".");
|
||||
auto Ext = file_types::getExtension(file_types::TY_SwiftModuleFile);
|
||||
OutPath.append(Ext);
|
||||
|
||||
auto &FEOpts = SubInvocation.getFrontendOptions();
|
||||
FEOpts.RequestedAction = FrontendOptions::ActionType::EmitModuleOnly;
|
||||
FEOpts.InputsAndOutputs.addPrimaryInputFile(InPath);
|
||||
FEOpts.InputsAndOutputs.setMainAndSupplementaryOutputs(
|
||||
{OutPath.str()}, {SupplementaryOutputPaths()});
|
||||
}
|
||||
|
||||
// FIXME: this needs to be a more extensive up-to-date check.
|
||||
static bool
|
||||
swiftModuleIsUpToDate(clang::vfs::FileSystem &FS,
|
||||
StringRef InPath, StringRef OutPath) {
|
||||
if (FS.exists(OutPath)) {
|
||||
auto InStatus = FS.status(InPath);
|
||||
auto OutStatus = FS.status(OutPath);
|
||||
if (InStatus && OutStatus) {
|
||||
return InStatus.get().getLastModificationTime() <=
|
||||
OutStatus.get().getLastModificationTime();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool buildSwiftModuleFromSwiftInterface(
|
||||
clang::vfs::FileSystem &FS, DiagnosticEngine &Diags,
|
||||
CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath) {
|
||||
bool SubError = false;
|
||||
bool RunSuccess = llvm::CrashRecoveryContext().RunSafelyOnThread([&] {
|
||||
|
||||
llvm::BumpPtrAllocator SubArgsAlloc;
|
||||
llvm::StringSaver SubArgSaver(SubArgsAlloc);
|
||||
SmallVector<const char *, 16> SubArgs;
|
||||
swift::version::Version Vers;
|
||||
if (extractSwiftInterfaceVersionAndArgs(Diags, FS, InPath, Vers,
|
||||
SubArgSaver, SubArgs)) {
|
||||
SubError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SubInvocation.parseArgs(SubArgs, Diags)) {
|
||||
SubError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the .swiftmodule; this is a _very_ abridged version of the logic in
|
||||
// performCompile in libFrontendTool, specialized, to just the one
|
||||
// module-serialization task we're trying to do here.
|
||||
LLVM_DEBUG(llvm::dbgs() << "Setting up instance\n");
|
||||
CompilerInstance SubInstance;
|
||||
if (SubInstance.setup(SubInvocation)) {
|
||||
SubError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << "Performing sema\n");
|
||||
SubInstance.performSema();
|
||||
if (SubInstance.getASTContext().hadError()) {
|
||||
SubError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
auto Mod = SubInstance.getMainModule();
|
||||
auto SILMod = SubInstance.takeSILModule();
|
||||
if (SILMod) {
|
||||
LLVM_DEBUG(llvm::dbgs() << "Running SIL diagnostic passes\n");
|
||||
if (runSILDiagnosticPasses(*SILMod)) {
|
||||
SubError = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << "Serializing " << OutPath << "\n");
|
||||
SerializationOptions serializationOpts;
|
||||
std::string OutPathStr = OutPath;
|
||||
serializationOpts.OutputPath = OutPathStr.c_str();
|
||||
serializationOpts.SerializeAllSIL = true;
|
||||
serialize(Mod, serializationOpts, SILMod.get());
|
||||
SubError = Diags.hadAnyError();
|
||||
});
|
||||
return !RunSuccess || SubError;
|
||||
}
|
||||
|
||||
/// Load a .swiftmodule associated with a .swiftinterface either from a
|
||||
/// cache or by converting it in a subordinate \c CompilerInstance, caching
|
||||
/// the results.
|
||||
std::error_code TextualInterfaceModuleLoader::openModuleFiles(
|
||||
StringRef DirName, StringRef ModuleFilename, StringRef ModuleDocFilename,
|
||||
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
|
||||
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer,
|
||||
llvm::SmallVectorImpl<char> &Scratch) {
|
||||
|
||||
auto &FS = *Ctx.SourceMgr.getFileSystem();
|
||||
auto &Diags = Ctx.Diags;
|
||||
llvm::SmallString<128> InPath, OutPath;
|
||||
|
||||
// First check to see if the .swiftinterface exists at all. Bail if not.
|
||||
InPath = DirName;
|
||||
llvm::sys::path::append(InPath, ModuleFilename);
|
||||
auto Ext = file_types::getExtension(file_types::TY_SwiftModuleInterfaceFile);
|
||||
llvm::sys::path::replace_extension(InPath, Ext);
|
||||
if (!FS.exists(InPath))
|
||||
return std::make_error_code(std::errc::no_such_file_or_directory);
|
||||
|
||||
// Set up a _potential_ sub-invocation to consume the .swiftinterface and emit
|
||||
// the .swiftmodule.
|
||||
CompilerInvocation SubInvocation;
|
||||
configureSubInvocationAndOutputPath(SubInvocation, InPath, OutPath);
|
||||
|
||||
// Evaluate if we need to run this sub-invocation, and if so run it.
|
||||
if (!swiftModuleIsUpToDate(FS, InPath, OutPath)) {
|
||||
if (buildSwiftModuleFromSwiftInterface(FS, Diags, SubInvocation, InPath,
|
||||
OutPath))
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
// Finish off by delegating back up to the SerializedModuleLoaderBase
|
||||
// routine that can load the recently-manufactured serialized module.
|
||||
LLVM_DEBUG(llvm::dbgs() << "Loading " << OutPath
|
||||
<< " via normal module loader\n");
|
||||
auto ErrorCode = SerializedModuleLoaderBase::openModuleFiles(
|
||||
CacheDir, llvm::sys::path::filename(OutPath), ModuleDocFilename,
|
||||
ModuleBuffer, ModuleDocBuffer, Scratch);
|
||||
LLVM_DEBUG(llvm::dbgs() << "Loaded " << OutPath
|
||||
<< " via normal module loader with error: "
|
||||
<< ErrorCode.message() << "\n");
|
||||
return ErrorCode;
|
||||
}
|
||||
|
||||
/// Diagnose any scoped imports in \p imports, i.e. those with a non-empty
|
||||
/// access path. These are not yet supported by textual interfaces, since the
|
||||
/// information about the declaration kind is not preserved through the binary
|
||||
|
||||
Reference in New Issue
Block a user