Skip to content

[Dependency Scanning] Resolve cross-import overlays relative to defining interface for prebuilt binary Swift dependencies #74945

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
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
24 changes: 20 additions & 4 deletions include/swift/AST/ModuleDependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,12 +397,14 @@ class SwiftBinaryModuleDependencyStorage
ArrayRef<ScannerImportStatementInfo> moduleImports,
ArrayRef<ScannerImportStatementInfo> optionalModuleImports,
ArrayRef<LinkLibrary> linkLibraries, StringRef headerImport,
bool isFramework, bool isStatic, StringRef moduleCacheKey)
StringRef definingModuleInterface, bool isFramework, bool isStatic,
StringRef moduleCacheKey)
: ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftBinary,
moduleImports, optionalModuleImports,
linkLibraries, moduleCacheKey),
compiledModulePath(compiledModulePath), moduleDocPath(moduleDocPath),
sourceInfoPath(sourceInfoPath), headerImport(headerImport),
definingModuleInterfacePath(definingModuleInterface),
isFramework(isFramework), isStatic(isStatic) {}

ModuleDependencyInfoStorageBase *clone() const override {
Expand All @@ -421,6 +423,10 @@ class SwiftBinaryModuleDependencyStorage
/// The path of the .h dependency of this module.
const std::string headerImport;

/// The path of the defining .swiftinterface that this
/// binary .swiftmodule was built from, if one exists.
const std::string definingModuleInterfacePath;

/// Source files on which the header inputs depend.
std::vector<std::string> headerSourceFiles;

Expand All @@ -433,6 +439,15 @@ class SwiftBinaryModuleDependencyStorage
/// A flag that indicates this dependency is associated with a static archive
const bool isStatic;

/// Return the path to the defining .swiftinterface of this module
/// of one was determined. Otherwise, return the .swiftmodule path
/// itself.
std::string getDefiningModulePath() const {
if (definingModuleInterfacePath.empty())
return compiledModulePath;
return definingModuleInterfacePath;
}

static bool classof(const ModuleDependencyInfoStorageBase *base) {
return base->dependencyKind == ModuleDependencyKind::SwiftBinary;
}
Expand Down Expand Up @@ -588,12 +603,13 @@ class ModuleDependencyInfo {
ArrayRef<ScannerImportStatementInfo> moduleImports,
ArrayRef<ScannerImportStatementInfo> optionalModuleImports,
ArrayRef<LinkLibrary> linkLibraries, StringRef headerImport,
bool isFramework, bool isStatic, StringRef moduleCacheKey) {
StringRef definingModuleInterface, bool isFramework,
bool isStatic, StringRef moduleCacheKey) {
return ModuleDependencyInfo(
std::make_unique<SwiftBinaryModuleDependencyStorage>(
compiledModulePath, moduleDocPath, sourceInfoPath, moduleImports,
optionalModuleImports, linkLibraries, headerImport, isFramework,
isStatic, moduleCacheKey));
optionalModuleImports, linkLibraries, headerImport,
definingModuleInterface,isFramework, isStatic, moduleCacheKey));
}

