Skip to content

[5.10 🍒][Dependency Scanning] Attempt to lookup optional transitive dependencies of binary module dependencies #68498 #68502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions include/swift/AST/ModuleDependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,10 @@ class ModuleDependencyInfoStorageBase {

ModuleDependencyInfoStorageBase(ModuleDependencyKind dependencyKind,
const std::vector<std::string> &moduleImports,
const std::vector<std::string> &optionalModuleImports,
StringRef moduleCacheKey = "")
: dependencyKind(dependencyKind), moduleImports(moduleImports),
optionalModuleImports(optionalModuleImports),
moduleCacheKey(moduleCacheKey.str()), resolved(false), finalized(false) {}

virtual ModuleDependencyInfoStorageBase *clone() const = 0;
Expand Down Expand Up @@ -296,11 +298,12 @@ class SwiftBinaryModuleDependencyStorage : public ModuleDependencyInfoStorageBas
const std::string &moduleDocPath,
const std::string &sourceInfoPath,
const std::vector<std::string> &moduleImports,
const std::vector<std::string> &optionalModuleImports,
const std::vector<std::string> &headerImports,
const bool isFramework,
const std::string &moduleCacheKey)
: ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftBinary,
moduleImports, moduleCacheKey),
moduleImports, optionalModuleImports, moduleCacheKey),
compiledModulePath(compiledModulePath), moduleDocPath(moduleDocPath),
sourceInfoPath(sourceInfoPath), preCompiledBridgingHeaderPaths(headerImports),
isFramework(isFramework) {}
Expand Down Expand Up @@ -471,12 +474,14 @@ class ModuleDependencyInfo {
const std::string &moduleDocPath,
const std::string &sourceInfoPath,
const std::vector<std::string> &moduleImports,
const std::vector<std::string> &optionalModuleImports,
const std::vector<std::string> &headerImports,
bool isFramework, const std::string &moduleCacheKey) {
return ModuleDependencyInfo(
std::make_unique<SwiftBinaryModuleDependencyStorage>(
compiledModulePath, moduleDocPath, sourceInfoPath,
moduleImports, headerImports, isFramework, moduleCacheKey));
moduleImports, optionalModuleImports,
headerImports, isFramework, moduleCacheKey));
}

/// Describe the main Swift module.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ using llvm::BCVBR;

/// Every .moddepcache file begins with these 4 bytes, for easy identification.
const unsigned char MODULE_DEPENDENCY_CACHE_FORMAT_SIGNATURE[] = {'I', 'M', 'D','C'};
const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MAJOR = 4;
const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MAJOR = 5; // optionalModuleImports
/// Increment this on every change.
const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MINOR = 1;

Expand Down Expand Up @@ -124,6 +124,7 @@ using ModuleInfoLayout =
IdentifierIDField, // moduleName
ContextHashIDField, // contextHash
ImportArrayIDField, // moduleImports
ImportArrayIDField, // optionalModuleImports
DependencyIDArrayIDField // resolvedDirectModuleDependencies
>;

