Skip to content

Commit 5b24d2b

Browse files
author
Harlan Haskins
committed
[Linux] Add linux linking support
As part of this, we unfortunately have to sink down most of the logic for constructing the link job. Hopefully we can move to using clang as a linker for all platforms, at which point we can bring some of the specifics back.
1 parent ef868d6 commit 5b24d2b

File tree

7 files changed

+792
-363
lines changed

7 files changed

+792
-363
lines changed
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
import TSCBasic
2+
import TSCUtility
3+
4+
extension DarwinToolchain {
5+
private func findARCLiteLibPath() throws -> AbsolutePath? {
6+
let path = try getToolPath(.swiftCompiler)
7+
.parentDirectory // 'swift'
8+
.parentDirectory // 'bin'
9+
.appending(components: "lib", "arc")
10+
11+
if localFileSystem.exists(path) { return path }
12+
13+
// If we don't have a 'lib/arc/' directory, find the "arclite" library
14+
// relative to the Clang in the active Xcode.
15+
if let clangPath = try? getToolPath(.clang) {
16+
return clangPath
17+
.parentDirectory // 'clang'
18+
.parentDirectory // 'bin'
19+
.appending(components: "lib", "arc")
20+
}
21+
return nil
22+
}
23+
24+
private func addProfileGenerationArgs(
25+
to commandLine: inout [Job.ArgTemplate],
26+
parsedOptions: inout ParsedOptions,
27+
targetTriple: Triple
28+
) throws {
29+
guard parsedOptions.hasArgument(.profile_generate) else { return }
30+
let clangPath = try clangLibraryPath(for: targetTriple,
31+
parsedOptions: &parsedOptions)
32+
33+
let runtime: String
34+
if targetTriple.os.isiOS {
35+
runtime = targetTriple.os.isTvOS ? "tvos" : "ios"
36+
} else if targetTriple.os.isWatchOS {
37+
runtime = "watchos"
38+
} else {
39+
assert(targetTriple.os.isMacOSX)
40+
runtime = "osx"
41+
}
42+
43+
var sim = ""
44+
if targetTriple.isSimulatorEnvironment {
45+
sim = "sim"
46+
}
47+
48+
var clangRTPath = clangPath
49+
.appending(component: "libclang_rt.profile_\(runtime)\(sim).a")
50+
51+
// FIXME: Continue accepting the old path for simulator libraries for now.
52+
if targetTriple.isSimulatorEnvironment &&
53+
!localFileSystem.exists(clangRTPath) {
54+
clangRTPath = clangRTPath.parentDirectory
55+
.appending(component: "libclang_rt.profile_\(runtime).a")
56+
}
57+
58+
commandLine.appendPath(clangRTPath)
59+
}
60+
61+
private func addDeploymentTargetArgs(
62+
to commandLine: inout [Job.ArgTemplate],
63+
targetTriple: Triple
64+
) {
65+
// FIXME: Properly handle deployment targets.
66+
assert(targetTriple.os.isiOS || targetTriple.os.isWatchOS || targetTriple.os.isMacOSX)
67+
68+
if (targetTriple.os.isiOS) {
69+
if (targetTriple.os.isTvOS) {
70+
if targetTriple.isSimulatorEnvironment {
71+
commandLine.appendFlag("-tvos_simulator_version_min")
72+
} else {
73+
commandLine.appendFlag("-tvos_version_min")
74+
}
75+
} else {
76+
if targetTriple.isSimulatorEnvironment {
77+
commandLine.appendFlag("-ios_simulator_version_min")
78+
} else {
79+
commandLine.appendFlag("-iphoneos_version_min")
80+
}
81+
}
82+
commandLine.appendFlag(targetTriple.iOSVersion().description)
83+
} else if targetTriple.os.isWatchOS {
84+
if targetTriple.isSimulatorEnvironment {
85+
commandLine.appendFlag("-watchos_simulator_version_min")
86+
} else {
87+
commandLine.appendFlag("-watchos_version_min")
88+
}
89+
commandLine.appendFlag(targetTriple.watchOSVersion().description)
90+
} else {
91+
commandLine.appendFlag("-macosx_version_min")
92+
commandLine.appendFlag(targetTriple.getMacOSXVersion().1.description)
93+
}
94+
}
95+
96+
/// Returns true if the compiler depends on features provided by the ObjC
97+
/// runtime that are not present on the deployment target indicated by
98+
/// `triple`.
99+
private func wantsObjCRuntime(triple: Triple) -> Bool {
100+
// When updating the versions listed here, please record the most recent
101+
// feature being depended on and when it was introduced:
102+
//
103+
// - Make assigning 'nil' to an NSMutableDictionary subscript delete the
104+
// entry, like it does for Swift.Dictionary, rather than trap.
105+
if triple.os.isiOS {
106+
return triple.iOSVersion() < Triple.Version(9, 0, 0)
107+
} else if triple.os.isMacOSX {
108+
return triple.getMacOSXVersion().1 < Triple.Version(10, 11, 0)
109+
} else if triple.os.isWatchOS {
110+
return false
111+
}
112+
fatalError("unknown Darwin OS")
113+
}
114+
115+
private func addArgsToLinkARCLite(
116+
to commandLine: inout [Job.ArgTemplate],
117+
parsedOptions: inout ParsedOptions,
118+
targetTriple: Triple
119+
) throws {
120+
if !parsedOptions.hasFlag(positive: .link_objc_runtime,
121+
negative: .no_link_objc_runtime,
122+
default: wantsObjCRuntime(triple: targetTriple)) {
123+
return
124+
}
125+
126+
guard let arcLiteLibPath = try findARCLiteLibPath(),
127+
let platformName = targetTriple.platformName else {
128+
return
129+
}
130+
let fullLibPath = arcLiteLibPath
131+
.appending(components: "libarclite_\(platformName).a")
132+
133+
commandLine.appendFlag("-force_load")
134+
commandLine.appendPath(fullLibPath)
135+
136+
// Arclite depends on CoreFoundation.
137+
commandLine.appendFlag(.framework)
138+
commandLine.appendFlag("CoreFoundation")
139+
}
140+
141+
/// Adds the arguments necessary to link the files from the given set of
142+
/// options for a Darwin platform.
143+
public func addPlatformSpecificLinkerArgs(
144+
to commandLine: inout [Job.ArgTemplate],
145+
parsedOptions: inout ParsedOptions,
146+
linkerOutputType: LinkOutputType,
147+
inputs: [TypedVirtualPath],
148+
outputFile: VirtualPath,
149+
sdkPath: String?,
150+
targetTriple: Triple
151+
) throws -> AbsolutePath {
152+
153+
// FIXME: If we used Clang as a linker instead of going straight to ld,
154+
// we wouldn't have to replicate a bunch of Clang's logic here.
155+
156+
// Always link the regular compiler_rt if it's present.
157+
//
158+
// Note: Normally we'd just add this unconditionally, but it's valid to build
159+
// Swift and use it as a linker without building compiler_rt.
160+
let darwinPlatformSuffix =
161+
targetTriple.darwinLibraryNameSuffix(distinguishSimulator: false)!
162+
let compilerRTPath =
163+
try clangLibraryPath(
164+
for: targetTriple, parsedOptions: &parsedOptions)
165+
.appending(component: "libclang_rt.\(darwinPlatformSuffix).a")
166+
if localFileSystem.exists(compilerRTPath) {
167+
commandLine.append(.path(.absolute(compilerRTPath)))
168+
}
169+
170+
// Set up for linking.
171+
let linkerTool: Tool
172+
switch linkerOutputType {
173+
case .dynamicLibrary:
174+
// Same as an executable, but with the -dylib flag
175+
commandLine.appendFlag("-dylib")
176+
fallthrough
177+
case .executable:
178+
linkerTool = .dynamicLinker
179+
let fSystemArgs = parsedOptions.filter {
180+
$0.option == .F || $0.option == .Fsystem
181+
}
182+
for opt in fSystemArgs {
183+
commandLine.appendFlag(.F)
184+
commandLine.appendPath(try VirtualPath(path: opt.argument.asSingle))
185+
}
186+
187+
// FIXME: Sanitizer args
188+
189+
commandLine.appendFlag("-arch")
190+
commandLine.appendFlag(targetTriple.archName)
191+
192+
try addArgsToLinkStdlib(
193+
to: &commandLine,
194+
parsedOptions: &parsedOptions,
195+
sdkPath: sdkPath,
196+
targetTriple: targetTriple
197+
)
198+
199+
// These custom arguments should be right before the object file at the
200+
// end.
201+
try commandLine.append(
202+
contentsOf: parsedOptions.filter { $0.option.group == .linker_option }
203+
)
204+
try commandLine.appendAllArguments(.Xlinker, from: &parsedOptions)
205+
206+
case .staticLibrary:
207+
linkerTool = .staticLinker
208+
commandLine.appendFlag(.static)
209+
break
210+
}
211+
212+
try addArgsToLinkARCLite(
213+
to: &commandLine,
214+
parsedOptions: &parsedOptions,
215+
targetTriple: targetTriple
216+
)
217+
addDeploymentTargetArgs(
218+
to: &commandLine,
219+
targetTriple: targetTriple
220+
)
221+
try addProfileGenerationArgs(
222+
to: &commandLine,
223+
parsedOptions: &parsedOptions,
224+
targetTriple: targetTriple
225+
)
226+
227+
commandLine.appendFlags(
228+
"-lobjc",
229+
"-lSystem",
230+
"-no_objc_category_merging"
231+
)
232+
233+
// Add the SDK path
234+
if let sdkPath = sdkPath {
235+
commandLine.appendFlag("-syslibroot")
236+
commandLine.appendPath(try VirtualPath(path: sdkPath))
237+
}
238+
239+
if parsedOptions.contains(.embed_bitcode) ||
240+
parsedOptions.contains(.embed_bitcode_marker) {
241+
commandLine.appendFlag("-bitcode_bundle")
242+
}
243+
244+
if parsedOptions.contains(.enable_app_extension) {
245+
commandLine.appendFlag("-application_extension")
246+
}
247+
248+
// Add inputs.
249+
commandLine.append(contentsOf: inputs.map { .path($0.file) })
250+
251+
// Add the output
252+
commandLine.appendFlag("-o")
253+
commandLine.appendPath(outputFile)
254+
255+
return try getToolPath(linkerTool)
256+
}
257+
}

0 commit comments

Comments
 (0)