1
1
import PackageModel
2
+ import Basics
2
3
3
4
// This class helps track module aliases in a package graph and override
4
5
// upstream alises if needed
@@ -98,7 +99,7 @@ class ModuleAliasTracker {
98
99
}
99
100
}
100
101
101
- func propagateAliases( ) {
102
+ func propagateAliases( observabilityScope : ObservabilityScope ) {
102
103
// First get the root package ID
103
104
var pkgID = childToParentID. first? . key
104
105
var rootPkg = pkgID
@@ -111,11 +112,12 @@ class ModuleAliasTracker {
111
112
// Propagate and override upstream aliases if needed
112
113
var aliasBuffer = [ String: ModuleAliasModel] ( )
113
114
propagate ( package : rootPkg, aliasBuffer: & aliasBuffer)
115
+
114
116
// Now merge overriden upstream aliases and add them to
115
117
// downstream targets
116
118
if let productToAllTargets = idToProductToAllTargets [ rootPkg] {
117
119
for productID in productToAllTargets. keys {
118
- mergeAliases ( productID: productID)
120
+ mergeAliases ( productID: productID, observabilityScope : observabilityScope )
119
121
}
120
122
}
121
123
}
@@ -130,11 +132,22 @@ class ModuleAliasTracker {
130
132
// aliases for targets; if the downstream aliases are applied
131
133
// to upstream targets, then they get removed
132
134
if aliasBuffer [ aliasModel. name] == nil {
133
- // Add a target name as a key. The buffer only tracks
134
- // a target that needs to be renamed, not the depending
135
- // targets which might have multiple target dependencies
136
- // with their aliases, so add a single alias model as value.
137
- aliasBuffer [ aliasModel. name] = aliasModel
135
+ // First look up if there's an alias to be overriden,
136
+ // e.g. upstream has `moduleAliases: [Utils: FooUtils]`,
137
+ // and downstream has `moduleAliases: [FooUtils: BarUtils]`,
138
+ // then we want to add [Utils: BarUtils] to the buffer
139
+ // to track. Both [Utils: FooUtils] and [FooUtils: BarUtils]
140
+ // are also added to the buffer.
141
+ var next = aliasModel. alias
142
+ while let nextValue = aliasBuffer [ next] {
143
+ next = nextValue. alias
144
+ }
145
+ if next != aliasModel. alias {
146
+ let chainedModel = ModuleAliasModel ( name: aliasModel. name, alias: next, originPackage: aliasModel. originPackage, consumingPackage: aliasModel. consumingPackage)
147
+ aliasBuffer [ aliasModel. name] = chainedModel
148
+ } else {
149
+ aliasBuffer [ aliasModel. name] = aliasModel
150
+ }
138
151
}
139
152
}
140
153
}
@@ -151,11 +164,24 @@ class ModuleAliasTracker {
151
164
var didAlias = false
152
165
for curTarget in targetsForCurProduct {
153
166
// Check if curTarget is relevant for aliasing
154
- let canAlias = curTarget. name == aliasModel. name || curTarget. dependencies. contains { $0. name == aliasModel. name }
155
- if canAlias {
167
+ let canAliasTarget = curTarget. name == aliasModel. name || curTarget. dependencies. contains { $0. name == aliasModel. name }
168
+ if canAliasTarget {
156
169
curTarget. addModuleAlias ( for: aliasModel. name, as: aliasModel. alias)
157
170
didAlias = true
158
171
}
172
+ // Check if curTarget needs pre-chained aliases
173
+ // from its dependency products.
174
+ let depProductAliases = curTarget. dependencies. compactMap { $0. product? . moduleAliases} . flatMap { $0}
175
+ let depAliasesWithDuplicateKeys = depProductAliases. filter { $0. key == aliasModel. name}
176
+ let usePreChainedAliases = ( didAlias && !depAliasesWithDuplicateKeys. isEmpty) || depAliasesWithDuplicateKeys. count > 1
177
+
178
+ if usePreChainedAliases {
179
+ for depAlias in depProductAliases {
180
+ if let preChainedAlias = aliasBuffer [ depAlias. value] {
181
+ curTarget. addModuleAlias ( for: preChainedAlias. name, as: preChainedAlias. alias)
182
+ }
183
+ }
184
+ }
159
185
}
160
186
if didAlias {
161
187
usedKeys. insert ( targetToAlias. name)
@@ -170,17 +196,20 @@ class ModuleAliasTracker {
170
196
}
171
197
}
172
198
}
173
- guard let children = parentToChildIDs [ package ] else { return }
199
+ guard let children = parentToChildIDs [ package ] else {
200
+ aliasBuffer. removeAll ( )
201
+ return
202
+ }
174
203
for childID in children {
175
204
propagate ( package : childID, aliasBuffer: & aliasBuffer)
176
205
}
177
206
}
178
207
179
208
// Merge overriden upstream aliases and add them to downstream targets
180
- func mergeAliases( productID: String ) {
209
+ func mergeAliases( productID: String , observabilityScope : ObservabilityScope ) {
181
210
guard let childProducts = parentToChildProducts [ productID] else { return }
182
211
for child in childProducts {
183
- mergeAliases ( productID: child)
212
+ mergeAliases ( productID: child, observabilityScope : observabilityScope )
184
213
// Filter out targets in the current product with names that are
185
214
// aliased with different values in the child products since they
186
215
// should either not be aliased or their existing aliases if any
@@ -197,21 +226,25 @@ class ModuleAliasTracker {
197
226
relevantTargets. append ( contentsOf: directTargets)
198
227
relevantTargets. append ( contentsOf: directRelevantTargets)
199
228
let relevantTargetSet = Set ( relevantTargets)
200
-
201
229
// Used to compare with aliases defined in other child products
202
230
// and detect a conflict if any.
203
231
let allTargetsInOtherChildProducts = childProducts. filter { $0 != child } . compactMap { productToAllTargets [ $0] } . flatMap { $0}
204
- let allTargetNamesInChildProduct = productToAllTargets [ child] ? . map { $0. name} ?? [ ]
232
+ let allTargetNamesInCurChildProduct = productToAllTargets [ child] ? . map { $0. name} ?? [ ]
205
233
for curTarget in relevantTargetSet {
206
- for (nameToBeAliased, aliasInChild) in childTargetsAliases {
207
- // If there are targets in other child products that
208
- // have the same name that's being aliased here, but
209
- // targets in this child product don't, we need to use
210
- // alias values of those targets as they take a precedence
211
- let otherAliasesInChildProducts = allTargetsInOtherChildProducts. filter { $0. name == nameToBeAliased} . compactMap { $0. moduleAliases} . flatMap { $0} . filter { $0. key == nameToBeAliased}
212
- if !otherAliasesInChildProducts. isEmpty,
213
- !allTargetNamesInChildProduct. contains ( curTarget. name) {
214
- for (aliasKey, aliasValue) in otherAliasesInChildProducts {
234
+ for (nameToBeAliased, aliasInCurChild) in childTargetsAliases {
235
+ // Look up targets in other child products that have the
236
+ // name `nameToBeAliased` but are aliased with values
237
+ // conflicting with `aliasInCurChild`
238
+ let aliasesForTargetsRenamedInOtherChildProducts = allTargetsInOtherChildProducts. filter { $0. name == nameToBeAliased} . compactMap { $0. moduleAliases} . flatMap { $0} . filter { $0. key == nameToBeAliased}
239
+ let hasConflictingAliasesForTargetsRenamed = aliasesForTargetsRenamedInOtherChildProducts. contains { $0. value != aliasInCurChild }
240
+
241
+ if hasConflictingAliasesForTargetsRenamed {
242
+ // If conflicting aliases are found, wait until
243
+ // all conflicts are found and deleted (in the last
244
+ // `else` case), so do nothing for now.
245
+ } else if !aliasesForTargetsRenamedInOtherChildProducts. isEmpty,
246
+ !allTargetNamesInCurChildProduct. contains ( curTarget. name) {
247
+ for (aliasKey, aliasValue) in aliasesForTargetsRenamedInOtherChildProducts {
215
248
// Reset the old alias value with this aliasValue
216
249
if curTarget. moduleAliases ? [ aliasKey] != aliasValue {
217
250
curTarget. addModuleAlias ( for: aliasKey, as: aliasValue)
@@ -221,20 +254,38 @@ class ModuleAliasTracker {
221
254
// If there are no aliases or conflicting aliases
222
255
// for the same key defined in other child products,
223
256
// those aliases should be removed from this target.
224
- let hasConflict = allTargetsInOtherChildProducts. contains { otherTarget in
257
+ let targetsWithConflictingAliases = allTargetsInOtherChildProducts. filter { otherTarget in
225
258
if let otherAlias = otherTarget. moduleAliases ? [ nameToBeAliased] {
226
- return otherAlias != aliasInChild
259
+ return otherAlias != aliasInCurChild
227
260
} else {
228
261
return otherTarget. name == nameToBeAliased
229
262
}
230
263
}
231
- if hasConflict {
232
- // If there are aliases, remove as aliasing should
233
- // not be applied
264
+
265
+ if !targetsWithConflictingAliases. isEmpty {
266
+ // If there are conflicting aliases, remove them
267
+ // and show a diag message
268
+ let conflictingAliases = targetsWithConflictingAliases. compactMap { $0. moduleAliases ? [ nameToBeAliased] }
269
+ var message = conflictingAliases. isEmpty ? " Targets named ' \( nameToBeAliased) ' found in other dependency products; " : " Conflicting module aliases found \( conflictingAliases + [ aliasInCurChild] ) for ' \( nameToBeAliased) '; "
270
+ message += " these will be removed from target ' \( curTarget. name) ' "
271
+ observabilityScope. emit ( info: message)
272
+
234
273
curTarget. removeModuleAlias ( for: nameToBeAliased)
235
- } else if curTarget. moduleAliases ? [ nameToBeAliased] == nil {
236
- // Otherwise add the alias if none exists
237
- curTarget. addModuleAlias ( for: nameToBeAliased, as: aliasInChild)
274
+ } else if curTarget. name != nameToBeAliased {
275
+ // Targets to be renamed already have aliases, so
276
+ // process their depending targets here
277
+ if curTarget. moduleAliases ? [ nameToBeAliased] == nil {
278
+ // Remove any pre-chained aliases, i.e. value
279
+ // of a module alias defined upstream that's used
280
+ // as a key downstream
281
+ let preChainedAliases = curTarget. moduleAliases? . filter { $0. value == aliasInCurChild} ?? [ : ]
282
+ for preChainedAlias in preChainedAliases {
283
+ if preChainedAlias. key != nameToBeAliased {
284
+ curTarget. removeModuleAlias ( for: preChainedAlias. key)
285
+ }
286
+ }
287
+ curTarget. addModuleAlias ( for: nameToBeAliased, as: aliasInCurChild)
288
+ }
238
289
}
239
290
}
240
291
}
0 commit comments