Skip to content

Commit 065e55d

Browse files
committed
Introduce module aliasing to Driver
Parse input and store aliases in ModuleOutputInfo Validate input aliases and emit diagnostics Resolves rdar://85523248
1 parent 1dbe411 commit 065e55d

File tree

5 files changed

+125
-3
lines changed

5 files changed

+125
-3
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,9 +2144,12 @@ extension Driver {
21442144
fallbackOrDiagnose(.error_stdlib_module_name(moduleName: moduleName, explicitModuleName: parsedOptions.contains(.moduleName)))
21452145
}
21462146

2147+
// Retrieve and validate module aliases if passed in
2148+
let moduleAliases = moduleAliasesFromInput(parsedOptions.arguments(for: [.moduleAlias]), with: moduleName, onError: diagnosticsEngine)
2149+
21472150
// If we're not emiting a module, we're done.
21482151
if moduleOutputKind == nil {
2149-
return ModuleOutputInfo(output: nil, name: moduleName, nameIsFallback: moduleNameIsFallback)
2152+
return ModuleOutputInfo(output: nil, name: moduleName, nameIsFallback: moduleNameIsFallback, aliases: moduleAliases)
21502153
}
21512154

21522155
// Determine the module file to output.
@@ -2180,10 +2183,60 @@ extension Driver {
21802183

21812184
switch moduleOutputKind! {
21822185
case .topLevel:
2183-
return ModuleOutputInfo(output: .topLevel(moduleOutputPath.intern()), name: moduleName, nameIsFallback: moduleNameIsFallback)
2186+
return ModuleOutputInfo(output: .topLevel(moduleOutputPath.intern()), name: moduleName, nameIsFallback: moduleNameIsFallback, aliases: moduleAliases)
21842187
case .auxiliary:
2185-
return ModuleOutputInfo(output: .auxiliary(moduleOutputPath.intern()), name: moduleName, nameIsFallback: moduleNameIsFallback)
2188+
return ModuleOutputInfo(output: .auxiliary(moduleOutputPath.intern()), name: moduleName, nameIsFallback: moduleNameIsFallback, aliases: moduleAliases)
2189+
}
2190+
}
2191+
2192+
// Validate and return module aliases passed via -module-alias
2193+
static func moduleAliasesFromInput(_ aliasArgs: [ParsedOption],
2194+
with moduleName: String,
2195+
onError diagnosticsEngine: DiagnosticsEngine) -> [String: String]? {
2196+
var moduleAliases: [String: String]? = nil
2197+
let validate = { (_ arg: String, allowModuleName: Bool) -> Bool in
2198+
if !arg.sd_isSwiftIdentifier {
2199+
diagnosticsEngine.emit(.error_bad_module_name(moduleName: arg, explicitModuleName: true))
2200+
return false
2201+
}
2202+
if arg == "Swift" {
2203+
diagnosticsEngine.emit(.error_stdlib_module_name(moduleName: arg, explicitModuleName: true))
2204+
return false
2205+
}
2206+
if !allowModuleName, arg == moduleName {
2207+
diagnosticsEngine.emit(.error_bad_module_alias(arg, moduleName: moduleName))
2208+
return false
2209+
}
2210+
return true
2211+
}
2212+
2213+
var used = [""]
2214+
for item in aliasArgs {
2215+
let arg = item.argument.asSingle
2216+
let pair = arg.components(separatedBy: "=")
2217+
guard pair.count == 2 else {
2218+
diagnosticsEngine.emit(.error_bad_module_alias(arg, moduleName: moduleName, formatted: false))
2219+
continue
2220+
}
2221+
guard let lhs = pair.first, validate(lhs, false) else { continue }
2222+
guard let rhs = pair.last, validate(rhs, true) else { continue }
2223+
2224+
if moduleAliases == nil {
2225+
moduleAliases = [String: String]()
2226+
}
2227+
if let _ = moduleAliases?[lhs] {
2228+
diagnosticsEngine.emit(.error_bad_module_alias(lhs, moduleName: moduleName, isDuplicate: true))
2229+
continue
2230+
}
2231+
if used.contains(rhs) {
2232+
diagnosticsEngine.emit(.error_bad_module_alias(rhs, moduleName: moduleName, isDuplicate: true))
2233+
continue
2234+
}
2235+
moduleAliases?[lhs] = rhs
2236+
used.append(lhs)
2237+
used.append(rhs)
21862238
}
2239+
return moduleAliases
21872240
}
21882241
}
21892242

Sources/SwiftDriver/Driver/ModuleOutputInfo.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,7 @@
4848

