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: {