Skip to content

Commit ced591b

Browse files
authored
Module aliasing: validate source files / only allow Swift files for modules aliased (#4303)
Resolves rdar://90661600
1 parent 8439932 commit ced591b

File tree

4 files changed

+291
-37
lines changed

4 files changed

+291
-37
lines changed

Sources/PackageGraph/PackageGraph+Loading.swift

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,8 @@ private func createResolvedPackages(
271271
}
272272

273273
// Gather and resolve module aliases specified for targets in all dependent packages
274-
let packageAliases = resolveModuleAliases(packageBuilders: packageBuilders,
275-
observabilityScope: observabilityScope)
274+
let packageAliases = try resolveModuleAliases(packageBuilders: packageBuilders,
275+
observabilityScope: observabilityScope)
276276

277277
// Scan and validate the dependencies
278278
for packageBuilder in packageBuilders {
@@ -621,7 +621,7 @@ private func computePlatforms(
621621

622622
// Track and override module aliases specified for targets in a package graph
623623
private func resolveModuleAliases(packageBuilders: [ResolvedPackageBuilder],
624-
observabilityScope: ObservabilityScope) -> [PackageIdentity: [String: [ModuleAliasModel]]]? {
624+
observabilityScope: ObservabilityScope) throws -> [PackageIdentity: [String: [ModuleAliasModel]]]? {
625625

626626
// If there are no module aliases specified, return early
627627
let hasAliases = packageBuilders.contains { $0.package.targets.contains {
@@ -647,17 +647,12 @@ private func resolveModuleAliases(packageBuilders: [ResolvedPackageBuilder],
647647

648648
if let aliasList = prodRef.moduleAliases {
649649
for (depName, depAlias) in aliasList {
650-
if let existingAlias = aliasTracker.alias(target: depName, originPackage: prodPkgID) {
651-
// Error if there are multiple aliases specified for this product dependency
652-
observabilityScope.emit(PackageGraphError.multipleModuleAliases(target: depName, product: prodRef.name, package: prodPkg, aliases: [existingAlias, depAlias]))
653-
return nil
654-
}
655650
// Track aliases for this product
656-
aliasTracker.addAlias(depAlias,
657-
target: depName,
658-
product: prodRef.name,
659-
originPackage: PackageIdentity.plain(prodPkg),
660-
consumingPackage: packageBuilder.package.identity)
651+
try aliasTracker.addAlias(depAlias,
652+
target: depName,
653+
product: prodRef.name,
654+
originPackage: PackageIdentity.plain(prodPkg),
655+
consumingPackage: packageBuilder.package.identity)
661656
}
662657
}
663658
}
@@ -667,16 +662,31 @@ private func resolveModuleAliases(packageBuilders: [ResolvedPackageBuilder],
667662

668663
// Track targets that need module aliases for each package
669664
for packageBuilder in packageBuilders {
670-
for produdct in packageBuilder.package.products {
671-
var list = produdct.targets.map{$0.dependencies}.flatMap{$0}.compactMap{$0.target?.name}
672-
list.append(contentsOf: produdct.targets.map{$0.name})
673-
aliasTracker.addAliasesForTargets(list, product: produdct.name, package: packageBuilder.package.identity)
665+
for product in packageBuilder.package.products {
666+
var allTargets = product.targets.map{$0.dependencies}.flatMap{$0}.compactMap{$0.target}
667+
allTargets.append(contentsOf: product.targets)
668+
aliasTracker.addAliasesForTargets(allTargets,
669+
product: product.name,
670+
package: packageBuilder.package.identity)
674671
}
675672
}
676673

677674
// Override module aliases upstream if needed
678675
aliasTracker.propagateAliases()
679676

677+
// Validate sources (Swift files only) for modules being aliased.
678+
// Needs to be done after `propagateAliases` since aliases defined
679+
// upstream can be overriden.
680+
for packageBuilder in packageBuilders {
681+
for product in packageBuilder.package.products {
682+
var allTargets = product.targets.map{$0.dependencies}.flatMap{$0}.compactMap{$0.target}
683+
allTargets.append(contentsOf: product.targets)
684+
try aliasTracker.validateSources(allTargets,
685+
product: product.name,
686+
package: packageBuilder.package.identity)
687+
}
688+
}
689+
680690
return aliasTracker.idTargetToAliases
681691
}
682692

@@ -694,7 +704,15 @@ private class ModuleAliasTracker {
694704
target: String,
695705
product: String,
696706
originPackage: PackageIdentity,
697-
consumingPackage: PackageIdentity) {
707+
consumingPackage: PackageIdentity) throws {
708+
if let aliasDict = aliasMap[originPackage] {
709+
let models = aliasDict.values.flatMap{$0}.filter { $0.name == target }
710+
if !models.isEmpty {
711+
// Error if there are multiple aliases specified for this product dependency
712+
throw PackageGraphError.multipleModuleAliases(target: target, product: product, package: originPackage.description, aliases: models.map{$0.alias} + [alias])
713+
}
714+
}
715+
698716
let model = ModuleAliasModel(name: target, alias: alias, originPackage: originPackage, consumingPackage: consumingPackage)
699717
aliasMap[originPackage, default: [:]][product, default: []].append(model)
700718
}
@@ -710,31 +728,31 @@ private class ModuleAliasTracker {
710728
}
711729
}
712730

713-
func addAliasesForTargets(_ targets: [String],
731+
func addAliasesForTargets(_ targets: [Target],
714732
product: String,
715733
package: PackageIdentity) {
716-
717734
let aliases = aliasMap[package]?[product]
718-
for targetName in targets {
719-
if idTargetToAliases[package]?[targetName] == nil {
720-
idTargetToAliases[package, default: [:]][targetName] = []
735+
for target in targets {
736+
if idTargetToAliases[package]?[target.name] == nil {
737+
idTargetToAliases[package, default: [:]][target.name] = []
721738
}
722739

723740
if let aliases = aliases {
724-
idTargetToAliases[package]?[targetName]?.append(contentsOf: aliases)
741+
idTargetToAliases[package]?[target.name]?.append(contentsOf: aliases)
725742
}
726743
}
727744
}
728745

729-
func alias(target: String,
730-
originPackage: PackageIdentity) -> String? {
731-
if let aliasDict = aliasMap[originPackage] {
732-
let models = aliasDict.values.flatMap{$0}.filter { $0.name == target }
733-
// this func only checks if there's any existing alias so
734-
// just return the first alias value
735-
return models.first?.alias
746+
func validateSources(_ targets: [Target],
747+
product: String,
748+
package: PackageIdentity) throws {
749+
for target in targets {
750+
if let aliases = idTargetToAliases[package]?[target.name], !aliases.isEmpty {
751+
if target.sources.containsNonSwiftFiles {
752+
throw PackageGraphError.invalidSourcesForModuleAliasing(target: target.name, product: product, package: package.description)
753+
}
754+
}
736755
}
737-
return nil
738756
}
739757

740758
func propagateAliases() {

Sources/PackageGraph/PackageGraph.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ enum PackageGraphError: Swift.Error {
4444
product: String,
4545
package: String,
4646
aliases: [String])
47+
48+
/// Invalid sources found (only Swift files allowed) for aliasing a module
49+
/// specified by a given target/product/package.
50+
case invalidSourcesForModuleAliasing(target: String,
51+
product: String,
52+
package: String)
4753
}
4854

4955
/// A collection of packages.
@@ -227,6 +233,8 @@ extension PackageGraphError: CustomStringConvertible {
227233
return "multiple products named '\(product)' in: '\(packages.joined(separator: "', '"))'"
228234
case .multipleModuleAliases(let target, let product, let package, let aliases):
229235
return "multiple aliases: ['\(aliases.joined(separator: "', '"))'] found for target '\(target)' in product '\(product)' from package '\(package)'"
236+
case .invalidSourcesForModuleAliasing(let target, let product, let package):
237+
return "module aliasing can only be used for Swift based targets; non-Swift sources found in target '\(target)' for product '\(product)' from package '\(package)'"
230238
}
231239
}
232240
}

Sources/PackageModel/Sources.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,13 @@ public struct Sources: Codable {
5050
return ext == SupportedLanguageExtension.m.rawValue || ext == SupportedLanguageExtension.mm.rawValue
5151
})
5252
}
53+
54+
public var containsNonSwiftFiles: Bool {
55+
return paths.contains(where: {
56+
guard let ext = $0.extension else {
57+
return false
58+
}
59+
return !SupportedLanguageExtension.swiftExtensions.contains(ext)
60+
})
61+
}
5362
}

0 commit comments

Comments
 (0)