4949
/// Whether `name` was picked by the driver instead of the user.
5050
public let nameIsFallback: Bool
51+
52+
/// Map of aliases and real names of modules referenced by source files in the current module
53+
public let aliases: [String: String]?
5154
}

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ extension Driver {
204204
try commandLine.appendAllArguments(.Xfrontend, from: &parsedOptions)
205205
try commandLine.appendAll(.coveragePrefixMap, from: &parsedOptions)
206206
try commandLine.appendLast(.warnConcurrency, from: &parsedOptions)
207+
try commandLine.appendAll(.moduleAlias, from: &parsedOptions)
207208

208209
// Expand the -experimental-hermetic-seal-at-link flag
209210
if parsedOptions.hasArgument(.experimentalHermeticSealAtLink) {

Sources/SwiftDriver/Utilities/Diagnostics.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,22 @@ extension Diagnostic.Message {
8989
return .error("module name \"\(moduleName)\" is reserved for the standard library\(suffix)")
9090
}
9191

92+
static func error_bad_module_alias(_ arg: String,
93+
moduleName: String,
94+
formatted: Bool = true,
95+
isDuplicate: Bool = false) -> Diagnostic.Message {
96+
if !formatted {
97+
return .error("invalid format \"\(arg)\"; use the format '-module-alias alias_name=underlying_name'")
98+
}
99+
if arg == moduleName {
100+
return .error("module alias \"\(arg)\" should be different from the module name \"\(moduleName)\"")
101+
}
102+
if isDuplicate {
103+
return .error("the name \"\(arg)\" is already used for a module alias or an underlying name")
104+
}
105+
return .error("bad module alias \"\(arg)\"")
106+
}
107+
92108
static var error_hermetic_seal_cannot_have_library_evolution: Diagnostic.Message {
93109
.error("Cannot use -experimental-hermetic-seal-at-link with -enable-library-evolution")
94110
}

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2226,6 +2226,55 @@ final class SwiftDriverTests: XCTestCase {
22262226
}
22272227
}
22282228

2229+
func testModuleAliasingWithImplicitBuild() throws {
2230+
var driver = try Driver(args: [
2231+
"swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "Car=Bar",
2232+
"-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule",
2233+
])
2234+
let plannedJobs = try driver.planBuild()
2235+
XCTAssertTrue(plannedJobs.contains { job in
2236+
job.commandLine.contains(.flag("-module-alias")) &&
2237+
job.commandLine.contains(.flag("Car=Bar")) &&
2238+
job.outputs[0].file.absolutePath?.pathString == "/tmp/dir/Foo.swiftmodule"
2239+
})
2240+
XCTAssertEqual(driver.moduleOutputInfo.name, "Foo")
2241+
XCTAssertNotNil(driver.moduleOutputInfo.aliases)
2242+
XCTAssertEqual(driver.moduleOutputInfo.aliases!.count, 1)
2243+
XCTAssertEqual(driver.moduleOutputInfo.aliases!["Car"], "Bar")
2244+
}
2245+
2246+
func testInvalidModuleAliasing() throws {
2247+
try assertDriverDiagnostics(
2248+
args: ["swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "CarBar", "-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule"]
2249+
) {
2250+
$1.expect(.error("invalid format \"CarBar\"; use the format '-module-alias alias_name=underlying_name'"))
2251+
}
2252+
2253+
try assertDriverDiagnostics(
2254+
args: ["swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "Foo=Bar", "-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule"]
2255+
) {
2256+
$1.expect(.error("module alias \"Foo\" should be different from the module name \"Foo\""))
2257+
}
2258+
2259+
try assertDriverDiagnostics(
2260+
args: ["swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "C-ar=Bar", "-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule"]
2261+
) {
2262+
$1.expect(.error("module name \"C-ar\" is not a valid identifier"))
2263+
}
2264+
2265+
try assertDriverDiagnostics(
2266+
args: ["swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "Car=Bar", "-module-alias", "Train=Car", "-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule"]
2267+
) {
2268+
$1.expect(.error("the name \"Car\" is already used for a module alias or an underlying name"))
2269+
}
2270+
2271+
try assertDriverDiagnostics(
2272+
args: ["swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "Car=Bar", "-module-alias", "Car=Bus", "-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule"]
2273+
) {
2274+
$1.expect(.error("the name \"Car\" is already used for a module alias or an underlying name"))
2275+
}
2276+
}
2277+
22292278
func testWholeModuleOptimizationUsingSupplementaryOutputFileMap() throws {
22302279
var driver1 = try Driver(args: [
22312280
"swiftc", "-whole-module-optimization", "foo.swift", "bar.swift", "wibble.swift", "-module-name", "Test",

0 commit comments

Comments
 (0)