From bd782be654a7db465301330d9935218b2d10100b Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Wed, 3 Jun 2020 12:05:46 -0700 Subject: [PATCH] Front-end: add a new module loader that loads explicitly built Swift modules To support -disable-implicit-swift-modules, the explicitly built modules are passed down as compiler arguments. We need this new module loader to handle these modules. This patch also stops ModuleInterfaceLoader from building module from interface when -disable-implicit-swift-modules is set. --- include/swift/AST/DiagnosticsFrontend.def | 2 + .../swift/Frontend/ModuleInterfaceLoader.h | 33 ++++++++ .../Serialization/SerializedModuleLoader.h | 3 + lib/Frontend/Frontend.cpp | 10 +++ lib/Frontend/ModuleInterfaceLoader.cpp | 82 +++++++++++++++++++ lib/Serialization/ModuleFile.cpp | 24 ++++++ lib/Serialization/ModuleFile.h | 4 + lib/Serialization/SerializedModuleLoader.cpp | 6 ++ .../Inputs/BuildModulesFromGraph.swift | 2 +- .../Inputs/Swift/SubE.swiftinterface | 5 ++ test/ScanDependencies/module_deps.swift | 6 ++ 11 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 test/ScanDependencies/Inputs/Swift/SubE.swiftinterface diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 5598cc8d660b4..7aa018966646b 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -326,6 +326,8 @@ ERROR(error_extracting_version_from_module_interface,none, ERROR(unsupported_version_of_module_interface,none, "unsupported version of module interface '%0': '%1'", (StringRef, llvm::VersionTuple)) +ERROR(error_opening_explicit_module_file,none, + "failed to open explicit Swift module: %0", (StringRef)) ERROR(error_extracting_flags_from_module_interface,none, "error extracting flags from module interface", ()) REMARK(rebuilding_module_from_interface,none, diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index c5ba052c8ffd4..22c183beb2c69 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -127,6 +127,38 @@ 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); + std::error_code findModuleFilesInDirectory( + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, + std::unique_ptr *ModuleBuffer, + std::unique_ptr *ModuleDocBuffer, + std::unique_ptr *ModuleSourceInfoBuffer) override; + + bool isCached(StringRef DepPath) override { return false; }; + + struct Implementation; + Implementation &Impl; +public: + static std::unique_ptr + create(ASTContext &ctx, + DependencyTracker *tracker, ModuleLoadingMode loadMode, + ArrayRef ExplicitModulePaths, + 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 &names) const override; + ~ExplicitSwiftModuleLoader(); +}; + struct ModuleInterfaceLoaderOptions { bool remarkOnRebuildFromInterface = false; bool disableInterfaceLock = false; @@ -137,6 +169,7 @@ struct ModuleInterfaceLoaderOptions { disableImplicitSwiftModule(Opts.DisableImplicitModules) {} ModuleInterfaceLoaderOptions() = default; }; + /// 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 diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index 83af39e5fc41a..558b1891bad02 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -138,6 +138,9 @@ class SerializedModuleLoaderBase : public ModuleLoader { /// Scan the given serialized module file to determine dependencies. llvm::ErrorOr scanModuleFile(Twine modulePath); + /// Load the module file into a buffer and also collect its module name. + static std::unique_ptr + getModuleName(ASTContext &Ctx, StringRef modulePath, std::string &Name); public: virtual ~SerializedModuleLoaderBase(); SerializedModuleLoaderBase(const SerializedModuleLoaderBase &) = delete; diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index f39fe20f71962..2f862316262a7 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -450,6 +450,16 @@ bool CompilerInstance::setUpModuleLoaders() { return true; } + // If implicit modules are disabled, we need to install an explicit module + // loader. + if (Invocation.getFrontendOptions().DisableImplicitModules) { + auto ESML = ExplicitSwiftModuleLoader::create( + *Context, + getDependencyTracker(), MLM, + Invocation.getSearchPathOptions().ExplicitSwiftModules, + IgnoreSourceInfoFile); + Context->addModuleLoader(std::move(ESML)); + } if (MLM != ModuleLoadingMode::OnlySerialized) { auto const &Clang = clangImporter->getClangInstance(); std::string ModuleCachePath = getModuleCachePathFromClang(Clang); diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 7b2845f3c6267..5e573ceb6412b 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -880,6 +880,10 @@ class ModuleInterfaceLoaderImpl { return std::move(module.moduleBuffer); } + // If implicit module is disabled, we are done. + if (Opts.disableImplicitSwiftModule) { + return std::make_error_code(std::errc::not_supported); + } std::unique_ptr moduleBuffer; @@ -1397,3 +1401,81 @@ bool InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleN // Run the action under the sub compiler instance. return action(info); } + +struct ExplicitSwiftModuleLoader::Implementation { + // Information about explicitly specified Swift module files. + struct ExplicitModuleInfo { + // Path of the module file. + StringRef path; + // Buffer of the module content. + std::unique_ptr moduleBuffer; + }; + llvm::StringMap ExplicitModuleMap; +}; + +ExplicitSwiftModuleLoader::ExplicitSwiftModuleLoader( + ASTContext &ctx, + DependencyTracker *tracker, + ModuleLoadingMode loadMode, + bool IgnoreSwiftSourceInfoFile): + SerializedModuleLoaderBase(ctx, tracker, loadMode, + IgnoreSwiftSourceInfoFile), + Impl(*new Implementation()) {} + +ExplicitSwiftModuleLoader::~ExplicitSwiftModuleLoader() { delete &Impl; } + +std::error_code ExplicitSwiftModuleLoader::findModuleFilesInDirectory( + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, + std::unique_ptr *ModuleBuffer, + std::unique_ptr *ModuleDocBuffer, + std::unique_ptr *ModuleSourceInfoBuffer) { + StringRef moduleName = ModuleID.Item.str(); + auto it = Impl.ExplicitModuleMap.find(moduleName); + // If no explicit module path is given matches the name, return with an + // error code. + if (it == Impl.ExplicitModuleMap.end()) { + return std::make_error_code(std::errc::not_supported); + } + // We found an explicit module matches the given name, give the buffer + // back to the caller side. + *ModuleBuffer = std::move(it->getValue().moduleBuffer); + return std::error_code(); +} + +void ExplicitSwiftModuleLoader::collectVisibleTopLevelModuleNames( + SmallVectorImpl &names) const { + for (auto &entry: Impl.ExplicitModuleMap) { + names.push_back(Ctx.getIdentifier(entry.getKey())); + } +} + +std::unique_ptr +ExplicitSwiftModuleLoader::create(ASTContext &ctx, + DependencyTracker *tracker, ModuleLoadingMode loadMode, + ArrayRef ExplicitModulePaths, + bool IgnoreSwiftSourceInfoFile) { + auto result = std::unique_ptr( + new ExplicitSwiftModuleLoader(ctx, tracker, loadMode, + IgnoreSwiftSourceInfoFile)); + auto &Impl = result->Impl; + for (auto path: ExplicitModulePaths) { + std::string name; + // Load the explicit module into a buffer and get its name. + std::unique_ptr buffer = getModuleName(ctx, path, name); + if (buffer) { + // Register this module for future loading. + auto &entry = Impl.ExplicitModuleMap[name]; + entry.path = path; + entry.moduleBuffer = std::move(buffer); + } else { + // We cannot read the module content, diagnose. + ctx.Diags.diagnose(SourceLoc(), + diag::error_opening_explicit_module_file, + path); + } + } + + return result; +} diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index dd26ab5e44062..524c264c0406f 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -2190,6 +2190,30 @@ TypeDecl *ModuleFile::lookupLocalType(StringRef MangledName) { return cast(getDecl(*iter)); } +std::unique_ptr +ModuleFile::getModuleName(ASTContext &Ctx, StringRef modulePath, + std::string &Name) { + // Open the module file + auto &fs = *Ctx.SourceMgr.getFileSystem(); + auto moduleBuf = fs.getBufferForFile(modulePath); + if (!moduleBuf) + return nullptr; + + // Load the module file without validation. + std::unique_ptr loadedModuleFile; + ExtendedValidationInfo ExtInfo; + bool isFramework = false; + serialization::ValidationInfo loadInfo = + ModuleFile::load(modulePath.str(), + std::move(moduleBuf.get()), + nullptr, + nullptr, + /*isFramework*/isFramework, loadedModuleFile, + &ExtInfo); + Name = loadedModuleFile->Name; + return std::move(loadedModuleFile->ModuleInputBuffer); +} + OpaqueTypeDecl *ModuleFile::lookupOpaqueResultType(StringRef MangledName) { PrettyStackTraceModuleFile stackEntry(*this); diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 0b239c86cbbcc..9f233aa1f89dc 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -106,6 +106,10 @@ class ModuleFile StringRef MiscVersion; public: + static std::unique_ptr getModuleName(ASTContext &Ctx, + StringRef modulePath, + std::string &Name); + /// Represents another module that has been imported as a dependency. class Dependency { public: diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index e8ce91f5936ae..6a8c3be82b06b 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -275,6 +275,12 @@ std::error_code SerializedModuleLoaderBase::openModuleDocFileIfPresent( return std::error_code(); } +std::unique_ptr +SerializedModuleLoaderBase::getModuleName(ASTContext &Ctx, StringRef modulePath, + std::string &Name) { + return ModuleFile::getModuleName(Ctx, modulePath, Name); +} + std::error_code SerializedModuleLoaderBase::openModuleSourceInfoFileIfPresent( AccessPathElem ModuleID, diff --git a/test/ScanDependencies/Inputs/BuildModulesFromGraph.swift b/test/ScanDependencies/Inputs/BuildModulesFromGraph.swift index f2fdefffd9a97..d7c25eaa06f5f 100644 --- a/test/ScanDependencies/Inputs/BuildModulesFromGraph.swift +++ b/test/ScanDependencies/Inputs/BuildModulesFromGraph.swift @@ -22,7 +22,7 @@ let moduleDependencyGraph = try! decoder.decode( func findModuleBuildingCommand(_ moduleName: String) -> [String]? { for (_, dep) in moduleDependencyGraph.modules { - if dep.modulePath.hasSuffix(moduleName) { + if URL(fileURLWithPath: dep.modulePath).lastPathComponent == moduleName { switch dep.details { case .swift(let details): return details.commandLine diff --git a/test/ScanDependencies/Inputs/Swift/SubE.swiftinterface b/test/ScanDependencies/Inputs/Swift/SubE.swiftinterface new file mode 100644 index 0000000000000..5623089bda83d --- /dev/null +++ b/test/ScanDependencies/Inputs/Swift/SubE.swiftinterface @@ -0,0 +1,5 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name SubE +import Swift +import E +public func funcSubE() {} diff --git a/test/ScanDependencies/module_deps.swift b/test/ScanDependencies/module_deps.swift index 2c4908b1b6b46..3e11657ec02d4 100644 --- a/test/ScanDependencies/module_deps.swift +++ b/test/ScanDependencies/module_deps.swift @@ -37,6 +37,8 @@ // RUN: %target-run %t/ModuleBuilder %t/deps.json %swift-path E.swiftmodule -o %t/clang-module-cache/E.swiftmodule -Xcc -Xclang -Xcc -fmodule-map-file=%S/Inputs/CHeaders/module.modulemap -Xcc -Xclang -Xcc -fmodule-file=%t/clang-module-cache/SwiftShims.pcm | %S/Inputs/CommandRunner.py // RUN: ls %t/clang-module-cache/E.swiftmodule +// RUN: %target-run %t/ModuleBuilder %t/deps.json %swift-path SubE.swiftmodule -o %t/clang-module-cache/SubE.swiftmodule -Xcc -Xclang -Xcc -fmodule-map-file=%S/Inputs/CHeaders/module.modulemap -Xcc -Xclang -Xcc -fmodule-file=%t/clang-module-cache/SwiftShims.pcm -swift-module-file %t/clang-module-cache/E.swiftmodule | %S/Inputs/CommandRunner.py +// RUN: ls %t/clang-module-cache/SubE.swiftmodule // REQUIRES: executable_test // REQUIRES: objc_interop @@ -44,6 +46,7 @@ import C import E import G +import SubE // CHECK: "mainModuleName": "deps" @@ -63,6 +66,9 @@ import G // CHECK-NEXT: "swift": "G" // CHECK-NEXT: } // CHECK-NEXT: { +// CHECK-NEXT: "swift": "SubE" +// CHECK-NEXT: } +// CHECK-NEXT: { // CHECK-NEXT: "swift": "Swift" // CHECK-NEXT: } // CHECK-NEXT: {