/// Describe the main Swift module.
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/ModuleLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ ModuleDependencyInfo::collectCrossImportOverlayNames(
}
case swift::ModuleDependencyKind::SwiftBinary: {
auto *swiftBinaryDep = getAsSwiftBinaryModule();
modulePath = swiftBinaryDep->compiledModulePath;
modulePath = swiftBinaryDep->getDefiningModulePath();
assert(modulePath.has_value());
StringRef parentDir = llvm::sys::path::parent_path(*modulePath);
if (llvm::sys::path::extension(parentDir) == ".swiftmodule") {
Expand Down
4 changes: 2 additions & 2 deletions lib/DependencyScan/ModuleDependencyCacheSerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,12 +514,12 @@ bool ModuleDependenciesCacheDeserializer::readGraph(SwiftDependencyScanningServi
if (!headerImport)
llvm::report_fatal_error("Bad binary direct dependencies: no header import");

// TODO: LinkLibraries
// TODO: LinkLibraries, DefiningModulePath
// Form the dependencies storage object
auto moduleDep = ModuleDependencyInfo::forSwiftBinaryModule(
*compiledModulePath, *moduleDocPath, *moduleSourceInfoPath,
currentModuleImports, currentOptionalModuleImports, {},
*headerImport, isFramework, isStatic, *moduleCacheKey);
*headerImport, "", isFramework, isStatic, *moduleCacheKey);

auto headerModuleDependencies = getStringArray(headerModuleDependenciesArrayID);
if (!headerModuleDependencies)
Expand Down
16 changes: 2 additions & 14 deletions lib/Serialization/ModuleFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,6 @@ static bool isTargetTooNew(const llvm::Triple &moduleTarget,
return ctxTarget.isOSVersionLT(moduleTarget);
}

std::string ModuleFile::resolveModuleDefiningFilename(const ASTContext &ctx) {
if (!Core->ModuleInterfacePath.empty()) {
std::string interfacePath = Core->ModuleInterfacePath.str();
if (llvm::sys::path::is_relative(interfacePath)) {
SmallString<128> absoluteInterfacePath(ctx.SearchPathOpts.getSDKPath());
llvm::sys::path::append(absoluteInterfacePath, interfacePath);
return absoluteInterfacePath.str().str();
} else
return interfacePath;
} else
return getModuleLoadedFilename().str();
}

namespace swift {
namespace serialization {
bool areCompatible(const llvm::Triple &moduleTarget,
Expand Down Expand Up @@ -272,7 +259,8 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,

ASTContext &ctx = getContext();
// Resolve potentially-SDK-relative module-defining .swiftinterface path
ResolvedModuleDefiningFilename = resolveModuleDefiningFilename(ctx);
ResolvedModuleDefiningFilename =
Core->resolveModuleDefiningFilePath(ctx.SearchPathOpts.getSDKPath());

llvm::Triple moduleTarget(llvm::Triple::normalize(Core->TargetTriple));
if (!areCompatible(moduleTarget, ctx.LangOpts.Target)) {
Expand Down
4 changes: 0 additions & 4 deletions lib/Serialization/ModuleFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,6 @@ class ModuleFile
ArrayRef<ProtocolConformanceID>
claimLazyConformanceLoaderToken(uint64_t token);

/// If the module-defining `.swiftinterface` file is an SDK-relative path,
/// resolve it to be absolute to the context's SDK.
std::string resolveModuleDefiningFilename(const ASTContext &ctx);

/// Represents an identifier that may or may not have been deserialized yet.
///
/// If \c Ident is empty, the identifier has not been loaded yet.
Expand Down
13 changes: 13 additions & 0 deletions lib/Serialization/ModuleFileSharedCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,19 @@ bool ModuleFileSharedCore::hasSourceInfo() const {
return !!DeclUSRsTable;
}

std::string ModuleFileSharedCore::resolveModuleDefiningFilePath(const StringRef SDKPath) const {
if (!ModuleInterfacePath.empty()) {
std::string interfacePath = ModuleInterfacePath.str();
if (llvm::sys::path::is_relative(interfacePath)) {
SmallString<128> absoluteInterfacePath(SDKPath);
llvm::sys::path::append(absoluteInterfacePath, interfacePath);
return absoluteInterfacePath.str().str();
} else
return interfacePath;
} else
return ModuleInputBuffer->getBufferIdentifier().str();
}

ModuleLoadingBehavior
ModuleFileSharedCore::getTransitiveLoadingBehavior(
const Dependency &dependency,
Expand Down
4 changes: 4 additions & 0 deletions lib/Serialization/ModuleFileSharedCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,10 @@ class ModuleFileSharedCore {
return Bits.IsStaticLibrary;
}

/// If the module-defining `.swiftinterface` file is an SDK-relative path,
/// resolve it to be absolute to the specified SDK.
std::string resolveModuleDefiningFilePath(const StringRef SDKPath) const;

/// Returns \c true if this module file contains a section with incremental
/// information.
bool hasIncrementalInfo() const { return HasIncrementalInfo; }
Expand Down
9 changes: 7 additions & 2 deletions lib/Serialization/SerializedModuleLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,11 +511,16 @@ SerializedModuleLoaderBase::scanModuleFile(Twine modulePath, bool isFramework,
LibraryKind::Framework));
}

// Attempt to resolve the module's defining .swiftinterface path
std::string definingModulePath =
loadedModuleFile->resolveModuleDefiningFilePath(Ctx.SearchPathOpts.getSDKPath());

// Map the set of dependencies over to the "module dependencies".
auto dependencies = ModuleDependencyInfo::forSwiftBinaryModule(
modulePath.str(), moduleDocPath, sourceInfoPath, moduleImports,
optionalModuleImports, linkLibraries, importedHeader, isFramework,
loadedModuleFile->isStaticLibrary(), /*module-cache-key*/ "");
optionalModuleImports, linkLibraries, importedHeader,
definingModulePath, isFramework, loadedModuleFile->isStaticLibrary(),
/*module-cache-key*/ "");

return std::move(dependencies);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// REQUIRES: objc_interop
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/module-cache)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the first empty-directory covers everything already?

// RUN: %empty-directory(%t/inputs)
// RUN: %empty-directory(%t/binaryInputs)
// RUN: %empty-directory(%t/separateModules)
// RUN: %empty-directory(%t/inputs/Foo.swiftcrossimport)
// RUN: split-file %s %t

// - Fixup the input module file map
// RUN: sed -e "s|INPUTSDIR|%/t/inputs|g" %t/map.json.template > %t/map.json.template1
// RUN: sed -e "s|STDLIBMOD|%/stdlib_module|g" %t/map.json.template1 > %t/map.json.template2
// RUN: sed -e "s|ONONEMOD|%/ononesupport_module|g" %t/map.json.template2 > %t/map.json.template3
// RUN: sed -e "s|SWIFTLIBDIR|%swift-lib-dir|g" %t/map.json.template3 > %t/map.json

// - Pre-compile explicit module dependency inputs
// RUN: %target-swift-emit-pcm -module-name SwiftShims %swift-lib-dir/swift/shims/module.modulemap -o %t/inputs/SwiftShims.pcm

// - Pre-compile the Foo module into a separately-stored binary module
// RUN: %target-swift-frontend -compile-module-from-interface %t/separateModules/Foo.swiftinterface -o %t/binaryInputs/Foo.swiftmodule -module-name Foo -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -disable-implicit-swift-modules -Xcc -fno-implicit-modules -Xcc -fno-implicit-module-maps -explicit-swift-module-map-file %t/map.json

// - Run a dependency scan on test.swift which will pick-up the ready-made binary dependency on Foo.swiftmodule
// and use the binary module's serialized originating defining .swiftinterface path to be able to
// discover the cross-import overlay _Foo_Bar.
// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/module-cache %t/test.swift -o %t/deps.json -I %t/inputs -I %t/binaryInputs -module-name test -enable-cross-import-overlays -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import

// CHECK: "mainModuleName": "test"
// CHECK: "swift": "test"
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "modulePath": "test.swiftmodule"
// CHECK-NEXT: "sourceFiles": [
// CHECK-NEXT: "{{.*}}{{/|\\}}module_deps_cross_import_of_binary_module.swift.tmp{{/|\\}}test.swift"
// CHECK-NEXT: ]
// CHECK-NEXT: "directDependencies": [
// CHECK-DAG: "swiftPrebuiltExternal": "Swift"
// CHECK-DAG: "swiftPrebuiltExternal": "SwiftOnoneSupport"
// CHECK-DAG: "swiftPrebuiltExternal": "Foo"
// CHECK-DAG: "swift": "Bar"
// CHECK-DAG: "swift": "_Foo_Bar"

//--- map.json.template
[
{
"moduleName": "Swift",
"modulePath": "STDLIBMOD",
"isFramework": false
},
{
"moduleName": "SwiftOnoneSupport",
"modulePath": "ONONEMOD",
"isFramework": false
},
{
"moduleName": "SwiftShims",
"isFramework": false,
"clangModuleMapPath": "SWIFTLIBDIR/swift/shims/module.modulemap",
"clangModulePath": "INPUTSDIR/SwiftShims.pcm"
}]

//--- test.swift
import Foo
import Bar

//--- separateModules/Foo.swiftinterface
// swift-interface-format-version: 1.0
// swift-module-flags: -module-name Foo
public func foo() {}

//--- inputs/Bar.swiftinterface
// swift-interface-format-version: 1.0
// swift-module-flags: -module-name Bar
public func bar() {}

//--- inputs/_Foo_Bar.swiftinterface
// swift-interface-format-version: 1.0
// swift-module-flags: -module-name _Foo_Bar
public func foobar() {}

//--- separateModules/Foo.swiftcrossimport/Bar.swiftoverlay
%YAML 1.2
---
version: 1
modules:
- name: _Foo_Bar