[ModuleInterface] Add TextualInterfaceModuleLoader sketch.

This commit is contained in:
Graydon Hoare
2018-10-03 01:12:31 -07:00
parent f9848e9392
commit 6ea9accbed
4 changed files with 238 additions and 1 deletions

View File

@@ -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