From 5310ea397b39081fb52b7b2e40ffe970ad8dddc0 Mon Sep 17 00:00:00 2001 From: Sam Khouri Date: Thu, 4 Sep 2025 15:33:11 -0400 Subject: [PATCH] SwiftBuild: Support Module aliasing Ensure the SwiftBuild integratation support the same Module Aliasing feature set as the native build system. Fixes: #8987 Issue: rdar://157315793 --- .../DirectDeps2/AppPkg/Package.swift | 3 +- .../Resolution/ResolvedProduct.swift | 2 +- .../PackagePIFBuilder+Helpers.swift | 23 +-- .../ModuleAliasingFixtureTests.swift | 134 +++++++++++++----- 4 files changed, 110 insertions(+), 52 deletions(-) diff --git a/Fixtures/ModuleAliasing/DirectDeps2/AppPkg/Package.swift b/Fixtures/ModuleAliasing/DirectDeps2/AppPkg/Package.swift index ea170d60f2a..63294ac0ee7 100644 --- a/Fixtures/ModuleAliasing/DirectDeps2/AppPkg/Package.swift +++ b/Fixtures/ModuleAliasing/DirectDeps2/AppPkg/Package.swift @@ -19,7 +19,8 @@ let package = Package( package: "Bpkg", moduleAliases: ["Utils": "BUtils"] ) - ]), + ] + ), ] ) diff --git a/Sources/PackageGraph/Resolution/ResolvedProduct.swift b/Sources/PackageGraph/Resolution/ResolvedProduct.swift index c6cbbe3bf1d..357cf515316 100644 --- a/Sources/PackageGraph/Resolution/ResolvedProduct.swift +++ b/Sources/PackageGraph/Resolution/ResolvedProduct.swift @@ -179,7 +179,7 @@ public struct ResolvedProduct { extension ResolvedProduct: CustomStringConvertible { public var description: String { - "" + "" } } diff --git a/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift b/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift index fcd0453308b..84c3928f57d 100644 --- a/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift +++ b/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift @@ -104,7 +104,7 @@ extension PackageModel.Product { var pifTargetGUID: GUID { pifTargetGUID(suffix: nil) } func pifTargetGUID(suffix: TargetSuffix?) -> GUID { - PackagePIFBuilder.targetGUID(forProductName: self.name, suffix: suffix) + PackagePIFBuilder.targetGUID(forProductName: self.name, withId:self.identity, suffix: suffix) } } @@ -134,11 +134,11 @@ extension PackagePIFBuilder { /// /// This format helps make sure that there is no collision with any other PIF targets, /// and in particular that a PIF target and a PIF product can have the same name (as they often do). - static func targetGUID(forProductName name: String, suffix: TargetSuffix? = nil) -> GUID { - let suffixDescription = suffix.uniqueDescription(forName: name) - return "PACKAGE-PRODUCT:\(name)\(suffixDescription)" + static func targetGUID(forProductName name: String, withId id: String, suffix: TargetSuffix? = nil) -> GUID { + let suffixDescription: String = suffix.uniqueDescription(forName: name) + return "PACKAGE-PRODUCT:\(id).\(name)\(suffixDescription)" } - + /// Helper function to consistently generate a target name string for a product in a package. /// /// This format helps make sure that modules and products with the same name (as they often have) @@ -775,13 +775,13 @@ extension Collection { /// Recursively applies a block to each of the *dependencies* of the given module, in topological sort order. /// Each module or product dependency is visited only once. func recursivelyTraverseDependencies(with block: (ResolvedModule.Dependency) -> Void) { - var moduleNamesSeen: Set = [] - var productNamesSeen: Set = [] + var moduleGuidsSeen: Set = [] + var productGuidsSeen: Set = [] func visitDependency(_ dependency: ResolvedModule.Dependency) { switch dependency { case .module(let moduleDependency, _): - let (unseenModule, _) = moduleNamesSeen.insert(moduleDependency.name) + let (unseenModule, _) = moduleGuidsSeen.insert(moduleDependency.id) guard unseenModule else { return } if moduleDependency.underlying.type != .macro { @@ -792,7 +792,7 @@ extension Collection { block(dependency) case .product(let productDependency, let conditions): - let (unseenProduct, _) = productNamesSeen.insert(productDependency.name) + let (unseenProduct, _) = productGuidsSeen.insert(productDependency.id) guard unseenProduct && !productDependency.isBinaryOnlyExecutableProduct else { return } block(dependency) @@ -800,7 +800,7 @@ extension Collection { // targets. // This is needed so that XCFramework processing always happens *prior* to building any client targets. for moduleDependency in productDependency.modules where moduleDependency.isBinary { - if moduleNamesSeen.contains(moduleDependency.name) { continue } + if moduleGuidsSeen.contains(moduleDependency.id) { continue } block(.module(moduleDependency, conditions: conditions)) } } @@ -848,12 +848,13 @@ extension ProjectModel.BuildSettings { extension ProjectModel.Project { @discardableResult public mutating func addTarget( + packageProductId: String, packageProductName: String, productType: ProjectModel.Target.ProductType ) throws -> WritableKeyPath { let targetKeyPath = try self.addTarget { _ in ProjectModel.Target( - id: PackagePIFBuilder.targetGUID(forProductName: packageProductName), + id: PackagePIFBuilder.targetGUID(forProductName: packageProductName, withId: packageProductId), productType: productType, name: packageProductName, productName: packageProductName diff --git a/Tests/FunctionalTests/ModuleAliasingFixtureTests.swift b/Tests/FunctionalTests/ModuleAliasingFixtureTests.swift index bdefcfe9fcf..6ee55500792 100644 --- a/Tests/FunctionalTests/ModuleAliasingFixtureTests.swift +++ b/Tests/FunctionalTests/ModuleAliasingFixtureTests.swift @@ -30,32 +30,39 @@ struct ModuleAliasingFixtureTests { .tags( Tag.Feature.Command.Build, ), - arguments: SupportedBuildSystemOnAllPlatforms, BuildConfiguration.allCases, + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), ) func moduleDirectDeps1( - buildSystem: BuildSystemProvider.Kind, - configuration: BuildConfiguration, + data: BuildData, ) async throws { + let buildSystem = data.buildSystem + let configuration = data.config + try await withKnownIssue(isIntermittent: true) { try await fixture(name: "ModuleAliasing/DirectDeps1") { fixturePath in let pkgPath = fixturePath.appending(components: "AppPkg") let buildPath = try pkgPath.appending(components: buildSystem.binPath(for: configuration)) + let expectedModules = [ + "GameUtils.swiftmodule", + "Utils.swiftmodule", + ] try await executeSwiftBuild( pkgPath, configuration: configuration, extraArgs: ["--vv"], buildSystem: buildSystem, ) + expectFileExists(at: buildPath.appending(components: executableName("App"))) - switch buildSystem { + for file in expectedModules { + switch buildSystem { case .native: - expectFileExists(at: buildPath.appending(components: "Modules", "GameUtils.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Modules", "Utils.swiftmodule")) + expectFileExists(at: buildPath.appending(components: "Modules", file)) case .swiftbuild: - expectFileExists(at: buildPath.appending(components: "GameUtils.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Utils.swiftmodule")) + expectFileExists(at: buildPath.appending(components: file)) case .xcode: - #expect(Bool(false), "expectations are not implemented") + Issue.record("expectations are not implemented") + } } _ = try await executeSwiftBuild( pkgPath, @@ -67,22 +74,30 @@ struct ModuleAliasingFixtureTests { ProcessInfo.hostOperatingSystem == .windows && buildSystem == .swiftbuild } } - + @Test( .issue("https://github.com/swiftlang/swift-package-manager/issues/8987", relationship: .defect), + .issue("https://github.com/swiftlang/swift-package-manager/pull/9130", relationship: .fixedBy), + .IssueWindowsLongPath, + .IssueWindowsCannotSaveAttachment, .tags( Tag.Feature.Command.Build, ), - arguments: SupportedBuildSystemOnAllPlatforms, BuildConfiguration.allCases, + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), ) func moduleDirectDeps2( - buildSystem: BuildSystemProvider.Kind, - configuration: BuildConfiguration, + data: BuildData ) async throws { - try await withKnownIssue { + let buildSystem = data.buildSystem + let configuration = data.config + try await withKnownIssue(isIntermittent: true) { try await fixture(name: "ModuleAliasing/DirectDeps2") { fixturePath in let pkgPath = fixturePath.appending(components: "AppPkg") let buildPath = try pkgPath.appending(components: buildSystem.binPath(for: configuration)) + let expectedModules = [ + "AUtils.swiftmodule", + "BUtils.swiftmodule", + ] try await executeSwiftBuild( pkgPath, configuration: configuration, @@ -90,8 +105,16 @@ struct ModuleAliasingFixtureTests { buildSystem: buildSystem, ) expectFileExists(at: buildPath.appending(components: executableName("App"))) - expectFileExists(at: buildPath.appending(components: "Modules", "AUtils.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Modules", "BUtils.swiftmodule")) + for file in expectedModules { + switch buildSystem { + case .native: + expectFileExists(at: buildPath.appending(components: "Modules", file)) + case .swiftbuild: + expectFileExists(at: buildPath.appending(components: file)) + case .xcode: + Issue.record("expectations are not implemented") + } + } _ = try await executeSwiftBuild( pkgPath, configuration: configuration, @@ -99,25 +122,37 @@ struct ModuleAliasingFixtureTests { ) } } when: { - buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && buildSystem == .swiftbuild } } - + @Test( .issue("https://github.com/swiftlang/swift-package-manager/issues/8987", relationship: .defect), + .issue("https://github.com/swiftlang/swift-package-manager/pull/9130", relationship: .fixedBy), + .IssueWindowsLongPath, + .IssueWindowsCannotSaveAttachment, .tags( Tag.Feature.Command.Build, ), - arguments: SupportedBuildSystemOnAllPlatforms, BuildConfiguration.allCases, + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), ) func moduleNestedDeps1( - buildSystem: BuildSystemProvider.Kind, - configuration: BuildConfiguration, + data: BuildData, ) async throws { - try await withKnownIssue { + let buildSystem = data.buildSystem + let configuration = data.config + try await withKnownIssue(isIntermittent: true) { try await fixture(name: "ModuleAliasing/NestedDeps1") { fixturePath in let pkgPath = fixturePath.appending(components: "AppPkg") let buildPath = try pkgPath.appending(components: buildSystem.binPath(for: configuration)) + let expectedModules = [ + "A.swiftmodule", + "AFooUtils.swiftmodule", + "CarUtils.swiftmodule", + "X.swiftmodule", + "XFooUtils.swiftmodule", + "XUtils.swiftmodule", + ] try await executeSwiftBuild( pkgPath, configuration: configuration, @@ -125,12 +160,17 @@ struct ModuleAliasingFixtureTests { buildSystem: buildSystem, ) expectFileExists(at: buildPath.appending(components: executableName("App"))) - expectFileExists(at: buildPath.appending(components: "Modules", "A.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Modules", "AFooUtils.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Modules", "CarUtils.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Modules", "X.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Modules", "XFooUtils.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Modules", "XUtils.swiftmodule")) + for file in expectedModules { + switch buildSystem { + case .native: + expectFileExists(at: buildPath.appending(components: "Modules", file)) + case .swiftbuild: + expectFileExists(at: buildPath.appending(components: file)) + case .xcode: + Issue.record("expectations are not implemented") + } + } + _ = try await executeSwiftBuild( pkgPath, configuration: configuration, @@ -138,22 +178,26 @@ struct ModuleAliasingFixtureTests { ) } } when: { - buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && buildSystem == .swiftbuild } } - + @Test( .issue("https://github.com/swiftlang/swift-package-manager/issues/8987", relationship: .defect), + .issue("https://github.com/swiftlang/swift-package-manager/pull/9130", relationship: .fixedBy), + .IssueWindowsLongPath, + .IssueWindowsCannotSaveAttachment, .tags( Tag.Feature.Command.Build, ), - arguments: SupportedBuildSystemOnAllPlatforms, BuildConfiguration.allCases, + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), ) func moduleNestedDeps2( - buildSystem: BuildSystemProvider.Kind, - configuration: BuildConfiguration, + data: BuildData, ) async throws { - try await withKnownIssue { + let buildSystem = data.buildSystem + let configuration = data.config + try await withKnownIssue(isIntermittent: true) { try await fixture(name: "ModuleAliasing/NestedDeps2") { fixturePath in let pkgPath = fixturePath.appending(components: "AppPkg") let buildPath = try pkgPath.appending(components: buildSystem.binPath(for: configuration)) @@ -163,11 +207,23 @@ struct ModuleAliasingFixtureTests { extraArgs: ["--vv"], buildSystem: buildSystem, ) + let expectedModules = [ + "A.swiftmodule", + "BUtils.swiftmodule", + "CUtils.swiftmodule", + "XUtils.swiftmodule", + ] expectFileExists(at: buildPath.appending(components: executableName("App"))) - expectFileExists(at: buildPath.appending(components: "Modules", "A.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Modules", "BUtils.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Modules", "CUtils.swiftmodule")) - expectFileExists(at: buildPath.appending(components: "Modules", "XUtils.swiftmodule")) + for file in expectedModules { + switch buildSystem { + case .native: + expectFileExists(at: buildPath.appending(components: "Modules", file)) + case .swiftbuild: + expectFileExists(at: buildPath.appending(components: file)) + case .xcode: + Issue.record("expectations are not implemented") + } + } _ = try await executeSwiftBuild( pkgPath, configuration: configuration, @@ -175,7 +231,7 @@ struct ModuleAliasingFixtureTests { ) } } when: { - buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && buildSystem == .swiftbuild } } }