Expand Down
28 changes: 25 additions & 3 deletions lib/DependencyScan/ModuleDependencyCacheSerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ bool ModuleDependenciesCacheDeserializer::readGraph(SwiftDependencyScanningServi
std::string currentModuleName;
unsigned currentContextHashID;
llvm::Optional<std::vector<std::string>> currentModuleImports;
llvm::Optional<std::vector<std::string>> currentOptionalModuleImports;
llvm::Optional<std::vector<ModuleDependencyID>> currentModuleDependencyIDs;

auto getContextHash = [&]() {
Expand Down Expand Up @@ -212,19 +213,24 @@ bool ModuleDependenciesCacheDeserializer::readGraph(SwiftDependencyScanningServi
case MODULE_NODE: {
hasCurrentModule = true;
unsigned moduleNameID, contextHashID,
moduleImportsArrayID, moduleDependencyIDArrayID;
moduleImportsArrayID, optionalModuleImportsArrayID,
moduleDependencyIDArrayID;
ModuleInfoLayout::readRecord(Scratch, moduleNameID, contextHashID,
moduleImportsArrayID,
optionalModuleImportsArrayID,
moduleDependencyIDArrayID);
auto moduleName = getIdentifier(moduleNameID);
if (!moduleName)
llvm::report_fatal_error("Bad module name");
currentModuleName = *moduleName;
currentContextHashID = contextHashID;
currentModuleImports = getStringArray(moduleImportsArrayID);
currentOptionalModuleImports = getStringArray(optionalModuleImportsArrayID);
currentModuleDependencyIDs = getModuleDependencyIDArray(moduleDependencyIDArrayID);
if (!currentModuleImports)
llvm::report_fatal_error("Bad direct dependencies: no imports");
if (!currentOptionalModuleImports)
llvm::report_fatal_error("Bad direct dependencies: no optional imports");
if (!currentModuleDependencyIDs)
llvm::report_fatal_error("Bad direct dependencies: no qualified dependencies");
break;
Expand Down Expand Up @@ -296,6 +302,9 @@ bool ModuleDependenciesCacheDeserializer::readGraph(SwiftDependencyScanningServi
// Add imports of this module
for (const auto &moduleName : *currentModuleImports)
moduleDep.addModuleImport(moduleName);
// Add optional imports of this module
for (const auto &moduleName : *currentOptionalModuleImports)
moduleDep.addOptionalModuleImport(moduleName);

// Add qualified dependencies of this module
moduleDep.resolveDirectDependencies(*currentModuleDependencyIDs);
Expand Down Expand Up @@ -404,6 +413,9 @@ bool ModuleDependenciesCacheDeserializer::readGraph(SwiftDependencyScanningServi
// Add dependencies of this module
for (const auto &moduleName : *currentModuleImports)
moduleDep.addModuleImport(moduleName);
// Add optional imports of this module
for (const auto &moduleName : *currentOptionalModuleImports)
moduleDep.addOptionalModuleImport(moduleName);

// Add bridging header file path
if (bridgingHeaderFileID != 0) {
Expand Down Expand Up @@ -488,8 +500,8 @@ bool ModuleDependenciesCacheDeserializer::readGraph(SwiftDependencyScanningServi
// Form the dependencies storage object
auto moduleDep = ModuleDependencyInfo::forSwiftBinaryModule(
*compiledModulePath, *moduleDocPath, *moduleSourceInfoPath,
*currentModuleImports, *headerImports, isFramework,
*moduleCacheKey);
*currentModuleImports, *currentOptionalModuleImports,
*headerImports, isFramework, *moduleCacheKey);

cache.recordDependency(currentModuleName, std::move(moduleDep),
getContextHash());
Expand Down Expand Up @@ -523,6 +535,9 @@ bool ModuleDependenciesCacheDeserializer::readGraph(SwiftDependencyScanningServi
// Add dependencies of this module
for (const auto &moduleName : *currentModuleImports)
moduleDep.addModuleImport(moduleName);
// Add optional imports of this module
for (const auto &moduleName : *currentOptionalModuleImports)
moduleDep.addOptionalModuleImport(moduleName);

cache.recordDependency(currentModuleName, std::move(moduleDep),
getContextHash());
Expand Down Expand Up @@ -581,6 +596,9 @@ bool ModuleDependenciesCacheDeserializer::readGraph(SwiftDependencyScanningServi
// Add dependencies of this module
for (const auto &moduleName : *currentModuleImports)
moduleDep.addModuleImport(moduleName);
// Add optional imports of this module
for (const auto &moduleName : *currentOptionalModuleImports)
moduleDep.addOptionalModuleImport(moduleName);

cache.recordDependency(currentModuleName, std::move(moduleDep),
getContextHash());
Expand Down Expand Up @@ -707,6 +725,7 @@ bool swift::dependencies::module_dependency_cache_serialization::
enum ModuleIdentifierArrayKind : uint8_t {
Empty = 0,
DependencyImports,
OptionalDependencyImports,
DependencyHeaders,
QualifiedModuleDependencyIDs,
CompiledModuleCandidates,
Expand Down Expand Up @@ -904,6 +923,7 @@ void ModuleDependenciesCacheSerializer::writeModuleInfo(
Out, ScratchRecord, AbbrCodes[ModuleInfoLayout::Code],
getIdentifier(moduleID.first), contextHashStrID,
getArrayID(moduleID, ModuleIdentifierArrayKind::DependencyImports),
getArrayID(moduleID, ModuleIdentifierArrayKind::OptionalDependencyImports),
getArrayID(moduleID, ModuleIdentifierArrayKind::QualifiedModuleDependencyIDs));

switch (dependencyInfo.getKind()) {
Expand Down Expand Up @@ -1112,6 +1132,8 @@ void ModuleDependenciesCacheSerializer::collectStringsAndArrays(
// Add the module's dependencies
addStringArray(moduleID, ModuleIdentifierArrayKind::DependencyImports,
dependencyInfo->getModuleImports());
addStringArray(moduleID, ModuleIdentifierArrayKind::OptionalDependencyImports,
dependencyInfo->getOptionalModuleImports());
addDependencyIDArray(
moduleID, ModuleIdentifierArrayKind::QualifiedModuleDependencyIDs,
dependencyInfo->getDirectModuleDependencies());
Expand Down
19 changes: 17 additions & 2 deletions lib/Serialization/SerializedModuleLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,15 @@ SerializedModuleLoaderBase::scanModuleFile(Twine modulePath, bool isFramework) {
if (!binaryModuleImports)
return binaryModuleImports.getError();

// Lookup optional imports of this module also
auto binaryModuleOptionalImports = getImportsOfModule(
modulePath, ModuleLoadingBehavior::Optional, isFramework,
isRequiredOSSAModules(), Ctx.LangOpts.SDKName, Ctx.LangOpts.PackageName,
Ctx.SourceMgr.getFileSystem().get(),
Ctx.SearchPathOpts.DeserializedPathRecoverer);
if (!binaryModuleOptionalImports)
return binaryModuleImports.getError();

auto importedModuleSet = binaryModuleImports.get().moduleImports;
std::vector<std::string> importedModuleNames;
importedModuleNames.reserve(importedModuleSet.size());
Expand All @@ -475,11 +484,17 @@ SerializedModuleLoaderBase::scanModuleFile(Twine modulePath, bool isFramework) {
return N.str();
});

auto &importedOptionalModuleSet = binaryModuleOptionalImports.get().moduleImports;
std::vector<std::string> importedOptionalModuleNames;
for (const auto optionalImportedModule : importedOptionalModuleSet.keys())
if (!importedModuleSet.contains(optionalImportedModule))
importedOptionalModuleNames.push_back(optionalImportedModule.str());

// Map the set of dependencies over to the "module dependencies".
auto dependencies = ModuleDependencyInfo::forSwiftBinaryModule(
modulePath.str(), moduleDocPath, sourceInfoPath,
importedModuleNames, importedHeaders, isFramework,
/*module-cache-key*/ "");
importedModuleNames, importedOptionalModuleNames,
importedHeaders, isFramework, /*module-cache-key*/ "");

return std::move(dependencies);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/clang-module-cache)
// RUN: %empty-directory(%t/Foo.swiftmodule)
// RUN: echo "@_implementationOnly import A; public func foo() {}" > %t/Foo.swift
// REQUIRES: executable_test
// REQUIRES: objc_interop

@testable import Foo

// Step 1: build a binary swift module for `Foo`, make it testable
// RUN: %target-swift-frontend -emit-module %t/Foo.swift -emit-module-path %t/Foo.swiftmodule/%target-swiftmodule-name -module-name Foo -I %S/Inputs/CHeaders -I %S/Inputs/Swift -enable-testing

// Step 2: scan dependencies
// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -I %t -sdk %t -prebuilt-module-cache-path %t/clang-module-cache -I %S/Inputs/CHeaders -I %S/Inputs/Swift
// RUN: %validate-json %t/deps.json | %FileCheck %s

// The dependency of `Foo` on `A` will not be visible if the scanner simply scans the textual interface
// of `Foo`. So we verify that for a `@testable` import, the scanner also opens up the adjacent binary module and
// attemtps to resolve optional dependencies contained within.
//
// CHECK: "swift": "A"
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
// Step 1: build swift interface and swift module side by side, make them testable
// 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 -I %S/Inputs/CHeaders -I %S/Inputs/Swift -enable-testing

// Step 3: scan dependencies
// Step 2: scan dependencies
// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -I %t -sdk %t -prebuilt-module-cache-path %t/clang-module-cache -I %S/Inputs/CHeaders -I %S/Inputs/Swift
// RUN: %validate-json %t/deps.json | %FileCheck %s

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
// Step 1: build swift interface and swift module side by side, make them testable
// 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 -I %S/Inputs/CHeaders -I %S/Inputs/Swift -enable-testing -enable-experimental-feature AccessLevelOnImport

// Step 3: scan dependencies
// Step 2: scan dependencies
// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -I %t -sdk %t -prebuilt-module-cache-path %t/clang-module-cache -I %S/Inputs/CHeaders -I %S/Inputs/Swift
// RUN: %validate-json %t/deps.json | %FileCheck %s

Expand Down