From de7ab4cc3f94f347fd50a60831fceb3c6bbbff27 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 29 Jul 2024 17:16:54 -0700 Subject: [PATCH] [Dependency Scanning] Disable validation of Swift dependency modules' existing pre-built candidate binary module files in the scanner, on a non-caching build. As-is, this default interferes with the incremental build machinery which conservatively assumes that binary module dependencies must cause dependents to be re-built. --- include/swift/AST/SearchPathOptions.h | 7 ++++--- include/swift/Option/FrontendOptions.td | 2 ++ lib/Frontend/CompilerInvocation.cpp | 9 ++++++--- lib/Frontend/ModuleInterfaceLoader.cpp | 5 ++--- lib/Serialization/ScanningLoaders.cpp | 2 +- lib/Serialization/SerializedModuleLoader.cpp | 2 +- test/ScanDependencies/compiled_swift_modules.swift | 6 +++--- test/ScanDependencies/testable-dependencies.swift | 12 ++++++------ 8 files changed, 25 insertions(+), 20 deletions(-) diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index f58abedcd2e4b..dd3b770ea4568 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -532,8 +532,9 @@ class SearchPathOptions { /// Specify the module loading behavior of the compilation. ModuleLoadingMode ModuleLoadMode = ModuleLoadingMode::PreferSerialized; - /// Legacy scanner search behavior. - bool NoScannerModuleValidation = false; + /// New scanner search behavior. Validate up-to-date existing Swift module + /// dependencies in the scanner itself. + bool ScannerModuleValidation = false; /// Return all module search paths that (non-recursively) contain a file whose /// name is in \p Filenames. @@ -583,7 +584,7 @@ class SearchPathOptions { hash_combine_range(RuntimeLibraryImportPaths.begin(), RuntimeLibraryImportPaths.end()), DisableModulesValidateSystemDependencies, - NoScannerModuleValidation, + ScannerModuleValidation, ModuleLoadMode); } diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 3bed7622f2e61..9009dff64df09 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -1357,6 +1357,8 @@ def disable_sending_args_and_results_with_region_isolation : Flag<["-"], HelpText<"Disable sending args and results when region based isolation is enabled. Only enabled with asserts">, Flags<[HelpHidden]>; +def scanner_module_validation: Flag<["-"], "scanner-module-validation">, + HelpText<"Validate binary modules in the dependency scanner">; def no_scanner_module_validation: Flag<["-"], "no-scanner-module-validation">, HelpText<"Do not validate binary modules in scanner and delegate the validation to swift-frontend">; def module_load_mode: Separate<["-"], "module-load-mode">, diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 0d07ebe919557..dd44796999424 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1967,6 +1967,7 @@ static bool validateSwiftModuleFileArgumentAndAdd(const std::string &swiftModule static bool ParseSearchPathArgs(SearchPathOptions &Opts, ArgList &Args, DiagnosticEngine &Diags, + const CASOptions &CASOpts, StringRef workingDirectory) { using namespace options; namespace path = llvm::sys::path; @@ -2104,8 +2105,10 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, ArgList &Args, Opts.ScannerPrefixMapper.push_back(Opt.str()); } - Opts.NoScannerModuleValidation |= - Args.hasArg(OPT_no_scanner_module_validation); + // rdar://132340493 disable scanner-side validation for non-caching builds + Opts.ScannerModuleValidation |= Args.hasFlag(OPT_scanner_module_validation, + OPT_no_scanner_module_validation, + CASOpts.EnableCaching); std::optional forceModuleLoadingMode; if (auto *A = Args.getLastArg(OPT_module_load_mode)) @@ -3494,7 +3497,7 @@ bool CompilerInvocation::parseArgs( ParseSymbolGraphArgs(SymbolGraphOpts, ParsedArgs, Diags, LangOpts); if (ParseSearchPathArgs(SearchPathOpts, ParsedArgs, Diags, - workingDirectory)) { + CASOpts, workingDirectory)) { return true; } diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 5ea55f9ca25a7..8a9a32578775c 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -1402,7 +1402,7 @@ ModuleInterfaceCheckerImpl::getCompiledModuleCandidatesForInterface( auto validateModule = [&](StringRef modulePath) { // Legacy behavior do not validate module. - if (Ctx.SearchPathOpts.NoScannerModuleValidation) + if (!Ctx.SearchPathOpts.ScannerModuleValidation) return true; // If we picked the other module already, no need to validate this one since @@ -1919,8 +1919,7 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl( searchPathOpts.PluginSearchOpts; // Get module loading behavior options. - genericSubInvocation.getSearchPathOptions().NoScannerModuleValidation = - searchPathOpts.NoScannerModuleValidation; + genericSubInvocation.getSearchPathOptions().ScannerModuleValidation = searchPathOpts.ScannerModuleValidation; genericSubInvocation.getSearchPathOptions().ModuleLoadMode = searchPathOpts.ModuleLoadMode; diff --git a/lib/Serialization/ScanningLoaders.cpp b/lib/Serialization/ScanningLoaders.cpp index 4145788bc5618..d3339d0815907 100644 --- a/lib/Serialization/ScanningLoaders.cpp +++ b/lib/Serialization/ScanningLoaders.cpp @@ -156,7 +156,7 @@ SwiftModuleScanner::scanInterfaceFile(Twine moduleInterfacePath, auto compiledCandidates = getCompiledCandidates(Ctx, realModuleName.str(), InPath); if (!compiledCandidates.empty() && - !Ctx.SearchPathOpts.NoScannerModuleValidation) { + Ctx.SearchPathOpts.ScannerModuleValidation) { assert(compiledCandidates.size() == 1 && "Should only have 1 candidate module"); auto BinaryDep = scanModuleFile(compiledCandidates[0], isFramework, diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 0c58bfa69555a..3f0e0f3bba240 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -454,7 +454,7 @@ SerializedModuleLoaderBase::scanModuleFile(Twine modulePath, bool isFramework, isRequiredOSSAModules(), Ctx.LangOpts.SDKName, Ctx.SearchPathOpts.DeserializedPathRecoverer, loadedModuleFile); - if (!Ctx.SearchPathOpts.NoScannerModuleValidation) { + if (Ctx.SearchPathOpts.ScannerModuleValidation) { // If failed to load, just ignore and return do not found. if (loadInfo.status != serialization::Status::Valid) { if (Ctx.LangOpts.EnableModuleLoadingRemarks) diff --git a/test/ScanDependencies/compiled_swift_modules.swift b/test/ScanDependencies/compiled_swift_modules.swift index 4ccda3f788fc1..df3b76a446041 100644 --- a/test/ScanDependencies/compiled_swift_modules.swift +++ b/test/ScanDependencies/compiled_swift_modules.swift @@ -10,7 +10,7 @@ import Foo // HAS_BINARY: "swiftPrebuiltExternal": "Foo" -// HAS_NO_COMPILED-NOT: "{{.*}}Foo.swiftmodule{{.*}}.swiftmodule" +// HAS_NO_COMPILED-NOT: "compiledModulePath":{{.*}}"{{.*}}Foo.swiftmodule{{.*}}.swiftmodule" // Step 1: build swift interface and swift module side by side // RUN: %target-swift-frontend -emit-module %t/Foo.swift -emit-module-path %t/Foo.swiftmodule/%target-swiftmodule-name -module-name Foo -emit-module-interface-path %t/Foo.swiftmodule/%target-swiftinterface-name @@ -20,7 +20,7 @@ import Foo // RUN: %validate-json %t/deps.json | %FileCheck %s -check-prefix=HAS_COMPILED /// Check scanner picked binary dependency if not requesting raw scan output. -// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -I %t -emit-dependencies -emit-dependencies-path %t/deps.d +// RUN: %target-swift-frontend -scan-dependencies %s -scanner-module-validation -o %t/deps.json -I %t -emit-dependencies -emit-dependencies-path %t/deps.d // RUN: %validate-json %t/deps.json | %FileCheck %s -check-prefix=HAS_BINARY // Step 3: remove the adjacent module. @@ -38,7 +38,7 @@ import Foo // RUN: %validate-json %t/deps.json | %FileCheck %s -check-prefix=HAS_COMPILED /// Check scanner picked binary dependency if not requesting raw scan output. -// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -I %t -emit-dependencies -emit-dependencies-path %t/deps.d -sdk %t -prebuilt-module-cache-path %t/ResourceDir/%target-sdk-name/prebuilt-modules +// RUN: %target-swift-frontend -scan-dependencies %s -scanner-module-validation -o %t/deps.json -I %t -emit-dependencies -emit-dependencies-path %t/deps.d -sdk %t -prebuilt-module-cache-path %t/ResourceDir/%target-sdk-name/prebuilt-modules // RUN: %validate-json %t/deps.json | %FileCheck %s -check-prefix=HAS_BINARY // Step 6: update the interface file from where the prebuilt module cache was built. diff --git a/test/ScanDependencies/testable-dependencies.swift b/test/ScanDependencies/testable-dependencies.swift index 87c5166ae0282..2023de021cd78 100644 --- a/test/ScanDependencies/testable-dependencies.swift +++ b/test/ScanDependencies/testable-dependencies.swift @@ -17,7 +17,7 @@ // RUN: %t/A.swift /// Import testable build, should use binary but no extra dependencies. -// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-serialized -module-name Test %t/main.swift \ +// RUN: %target-swift-frontend -scan-dependencies -scanner-module-validation -module-load-mode prefer-serialized -module-name Test %t/main.swift \ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib \ // RUN: -o %t/deps1.json -I %t/testable -swift-version 5 -Rmodule-loading // RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps1.json Test directDependencies | %FileCheck %s --check-prefix TEST1 @@ -26,7 +26,7 @@ // EMPTY-NOT: B /// Import regular build, should use binary. -// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-serialized -module-name Test %t/main.swift \ +// RUN: %target-swift-frontend -scan-dependencies -scanner-module-validation -module-load-mode prefer-serialized -module-name Test %t/main.swift \ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib \ // RUN: -o %t/deps2.json -I %t/regular -swift-version 5 -Rmodule-loading // RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps2.json Test directDependencies | %FileCheck %s --check-prefix TEST2 @@ -34,7 +34,7 @@ // RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps2.json swiftPrebuiltExternal:A directDependencies | %FileCheck %s --check-prefix EMPTY --allow-empty /// Testable import testable build, should use binary, even interface is preferred. -// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -module-name Test %t/testable.swift \ +// RUN: %target-swift-frontend -scan-dependencies -scanner-module-validation -module-load-mode prefer-interface -module-name Test %t/testable.swift \ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib -enable-testing \ // RUN: -o %t/deps3.json -I %t/testable -I %t/internal -swift-version 5 -Rmodule-loading // RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps3.json Test directDependencies | %FileCheck %s --check-prefix TEST3 @@ -43,7 +43,7 @@ // TEST3-A: "swift": "B" /// Testable import non-testable build without enable testing. -// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -module-name Test %t/testable.swift \ +// RUN: %target-swift-frontend -scan-dependencies -scanner-module-validation -module-load-mode prefer-interface -module-name Test %t/testable.swift \ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib \ // RUN: -o %t/deps4.json -I %t/regular -swift-version 5 // RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps4.json Test directDependencies | %FileCheck %s --check-prefix TEST4 @@ -51,13 +51,13 @@ // RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps4.json swiftPrebuiltExternal:A directDependencies | %FileCheck %s --check-prefix EMPTY --allow-empty /// Testable import non-testable build enable testing, still succeed since swift-frontend can provide a better diagnostics when the module is actually imported. -// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -module-name Test %t/testable.swift \ +// RUN: %target-swift-frontend -scan-dependencies -scanner-module-validation -module-load-mode prefer-interface -module-name Test %t/testable.swift \ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib -enable-testing \ // RUN: -o %t/deps5.json -I %t/regular -swift-version 5 -Rmodule-loading /// Regular import a testable module with no interface, don't load optional dependencies. // RUN: rm %t/testable/A.swiftinterface -// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -module-name Test %t/main.swift \ +// RUN: %target-swift-frontend -scan-dependencies -scanner-module-validation -module-load-mode prefer-interface -module-name Test %t/main.swift \ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib -enable-testing \ // RUN: -o %t/deps6.json -I %t/testable -swift-version 5 -Rmodule-loading // RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps6.json Test directDependencies | %FileCheck %s --check-prefix TEST6