From f9396f28127bcbc3479c4aa2798c71fdcd237685 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 17 Jul 2020 23:48:43 -0700 Subject: [PATCH] ModuleInterface: teach -compile-module-from-interface to emit forwarding module -compile-module-from-interface action now takes arguments of -candidate-module-file. If one of the candidate module files is up-to-date, the action emits a forwarding module pointing to the candidate module instead of building a binary module. --- include/swift/AST/SearchPathOptions.h | 3 ++ .../swift/Frontend/ModuleInterfaceLoader.h | 8 ++++ include/swift/Option/FrontendOptions.td | 4 ++ .../Serialization/SerializedModuleLoader.h | 6 +++ lib/Frontend/CompilerInvocation.cpp | 4 ++ lib/Frontend/ModuleInterfaceBuilder.cpp | 16 ++++++-- lib/Frontend/ModuleInterfaceBuilder.h | 6 ++- lib/Frontend/ModuleInterfaceLoader.cpp | 39 ++++++++++++++++++- .../emit-forward-modules.swift | 26 +++++++++++++ 9 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 test/ModuleInterface/emit-forward-modules.swift diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index cd65528e48f17..da5913a72849a 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -85,6 +85,9 @@ class SearchPathOptions { /// The paths to a set of explicitly built modules from interfaces. std::vector ExplicitSwiftModules; + /// A set of compiled modules that may be ready to use. + std::vector CandidateCompiledModules; + /// A map of explict Swift module information. std::string ExplicitSwiftModuleMap; private: diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index b376ec61f1687..d6212ac45a5bd 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -240,6 +240,14 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { std::vector 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 candidates, + StringRef outPath) override; }; struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate { diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 460f5374310e7..2da020d4f4d59 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -719,4 +719,8 @@ def target_variant_sdk_version : Separate<["-"], "target-variant-sdk-version">, def extra_clang_options_only : Flag<["-"], "only-use-extra-clang-opts">, HelpText<"Options passed via -Xcc are sufficient for Clang configuration">; + +def candidate_module_file + : Separate<["-"], "candidate-module-file">, MetaVarName<"">, + HelpText<"Specify Swift module may be ready to use for an interface">; } // end let Flags = [FrontendOption, NoDriverOption, HelpHidden] diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index 44c9d3565089e..fd8bfe227266d 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -206,6 +206,12 @@ class SerializedModuleLoaderBase : public ModuleLoader { StringRef interfacePath) { return std::vector(); } + virtual bool tryEmitForwardingModule(StringRef moduleName, + StringRef interfacePath, + ArrayRef candidates, + StringRef outPath) { + return false; + } }; /// Imports serialized Swift modules into an ASTContext. diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 4661350c6ed9e..3df1afbf8beef 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -900,6 +900,10 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, } if (const Arg *A = Args.getLastArg(OPT_explict_swift_module_map)) Opts.ExplicitSwiftModuleMap = A->getValue(); + for (auto A: Args.filtered(OPT_candidate_module_file)) { + Opts.CandidateCompiledModules.push_back(resolveSearchPath(A->getValue())); + } + // Opts.RuntimeIncludePath is set by calls to // setRuntimeIncludePath() or setMainExecutablePath(). // Opts.RuntimeImportPath is set by calls to diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index bcfb5c6dd5968..4e118f04c0e5a 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -147,7 +147,8 @@ bool ModuleInterfaceBuilder::collectDepsForSerialization( bool ModuleInterfaceBuilder::buildSwiftModuleInternal( StringRef OutPath, bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer) { + std::unique_ptr *ModuleBuffer, + ArrayRef CompiledCandidates) { auto outerPrettyStackState = llvm::SavePrettyStackState(); @@ -167,6 +168,13 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( [&](SubCompilerInstanceInfo &info) { auto &SubInstance = *info.Instance; auto subInvocation = SubInstance.getInvocation(); + // Try building forwarding module first. If succeed, return. + if (static_cast(SubInstance.getASTContext() + .getModuleInterfaceLoader())->tryEmitForwardingModule(moduleName, + interfacePath, + CompiledCandidates, OutPath)) { + return false; + } FrontendOptions &FEOpts = subInvocation.getFrontendOptions(); const auto &InputInfo = FEOpts.InputsAndOutputs.firstInput(); StringRef InPath = InputInfo.file(); @@ -256,12 +264,14 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps, std::unique_ptr *ModuleBuffer, - llvm::function_ref RemarkRebuild) { + llvm::function_ref RemarkRebuild, + ArrayRef CompiledCandidates) { auto build = [&]() { if (RemarkRebuild) { RemarkRebuild(); } - return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer); + return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer, + CompiledCandidates); }; if (disableInterfaceFileLock) { return build(); diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index e07fa18f08b1b..ae90b2ded60f9 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -85,7 +85,8 @@ class ModuleInterfaceBuilder { bool IsHashBased); bool buildSwiftModuleInternal(StringRef OutPath, bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer); + std::unique_ptr *ModuleBuffer, + ArrayRef CandidateModules); public: ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine &diags, InterfaceSubContextDelegate &subASTDelegate, @@ -111,7 +112,8 @@ class ModuleInterfaceBuilder { bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps, std::unique_ptr *ModuleBuffer, - llvm::function_ref RemarkRebuild = nullptr); + llvm::function_ref RemarkRebuild = nullptr, + ArrayRef CandidateModules = {}); }; } // end namespace swift diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index a3d52295e9a58..be6cf8f9a7fd7 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -1029,7 +1029,6 @@ ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleN dependencyTracker, llvm::is_contained(PreferInterfaceForModules, moduleName) ? ModuleLoadingMode::PreferInterface : LoadMode); - SmallVector allDeps; std::vector results; auto pair = Impl.getCompiledModuleCandidates(); // Add compiled module candidates only when they are non-empty. @@ -1040,6 +1039,41 @@ ModuleInterfaceLoader::getCompiledModuleCandidatesForInterface(StringRef moduleN return results; } +bool ModuleInterfaceLoader::tryEmitForwardingModule(StringRef moduleName, + StringRef interfacePath, + ArrayRef candidates, + StringRef outputPath) { + // Derive .swiftmodule path from the .swiftinterface path. + auto newExt = file_types::getExtension(file_types::TY_SwiftModuleFile); + llvm::SmallString<32> modulePath = interfacePath; + llvm::sys::path::replace_extension(modulePath, newExt); + ModuleInterfaceLoaderImpl Impl( + Ctx, modulePath, interfacePath, moduleName, + CacheDir, PrebuiltCacheDir, SourceLoc(), + Opts, + dependencyTracker, + llvm::is_contained(PreferInterfaceForModules, moduleName) ? + ModuleLoadingMode::PreferInterface : LoadMode); + SmallVector deps; + std::unique_ptr moduleBuffer; + for (auto mod: candidates) { + // Check if the candidate compiled module is still up-to-date. + if (Impl.swiftModuleIsUpToDate(mod, deps, moduleBuffer)) { + // If so, emit a forwarding module to the candidate. + ForwardingModule FM(mod); + auto hadError = withOutputFile(Ctx.Diags, outputPath, + [&](llvm::raw_pwrite_stream &out) { + llvm::yaml::Output yamlWriter(out); + yamlWriter << FM; + return false; + }); + if (!hadError) + return true; + } + } + return false; +} + bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( SourceManager &SourceMgr, DiagnosticEngine &Diags, const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts, @@ -1069,7 +1103,8 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( // FIXME: We really only want to serialize 'important' dependencies here, if // we want to ship the built swiftmodules to another machine. return builder.buildSwiftModule(OutPath, /*shouldSerializeDeps*/true, - /*ModuleBuffer*/nullptr); + /*ModuleBuffer*/nullptr, nullptr, + SearchPathOpts.CandidateCompiledModules); } void ModuleInterfaceLoader::collectVisibleTopLevelModuleNames( diff --git a/test/ModuleInterface/emit-forward-modules.swift b/test/ModuleInterface/emit-forward-modules.swift new file mode 100644 index 0000000000000..b1af6c435f148 --- /dev/null +++ b/test/ModuleInterface/emit-forward-modules.swift @@ -0,0 +1,26 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/Foo.swiftmodule) +// RUN: %empty-directory(%t/ResourceDir/%target-sdk-name/prebuilt-modules/Foo.swiftmodule) +// RUN: echo "public func foo() {}" > %t/Foo.swift + +import Foo + +// Step 1: build swift interface from the source +// RUN: %target-swift-frontend -emit-module %t/Foo.swift -module-name Foo -emit-module-interface-path %t/Foo.swiftmodule/%target-swiftinterface-name + +// Step 2: building a module from the interface, and the module should not be a forwarding module. +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo.swiftmodule/%target-swiftinterface-name -o %t/Foo.swiftmodule/%target-swiftmodule-name -module-name Foo + +// RUN: not %{python} %S/ModuleCache/Inputs/check-is-forwarding-module.py %t/Foo.swiftmodule/%target-swiftmodule-name + +// Step 3: given the adjacent binary module as a candidate, building a module from the interface should give us a forwarding module +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo.swiftmodule/%target-swiftinterface-name -o %t/Foo-from-interface.swiftmodule -module-name Foo -candidate-module-file %t/Foo.swiftmodule/%target-swiftmodule-name + +// RUN: %{python} %S/ModuleCache/Inputs/check-is-forwarding-module.py %t/Foo-from-interface.swiftmodule + +// Step 4: given the stale adjacent binary module as a candidate, building a module from the interface should give us a binary module +// RUN: touch %t/Foo.swiftmodule/%target-swiftinterface-name + +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo.swiftmodule/%target-swiftinterface-name -o %t/Foo-from-interface.swiftmodule -module-name Foo -candidate-module-file %t/Foo.swiftmodule/%target-swiftmodule-name + +// RUN: not %{python} %S/ModuleCache/Inputs/check-is-forwarding-module.py %t/Foo-from-interface.swiftmodule