Skip to content

Commit a557d59

Browse files
keithDougGregor
authored andcommitted
Switch to using clang as linker on darwin
This allows us to remove lots of duplicated logic from the clang driver.
1 parent d8640a2 commit a557d59

File tree

5 files changed

+120
-411
lines changed

5 files changed

+120
-411
lines changed

Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift

Lines changed: 40 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -47,153 +47,6 @@ extension DarwinToolchain {
4747
return try findXcodeClangLibPath("arc")
4848
}
4949

50-
internal func addLTOLibArgs(to commandLine: inout [Job.ArgTemplate]) throws {
51-
if let path = try findXcodeClangLibPath("libLTO.dylib") {
52-
commandLine.appendFlag("-lto_library")
53-
commandLine.appendPath(path)
54-
}
55-
}
56-
57-
func addLinkRuntimeLibraryRPath(
58-
to commandLine: inout [Job.ArgTemplate],
59-
parsedOptions: inout ParsedOptions,
60-
targetInfo: FrontendTargetInfo,
61-
darwinLibName: String
62-
) throws {
63-
// Adding the rpaths might negatively interact when other rpaths are involved,
64-
// so we should make sure we add the rpaths last, after all user-specified
65-
// rpaths. This is currently true from this place, but we need to be
66-
// careful if this function is ever called before user's rpaths are emitted.
67-
assert(darwinLibName.hasSuffix(".dylib"), "must be a dynamic library")
68-
69-
// Add @executable_path to rpath to support having the dylib copied with
70-
// the executable.
71-
commandLine.appendFlag("-rpath")
72-
commandLine.appendFlag("@executable_path")
73-
74-
// Add the path to the resource dir to rpath to support using the dylib
75-
// from the default location without copying.
76-
77-
78-
let clangPath = try clangLibraryPath(
79-
for: targetInfo,
80-
parsedOptions: &parsedOptions)
81-
commandLine.appendFlag("-rpath")
82-
commandLine.appendPath(clangPath)
83-
}
84-
85-
func addLinkSanitizerLibArgsForDarwin(
86-
to commandLine: inout [Job.ArgTemplate],
87-
parsedOptions: inout ParsedOptions,
88-
targetInfo: FrontendTargetInfo,
89-
sanitizer: Sanitizer,
90-
isShared: Bool
91-
) throws {
92-
// Sanitizer runtime libraries requires C++.
93-
commandLine.appendFlag("-lc++")
94-
// Add explicit dependency on -lc++abi, as -lc++ doesn't re-export
95-
// all RTTI-related symbols that are used.
96-
commandLine.appendFlag("-lc++abi")
97-
98-
let sanitizerName = try runtimeLibraryName(
99-
for: sanitizer,
100-
targetTriple: targetInfo.target.triple,
101-
isShared: isShared
102-
)
103-
try addLinkRuntimeLibrary(
104-
named: sanitizerName,
105-
to: &commandLine,
106-
for: targetInfo,
107-
parsedOptions: &parsedOptions
108-
)
109-
110-
if isShared {
111-
try addLinkRuntimeLibraryRPath(
112-
to: &commandLine,
113-
parsedOptions: &parsedOptions,
114-
targetInfo: targetInfo,
115-
darwinLibName: sanitizerName
116-
)
117-
}
118-
}
119-
120-
private func addProfileGenerationArgs(
121-
to commandLine: inout [Job.ArgTemplate],
122-
parsedOptions: inout ParsedOptions,
123-
targetInfo: FrontendTargetInfo
124-
) throws {
125-
guard parsedOptions.hasArgument(.profileGenerate) else { return }
126-
let clangPath = try clangLibraryPath(for: targetInfo,
127-
parsedOptions: &parsedOptions)
128-
129-
for runtime in targetInfo.target.triple.darwinPlatform!.profileLibraryNameSuffixes {
130-
let clangRTPath = clangPath
131-
.appending(component: "libclang_rt.profile_\(runtime).a")
132-
commandLine.appendPath(clangRTPath)
133-
}
134-
}
135-
136-
private func addPlatformVersionArg(to commandLine: inout [Job.ArgTemplate],
137-
for triple: Triple, sdkPath: VirtualPath.Handle?) {
138-
assert(triple.isDarwin)
139-
let platformName = triple.darwinPlatform!.linkerPlatformName
140-
let platformVersion = triple.darwinLinkerPlatformVersion
141-
let sdkVersion: Version
142-
if let sdkPath = sdkPath,
143-
let sdkInfo = getTargetSDKInfo(sdkPath: sdkPath) {
144-
sdkVersion = sdkInfo.sdkVersion(for: triple)
145-
} else {
146-
sdkVersion = Version(0, 0, 0)
147-
}
148-
149-
commandLine.append(.flag("-platform_version"))
150-
commandLine.append(.flag(platformName))
151-
commandLine.append(.flag(platformVersion.description))
152-
commandLine.append(.flag(sdkVersion.description))
153-
}
154-
155-
private func addDeploymentTargetArgs(
156-
to commandLine: inout [Job.ArgTemplate],
157-
targetTriple: Triple,
158-
targetVariantTriple: Triple?,
159-
sdkPath: VirtualPath.Handle?
160-
) {
161-
addPlatformVersionArg(to: &commandLine, for: targetTriple, sdkPath: sdkPath)
162-
if let variantTriple = targetVariantTriple {
163-
assert(targetTriple.isValidForZipperingWithTriple(variantTriple))
164-
addPlatformVersionArg(to: &commandLine, for: variantTriple,
165-
sdkPath: sdkPath)
166-
}
167-
}
168-
169-
private func addArgsToLinkARCLite(
170-
to commandLine: inout [Job.ArgTemplate],
171-
parsedOptions: inout ParsedOptions,
172-
targetTriple: Triple
173-
) throws {
174-
guard parsedOptions.hasFlag(
175-
positive: .linkObjcRuntime,
176-
negative: .noLinkObjcRuntime,
177-
default: !targetTriple.supports(.nativeARC)
178-
) else {
179-
return
180-
}
181-
182-
guard let arcLiteLibPath = try findARCLiteLibPath(),
183-
let platformName = targetTriple.platformName() else {
184-
return
185-
}
186-
let fullLibPath = arcLiteLibPath
187-
.appending(components: "libarclite_\(platformName).a")
188-
189-
commandLine.appendFlag("-force_load")
190-
commandLine.appendPath(fullLibPath)
191-
192-
// Arclite depends on CoreFoundation.
193-
commandLine.appendFlag(.framework)
194-
commandLine.appendFlag("CoreFoundation")
195-
}
196-
19750
/// Adds the arguments necessary to link the files from the given set of
19851
/// options for a Darwin platform.
19952
public func addPlatformSpecificLinkerArgs(
@@ -213,7 +66,7 @@ extension DarwinToolchain {
21366
case .dynamicLibrary:
21467
// Same as an executable, but with the -dylib flag
21568
linkerTool = .dynamicLinker
216-
commandLine.appendFlag("-dylib")
69+
commandLine.appendFlag("-dynamiclib")
21770
addLinkInputs(shouldUseInputFileList: shouldUseInputFileList,
21871
commandLine: &commandLine,
21972
inputs: inputs,
@@ -280,8 +133,7 @@ extension DarwinToolchain {
280133
commandLine.appendPath(fileList)
281134
if linkerOutputType != .staticLibrary {
282135
for module in inputModules {
283-
commandLine.append(.flag("-add_ast_path"))
284-
commandLine.append(.path(module))
136+
commandLine.append(.joinedOptionAndPath("-Wl,-add_ast_path,", module))
285137
}
286138
}
287139

@@ -291,7 +143,7 @@ extension DarwinToolchain {
291143
commandLine.append(contentsOf: inputs.flatMap {
292144
(path: TypedVirtualPath) -> [Job.ArgTemplate] in
293145
if path.type == .swiftModule && linkerOutputType != .staticLibrary {
294-
return [.flag("-add_ast_path"), .path(path.file)]
146+
return [.joinedOptionAndPath("-Wl,-add_ast_path,", path.file)]
295147
} else if path.type == .object {
296148
return [.path(path.file)]
297149
} else if path.type == .tbd {
@@ -311,39 +163,16 @@ extension DarwinToolchain {
311163
sanitizers: Set<Sanitizer>,
312164
linkerOutputType: LinkOutputType,
313165
lto: LTOKind?) throws {
314-
// FIXME: If we used Clang as a linker instead of going straight to ld,
315-
// we wouldn't have to replicate a bunch of Clang's logic here.
316-
317-
// Always link the regular compiler_rt if it's present. Note that the
318-
// regular libclang_rt.a uses a fat binary for device and simulator; this is
319-
// not true for all compiler_rt build products.
320-
//
321-
// Note: Normally we'd just add this unconditionally, but it's valid to build
322-
// Swift and use it as a linker without building compiler_rt.
323-
let targetTriple = targetInfo.target.triple
324-
let darwinPlatformSuffix =
325-
targetTriple.darwinPlatform!.with(.device)!.libraryNameSuffix
326-
let compilerRTPath =
327-
try clangLibraryPath(
328-
for: targetInfo,
329-
parsedOptions: &parsedOptions)
330-
.appending(component: "libclang_rt.\(darwinPlatformSuffix).a")
331-
if try fileSystem.exists(compilerRTPath) {
332-
commandLine.append(.path(compilerRTPath))
333-
}
334-
335-
try addArgsToLinkARCLite(
336-
to: &commandLine,
337-
parsedOptions: &parsedOptions,
338-
targetTriple: targetTriple
339-
)
166+
if let lto = lto {
167+
switch lto {
168+
case .llvmFull:
169+
commandLine.appendFlag("-flto=full")
170+
case .llvmThin:
171+
commandLine.appendFlag("-flto=thin")
172+
}
340173

341-
if lto != nil {
342174
if let arg = parsedOptions.getLastArgument(.ltoLibrary)?.asSingle {
343-
commandLine.appendFlag("-lto_library")
344-
commandLine.appendPath(try VirtualPath(path: arg))
345-
} else {
346-
try addLTOLibArgs(to: &commandLine)
175+
commandLine.append(.joinedOptionAndPath("-Wl,-lto_library,", try VirtualPath(path: arg)))
347176
}
348177
}
349178

@@ -354,43 +183,44 @@ extension DarwinToolchain {
354183
}
355184

356185
if parsedOptions.contains(.enableAppExtension) {
357-
commandLine.appendFlag("-application_extension")
186+
commandLine.appendFlag("-fapplication-extension")
358187
}
359188

360189
// Linking sanitizers will add rpaths, which might negatively interact when
361190
// other rpaths are involved, so we should make sure we add the rpaths after
362191
// all user-specified rpaths.
363-
for sanitizer in sanitizers {
364-
if sanitizer == .fuzzer {
365-
guard linkerOutputType == .executable else { continue }
366-
}
367-
try addLinkSanitizerLibArgsForDarwin(
368-
to: &commandLine,
369-
parsedOptions: &parsedOptions,
370-
targetInfo: targetInfo,
371-
sanitizer: sanitizer,
372-
isShared: sanitizer != .fuzzer
373-
)
192+
if linkerOutputType == .executable && !sanitizers.isEmpty {
193+
let sanitizerNames = sanitizers
194+
.map { $0.rawValue }
195+
.sorted() // Sort so we get a stable, testable order
196+
.joined(separator: ",")
197+
commandLine.appendFlag("-fsanitize=\(sanitizerNames)")
374198
}
375199

376-
if parsedOptions.contains(.embedBitcode) ||
377-
parsedOptions.contains(.embedBitcodeMarker) {
378-
commandLine.appendFlag("-bitcode_bundle")
200+
if parsedOptions.contains(.embedBitcode) {
201+
commandLine.appendFlag("-fembed-bitcode")
202+
} else if parsedOptions.contains(.embedBitcodeMarker) {
203+
commandLine.appendFlag("-fembed-bitcode=marker")
379204
}
380205

381206
// Add the SDK path
382207
if let sdkPath = targetInfo.sdkPath?.path {
383-
commandLine.appendFlag("-syslibroot")
208+
commandLine.appendFlag("--sysroot")
384209
commandLine.appendPath(VirtualPath.lookup(sdkPath))
385210
}
386211

387212
commandLine.appendFlags(
213+
"-fobjc-link-runtime",
388214
"-lobjc",
389215
"-lSystem"
390216
)
391217

392-
commandLine.appendFlag("-arch")
393-
commandLine.appendFlag(targetTriple.archName)
218+
let targetTriple = targetInfo.target.triple
219+
commandLine.appendFlag("--target=\(targetTriple.triple)")
220+
if let variantTriple = targetInfo.targetVariant?.triple {
221+
assert(targetTriple.isValidForZipperingWithTriple(variantTriple))
222+
commandLine.appendFlag("-darwin-target-variant=\(variantTriple.triple)")
223+
}
394224

395225
// On Darwin, we only support libc++.
396226
if parsedOptions.contains(.enableExperimentalCxxInterop) {
@@ -405,19 +235,9 @@ extension DarwinToolchain {
405235
fileSystem: fileSystem
406236
)
407237

408-
try addProfileGenerationArgs(
409-
to: &commandLine,
410-
parsedOptions: &parsedOptions,
411-
targetInfo: targetInfo
412-
)
413-
414-
let targetVariantTriple = targetInfo.targetVariant?.triple
415-
addDeploymentTargetArgs(
416-
to: &commandLine,
417-
targetTriple: targetTriple,
418-
targetVariantTriple: targetVariantTriple,
419-
sdkPath: targetInfo.sdkPath?.path
420-
)
238+
if parsedOptions.hasArgument(.profileGenerate) {
239+
commandLine.appendFlag("-fprofile-generate")
240+
}
421241

422242
// These custom arguments should be right before the object file at the
423243
// end.
@@ -427,7 +247,13 @@ extension DarwinToolchain {
427247
from: &parsedOptions
428248
)
429249
addLinkedLibArgs(to: &commandLine, parsedOptions: &parsedOptions)
430-
try commandLine.appendAllArguments(.Xlinker, from: &parsedOptions)
250+
// Because we invoke `clang` as the linker executable, we must still
251+
// use `-Xlinker` for linker-specific arguments.
252+
for linkerOpt in parsedOptions.arguments(for: .Xlinker) {
253+
commandLine.appendFlag(.Xlinker)
254+
commandLine.appendFlag(linkerOpt.argument.asSingle)
255+
}
256+
try commandLine.appendAllArguments(.XclangLinker, from: &parsedOptions)
431257
}
432258
}
433259

Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,6 @@ extension Toolchain {
5656
return result
5757
}
5858

59-
func addLinkRuntimeLibrary(
60-
named name: String,
61-
to commandLine: inout [Job.ArgTemplate],
62-
for targetInfo: FrontendTargetInfo,
63-
parsedOptions: inout ParsedOptions
64-
) throws {
65-
let path = try clangLibraryPath(
66-
for: targetInfo,
67-
parsedOptions: &parsedOptions)
68-
.appending(component: name)
69-
commandLine.appendPath(path)
70-
}
71-
7259
func runtimeLibraryExists(
7360
for sanitizer: Sanitizer,
7461
targetInfo: FrontendTargetInfo,

Sources/SwiftDriver/Toolchains/DarwinToolchain.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public final class DarwinToolchain: Toolchain {
6464
case .swiftCompiler:
6565
return try lookup(executable: "swift-frontend")
6666
case .dynamicLinker:
67-
return try lookup(executable: "ld")
67+
return try lookup(executable: "clang")
6868
case .staticLinker:
6969
return try lookup(executable: "libtool")
7070
case .dsymutil:

Tests/SwiftDriverTests/JobExecutorTests.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,11 @@ final class JobExecutorTests: XCTestCase {
179179
.path(.temporary(RelativePath("foo.o"))),
180180
.path(.temporary(RelativePath("main.o"))),
181181
.path(.absolute(try toolchain.clangRT.get())),
182-
"-syslibroot", .path(.absolute(try toolchain.sdk.get())),
183-
"-lobjc", "-lSystem", "-arch", .flag(hostTriple.archName),
182+
"--sysroot", .path(.absolute(try toolchain.sdk.get())),
183+
"-lobjc", "-lSystem", .flag("--target=\(hostTriple.triple)"),
184184
"-L", .path(.absolute(try toolchain.resourcesDirectory.get())),
185185
"-L", .path(.absolute(try toolchain.sdkStdlib(sdk: toolchain.sdk.get()))),
186-
"-rpath", "/usr/lib/swift", "-macosx_version_min", "10.14.0", "-o",
186+
"-rpath", "/usr/lib/swift", "-o",
187187
.path(.relative(RelativePath("main"))),
188188
],
189189
inputs: [
@@ -249,12 +249,10 @@ final class JobExecutorTests: XCTestCase {
249249
tool: try toolchain.resolvedTool(.dynamicLinker),
250250
commandLine: [
251251
.path(.temporary(RelativePath("main.o"))),
252-
.path(.absolute(try toolchain.clangRT.get())),
253-
"-syslibroot", .path(.absolute(try toolchain.sdk.get())),
254-
"-lobjc", "-lSystem", "-arch", .flag(hostTriple.archName),
252+
"--sysroot", .path(.absolute(try toolchain.sdk.get())),
253+
"-lobjc", "-lSystem", .flag("--target=\(hostTriple.triple)"),
255254
"-L", .path(.absolute(try toolchain.resourcesDirectory.get())),
256255
"-L", .path(.absolute(try toolchain.sdkStdlib(sdk: toolchain.sdk.get()))),
257-
"-rpath", "/usr/lib/swift", "-macosx_version_min", "10.14.0",
258256
"-o", .path(.absolute(exec)),
259257
],
260258
inputs: [

0 commit comments

Comments
 (0)