Skip to content

Commit f2cbf10

Browse files
bripeticcaheckj
andauthored
Port contents of Documentation/ModuleAliasing.md to DocC (#8730)
Fixes #8588 Migrate the contents of `Documentation/ModuleAliasing.md` to our DocC catalog in `Sources/PackageManagerDocs/Documentation.docc/ModuleAliasing.md`. --------- Co-authored-by: Joseph Heck <[email protected]>
1 parent 4dce989 commit f2cbf10

File tree

2 files changed

+163
-0
lines changed

2 files changed

+163
-0
lines changed

Sources/PackageManagerDocs/Documentation.docc/Documentation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ The Swift Package Manager lets you share your code as a package, depend on and u
2424
- <doc:ResolvingPackageVersions>
2525
- <doc:CreatingCLanguageTargets>
2626
- <doc:SwiftPMAsALibrary>
27+
- <doc:ModuleAliasing>
2728

2829
<!-- ### Command Plugins -->
2930
<!-- placeholder for content about swift package manager extensions - command plugins -->
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# Module Aliasing
2+
3+
@Metadata {
4+
@Available("Swift", introduced: "5.7")
5+
}
6+
7+
Create aliased names for modules to avoid collisions between targets in your package or its dependencies.
8+
9+
## Overview
10+
11+
As you add dependencies to your package, a name collision can occur among modules from different packages.
12+
Module names such as `Logging` or `Utils` are common examples.
13+
In order to resolve the collision, package manager, **from Swift 5.7 or later**, provides the parameter `moduleAliases` when defining dependencies for targets.
14+
You define new unique names for the modules that would otherwise conflict, without requiring any source code changes.
15+
16+
Note the following additional requirements:
17+
* A module being aliased needs to be a pure Swift module only: no ObjC/C/C++/Asm are supported due to a likely symbol collision. Similarly, use of `@objc(name)` should be avoided.
18+
* A module being aliased cannot be a prebuilt binary due to the impact on mangling and serialization, i.e. source-based only.
19+
* A module being aliased should not be passed to a runtime call such as `NSClassFromString(...)` that converts (directly or indirectly) String to a type in a module since such call will fail.
20+
* If a target mapped to a module being aliased contains resources, they should be asset catalogs, localized strings, or resources that do not require explicit module names.
21+
* If a product that a module being aliased belongs to has a conflicting name with another product, at most one of the products can be a non-automatic library type.
22+
23+
24+
### How to Use
25+
26+
Module aliases are defined as a dictionary parameter in a target's dependencies where the key is the original module name in conflict and the value is a user-defined new unique name:
27+
28+
```swift
29+
targets: [
30+
.target(
31+
name: "MyTarget",
32+
dependencies: [
33+
.product(
34+
name: "Utils",
35+
package: "MyPackage",
36+
moduleAliases: ["Utils": "MyUtils"]
37+
)
38+
]
39+
)
40+
]
41+
```
42+
43+
This will rename the `Utils` module in the `MyPackage` package to the new user-defined unique name, in this case `MyUtils`; the name of the binary will be `MyUtils.swiftmodule`. No source or manifest changes are required by the dependency package.
44+
45+
To use the aliased module, your root package needs to reference the the new name, i.e. `import MyUtils`.
46+
47+
Consider the following example to go over how module aliasing can be used in more detail.
48+
49+
#### Example
50+
51+
The following example of a package `App` imports the modules `Utils` and `Logging` from a package `swift-draw`.
52+
It wants to add another package dependency `swift-game` and imports the modules `Utils` and `Game` vended from the package. The `Game` module imports `Logging` from the same package.
53+
54+
```
55+
App
56+
|— Module Utils (from package ‘swift-draw’)
57+
|— Module Logging (from package ‘swift-draw’)
58+
|— Module Utils (from package ‘swift-game’)
59+
|— Module Game (from package ‘swift-game’)
60+
|— Module Logging (from package ‘swift-game’)
61+
```
62+
63+
Package manifest `swift-game`
64+
```
65+
{
66+
name: "swift-game",
67+
products: [
68+
.library(name: "Utils", targets: ["Utils"]),
69+
.library(name: "Game", targets: ["Game"]),
70+
],
71+
targets: [
72+
.target(name: "Game", dependencies: ["Logging"]),
73+
.target(name: "Utils", dependencies: []),
74+
.target(name: "Logging", dependencies: [])
75+
]
76+
}
77+
```
78+
79+
Package manifest `swift-draw`
80+
```
81+
{
82+
name: "swift-draw",
83+
products: [
84+
.library(name: "Utils", targets: ["Utils"]),
85+
.library(name: "Logging", targets: ["Logging"]),
86+
],
87+
targets: [
88+
.target(name: "Utils", dependencies: []),
89+
.target(name: "Logging", dependencies: []),
90+
]
91+
}
92+
```
93+
94+
##### Analyzing the conflicts
95+
96+
###### Utils modules
97+
98+
Both `swift-draw` and `swift-game` vend modules with the same name `Utils`, thus causing a conflict. To resolve the collision, a new parameter `moduleAliases` can now be used to disambiguate them.
99+
100+
Package manifest `App`
101+
```
102+
targets: [
103+
.executableTarget(
104+
name: "App",
105+
dependencies: [
106+
.product(name: "Utils",
107+
package: "swift-draw"),
108+
.product(name: "Utils",
109+
package: "swift-game",
110+
moduleAliases: ["Utils": "GameUtils"]),
111+
])
112+
]
113+
```
114+
115+
This will rename the `Utils` module in package `swift-game` as `GameUtils`; the name of the binary will be `GameUtils.swiftmodule`.
116+
117+
To use the aliased module, `App` needs to reference the the new name, i.e. `import GameUtils`. Its existing `import Utils` statement will continue to reference the `Utils` module from package `swift-draw`, as expected.
118+
119+
Note that the dependency product names are duplicate, i.e. both have the same name `Utils`, which is by default not allowed.
120+
However, this is allowed when module aliasing is used as long as no multiple files with the same product name are created.
121+
This means they must all be automatic library types, or at most one of them can be a static library, dylib, an executable, or any other type that creates a file or a directory with the product name.
122+
123+
###### Transitive Logging modules
124+
125+
Similar to the prior conflict with `Utils`, both the `swift-draw` and `swift-game` packages contain modules with the same name `Logging`, thus causing a conflict.
126+
Although `App` does not directly import `Logging` from `swift-game`, the conflicting module still needs to be disambiguated.
127+
128+
We can use `moduleAliases` again, as follows.
129+
130+
Package manifest `App`
131+
```
132+
targets: [
133+
.executableTarget(
134+
name: "App",
135+
dependencies: [
136+
// Utils module aliasing:
137+
.product(name: "Utils",
138+
package: "swift-draw"),
139+
.product(name: "Utils",
140+
package: "swift-game",
141+
moduleAliases: ["Utils": "GameUtils"]),
142+
// Logging module aliasing:
143+
.product(name: "Logging",
144+
package: "swift-draw"),
145+
.product(name: "Game",
146+
package: "swift-game",
147+
moduleAliases: ["Logging": "GameLogging"]),
148+
])
149+
]
150+
```
151+
152+
The `Logging` module from `swift-game` is renamed as `GameLogging`, and all the references to `Logging` in source files of `Game` are compiled as `GameLogging`. Similar to before, no source or manifest changes are required by the `swift-game` package.
153+
154+
If more aliases need to be defined, they can be added with a comma delimiter, per below.
155+
156+
```
157+
moduleAliases: ["Utils": "GameUtils", "Logging": "GameLogging"]),
158+
```
159+
160+
### Override Module Aliases
161+
162+
If module alias values defined upstream are conflicting downstream, they can be overridden by chaining; add an entry to the `moduleAliases` parameter downstream using the conflicting alias value as a key and provide a unique value.

0 commit comments

Comments
 (0)