@@ -197,24 +197,24 @@ private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], obse
197
197
198
198
extension Package {
199
199
// Add module aliases specified for applicable targets
200
- fileprivate func setModuleAliasesForTargets( with moduleAliasMap: [ String : String ] ) {
200
+ fileprivate func setModuleAliasesForTargets( with moduleAliasMap: [ String : [ ModuleAliasModel ] ] ) {
201
201
// Set module aliases for each target's dependencies
202
- for (entryName , entryAlias ) in moduleAliasMap {
203
- for target in self . targets {
204
- // First add dependency module aliases for this target
205
- if entryName != target. name {
206
- target. addModuleAlias ( for: entryName , as: entryAlias )
202
+ for target in self . targets {
203
+ let aliasesForTarget = moduleAliasMap . filter { $0 . key == target . name } . values . flatMap { $0 }
204
+ for entry in aliasesForTarget {
205
+ if entry . name != target. name {
206
+ target. addModuleAlias ( for: entry . name , as: entry . alias )
207
207
}
208
208
}
209
209
}
210
210
211
211
// This loop should run after the loop above as it may rename the target
212
212
// as an alias if specified
213
- for (entryName , entryAlias ) in moduleAliasMap {
214
- for target in self . targets {
215
- // Then set this target to be aliased if specified
216
- if entryName == target. name {
217
- target. addModuleAlias ( for: target . name, as: entryAlias )
213
+ for target in self . targets {
214
+ let aliasesForTarget = moduleAliasMap . filter { $0 . key == target . name } . values . flatMap { $0 }
215
+ for entry in aliasesForTarget {
216
+ if entry . name == target. name {
217
+ target. addModuleAlias ( for: entry . name, as: entry . alias )
218
218
}
219
219
}
220
220
}
@@ -264,8 +264,9 @@ private func createResolvedPackages(
264
264
return ( $0. package . identity, $0)
265
265
}
266
266
267
- // Gather all module aliases specified for targets in all dependent packages
268
- let packageAliases = gatherModuleAliases ( from: packageBuilders, for: rootManifests. first? . key, with: packagesByIdentity, onError: observabilityScope)
267
+ // Gather and resolve module aliases specified for targets in all dependent packages
268
+ let packageAliases = resolveModuleAliases ( with: packageBuilders,
269
+ onError: observabilityScope)
269
270
270
271
// Scan and validate the dependencies
271
272
for packageBuilder in packageBuilders {
@@ -522,11 +523,10 @@ private func createResolvedPackages(
522
523
return try packageBuilders. map { try $0. construct ( ) }
523
524
}
524
525
525
- // Create a map between a package and module aliases specified for the targets in the package.
526
- private func gatherModuleAliases( from packageBuilders: [ ResolvedPackageBuilder ] ,
527
- for rootPkgID: PackageIdentity ? ,
528
- with packagesByIdentity: [ PackageIdentity : ResolvedPackageBuilder ] ,
529
- onError observabilityScope: ObservabilityScope ) -> [ PackageIdentity : [ String : String ] ] ? {
526
+ // Track and override module aliases specified for targets in a package graph
527
+ private func resolveModuleAliases( with packageBuilders: [ ResolvedPackageBuilder ] ,
528
+ onError observabilityScope: ObservabilityScope ) -> [ PackageIdentity : [ String : [ ModuleAliasModel ] ] ] ? {
529
+
530
530
// If there are no aliases, return early
531
531
let depsWithAliases = packageBuilders. map { $0. package . targets. map { $0. dependencies. filter { dep in
532
532
if case let . product( prodRef, _) = dep {
@@ -536,79 +536,165 @@ private func gatherModuleAliases(from packageBuilders: [ResolvedPackageBuilder],
536
536
} } } . flatMap { $0} . flatMap { $0}
537
537
538
538
guard !depsWithAliases. isEmpty else { return nil }
539
-
540
- var result = [ PackageIdentity: [ String: String] ] ( )
541
539
542
- // There could be multiple root packages but the common cases involve
543
- // just one root package; handling multiple roots is tracked rdar://88518683
544
- if let rootPkg = rootPkgID {
545
- var pkgStack = [ PackageIdentity] ( )
546
- walkPkgTreeAndGetModuleAliases ( for: rootPkg, with: packagesByIdentity, onError: observabilityScope, using: & pkgStack, output: & result)
540
+ let aliasTracker = ModuleAliasTracker ( )
541
+ for packageBuilder in packageBuilders {
542
+ for target in packageBuilder. package . targets {
543
+ for dep in target. dependencies {
544
+ if case let . product( prodRef, _) = dep,
545
+ let prodPkg = prodRef. package {
546
+ let prodPkgID = PackageIdentity . plain ( prodPkg)
547
+ // Track package ID dependency chain
548
+ aliasTracker. addPackageIDChain ( parent: packageBuilder. package . identity, child: prodPkgID)
549
+
550
+ if let aliasList = prodRef. moduleAliases {
551
+ for (depName, depAlias) in aliasList {
552
+ if let existingAlias = aliasTracker. alias ( of: depName, in: prodPkgID) {
553
+ // Error if there are multiple aliases specified for this product dependency
554
+ observabilityScope. emit ( PackageGraphError . multipleModuleAliases ( target: depName, product: prodRef. name, package : prodPkg, aliases: [ existingAlias, depAlias] ) )
555
+ return nil
556
+ }
557
+ // Track aliases for this product
558
+ aliasTracker. addAlias ( depAlias,
559
+ of: depName,
560
+ for: prodRef. name,
561
+ from: PackageIdentity . plain ( prodPkg) ,
562
+ in: packageBuilder. package . identity)
563
+ }
564
+ }
565
+ }
566
+ }
567
+ }
547
568
}
548
- return result
569
+
570
+ // Track targets that need module aliases for each package
571
+ for packageBuilder in packageBuilders {
572
+ for prod in packageBuilder. package . products {
573
+ var list = prod. targets. map { $0. dependencies} . flatMap { $0} . compactMap { $0. target? . name}
574
+ list. append ( contentsOf: prod. targets. map { $0. name} )
575
+ aliasTracker. addAliasesForTargets ( list, for: prod. name, in: packageBuilder. package . identity)
576
+ }
577
+ }
578
+
579
+ // Override module aliases upstream if needed
580
+ aliasTracker. propagateAliases ( )
581
+
582
+ return aliasTracker. idTargetToAliases
549
583
}
550
584
551
- // Walk a package dependency tree and set the module aliases for targets in each package.
552
- // If multiple aliases are specified in upstream packages, aliases specified most downstream
553
- // will be used.
554
- private func walkPkgTreeAndGetModuleAliases( for pkgID: PackageIdentity ,
555
- with packagesByIdentity: [ PackageIdentity : ResolvedPackageBuilder ] ,
556
- onError observabilityScope: ObservabilityScope ,
557
- using pkgStack: inout [ PackageIdentity ] ,
558
- output result: inout [ PackageIdentity : [ String : String ] ] ) {
559
- // Get the builder first
560
- if let builder = packagesByIdentity [ pkgID] {
561
- builder. package . targets. forEach { target in
562
- target. dependencies. forEach { dep in
563
- // Check if a dependency for this target has module aliases specified
564
- if case let . product( prodRef, _) = dep {
565
- if let prodPkg = prodRef. package {
566
- if let prodModuleAliases = prodRef. moduleAliases {
567
- for (depName, depAlias) in prodModuleAliases {
568
- let prodPkgID = PackageIdentity . plain ( prodPkg)
569
- if let existingAlias = result [ prodPkgID, default: [ : ] ] [ depName] {
570
- // error if there are multiple aliases for
571
- // a dependency target for a product
572
- observabilityScope. emit ( PackageGraphError . multipleModuleAliases ( target: depName, product: prodRef. name, package : prodPkg, aliases: [ existingAlias, depAlias] ) )
573
- return
574
- }
585
+ // This class helps track module aliases in a package graph and override
586
+ // upstream alises if needed
587
+ private class ModuleAliasTracker {
588
+ var aliasMap = [ PackageIdentity: [ String: [ ModuleAliasModel] ] ] ( )
589
+ var idTargetToAliases = [ PackageIdentity: [ String: [ ModuleAliasModel] ] ] ( )
590
+ var parentToChildIDs = [ PackageIdentity: [ PackageIdentity] ] ( )
591
+ var childToParentID = [ PackageIdentity: PackageIdentity] ( )
592
+
593
+ init ( ) { }
594
+
595
+ func addAlias( _ alias: String ,
596
+ of targetName: String ,
597
+ for product: String ,
598
+ from originPackage: PackageIdentity ,
599
+ in parentPackage: PackageIdentity ) {
600
+ let model = ModuleAliasModel ( name: targetName, alias: alias, originPackage: originPackage, parentPackage: parentPackage)
601
+ aliasMap [ originPackage, default: [ : ] ] [ product, default: [ ] ] . append ( model)
602
+ }
575
603
576
- // Add the specified alias and the dependency package to a map
577
- result [ prodPkgID, default: [ : ] ] [ depName] = depAlias
604
+ func addPackageIDChain( parent: PackageIdentity ,
605
+ child: PackageIdentity ) {
606
+ if parentToChildIDs [ parent] ? . contains ( child) ?? false {
607
+ // Already added
608
+ } else {
609
+ parentToChildIDs [ parent, default: [ ] ] . append ( child)
610
+ // Used to track the top-most level package
611
+ childToParentID [ child] = parent
612
+ }
613
+ }
614
+
615
+ func addAliasesForTargets( _ targets: [ String ] ,
616
+ for product: String ,
617
+ in package : PackageIdentity ) {
618
+
619
+ let aliases = aliasMap [ package ] ? [ product]
620
+ for targetName in targets {
621
+ if idTargetToAliases [ package ] ? [ targetName] == nil {
622
+ idTargetToAliases [ package , default: [ : ] ] [ targetName] = [ ]
623
+ }
624
+
625
+ if let aliases = aliases {
626
+ idTargetToAliases [ package ] ? [ targetName] ? . append ( contentsOf: aliases)
627
+ }
628
+ }
629
+ }
630
+
631
+ func alias( of targetName: String ,
632
+ in originPackage: PackageIdentity ) -> String ? {
633
+ if let aliasDict = aliasMap [ originPackage] {
634
+ let models = aliasDict. values. flatMap { $0} . filter { $0. name == targetName }
635
+ // this func only checks if there's any existing alias so
636
+ // just return the first alias value
637
+ return models. first? . alias
638
+ }
639
+ return nil
640
+ }
641
+
642
+ func propagateAliases( ) {
643
+ // First get the root package ID
644
+ var pkgID = childToParentID. first? . key
645
+ var rootPkg = pkgID
646
+ while pkgID != nil {
647
+ rootPkg = pkgID
648
+ // pkgID is not nil here so can be force unwrapped
649
+ pkgID = childToParentID [ pkgID!]
650
+ }
651
+
652
+ guard let rootPkg = rootPkg else { return }
653
+ propagate ( from: rootPkg)
654
+ }
655
+
656
+ func propagate( from cur: PackageIdentity ) {
657
+ guard let children = parentToChildIDs [ cur] else { return }
658
+ for child in children {
659
+ if let parentMap = idTargetToAliases [ cur] ,
660
+ let childMap = idTargetToAliases [ child] {
661
+ for (parentTarget, parentAliases) in parentMap {
662
+ for parentModel in parentAliases {
663
+ for (childTarget, childAliases) in childMap {
664
+ if !parentMap. keys. contains ( childTarget) ,
665
+ childTarget == parentModel. name {
666
+ if childAliases. isEmpty {
667
+ idTargetToAliases [ child] ? [ childTarget] ? . append ( parentModel)
668
+ } else {
669
+ for childModel in childAliases {
670
+ childModel. alias = parentModel. alias
671
+ }
672
+ }
578
673
}
579
674
}
580
675
}
581
676
}
582
677
}
583
-
584
- // If multiple aliases are specified in the package chain,
585
- // use the ones specified most downstream to override
586
- // upstream targets
587
- for pkgInChain in pkgStack {
588
- if let entry = result [ pkgInChain] ,
589
- let aliasToOverride = entry [ target. name] {
590
- result [ pkgID, default: [ : ] ] [ target. name] = aliasToOverride
591
- break
592
- }
593
- }
594
- }
595
- // Add pkgID to a stack used to keep track of multiple
596
- // aliases specified in the package chain. Need to add
597
- // pkgID here, otherwise need pkgID != pkgInChain check
598
- // in the for loop above
599
- pkgStack. append ( pkgID)
600
-
601
- // Recursively (depth-first) walk the package dependency tree
602
- for pkgDep in builder. package . manifest. dependencies {
603
- walkPkgTreeAndGetModuleAliases ( for: pkgDep. identity, with: packagesByIdentity, onError: observabilityScope, using: & pkgStack, output: & result)
604
- // Last added package has been looked up, so pop here
605
- if !pkgStack. isEmpty {
606
- pkgStack. removeLast ( )
607
- }
678
+ propagate ( from: child)
608
679
}
609
680
}
610
681
}
611
682
683
+ // Used to keep track of module alias info for each package
684
+ private class ModuleAliasModel {
685
+ let name : String
686
+ var alias : String
687
+ let originPackage : PackageIdentity
688
+ let parentPackage : PackageIdentity
689
+
690
+ init ( name: String , alias: String , originPackage: PackageIdentity , parentPackage: PackageIdentity ) {
691
+ self . name = name
692
+ self . alias = alias
693
+ self . originPackage = originPackage
694
+ self . parentPackage = parentPackage
695
+ }
696
+ }
697
+
612
698
/// A generic builder for `Resolved` models.
613
699
private class ResolvedBuilder < T> {
614
700
/// The constructed object, available after the first call to `construct()`.
0 commit comments