Skip to content

Commit c764a39

Browse files
committed
SwiftDriver: forward -sysroot provided by the user
This addresses a missing component of the initial Android support. When the user specifies `-sysroot`, that should always be given precedence as it is explicitly specified by the user. The `ANDROID_NDK_ROOT` environment variable is set by the Android NDK and is used as a _default_ value in the scenario that the user does not specify the `-sysroot` option. This makes Android both behave more like the Windows platform and also ensures that the user has full control over the behaviour of the toolchain. Take the opportunity to refactor some of the code to extract the Android NDK specific helpers into an uninhabited enum and provide some behavioural test cases.
1 parent effb4e5 commit c764a39

File tree

3 files changed

+98
-38
lines changed

3 files changed

+98
-38
lines changed

Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift

+6-3
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,12 @@ extension GenericUnixToolchain {
218218
}
219219

220220
if targetTriple.environment == .android {
221-
if let sysroot = try getAndroidNDKSysrootPath() {
222-
commandLine.appendFlag("--sysroot")
223-
commandLine.appendPath(sysroot)
221+
if let sysroot = parsedOptions.getLastArgument(.sysroot)?.asSingle {
222+
commandLine.appendFlag("-sysroot")
223+
try commandLine.appendPath(VirtualPath(path: sysroot))
224+
} else if let sysroot = AndroidNDK.getDefaultSysrootPath(in: self.env) {
225+
commandLine.appendFlag("-sysroot")
226+
try commandLine.appendPath(VirtualPath(path: sysroot.pathString))
224227
}
225228
} else if let path = targetInfo.sdkPath?.path {
226229
commandLine.appendFlag("--sysroot")

Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift

+33-34
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,34 @@ import protocol TSCBasic.FileSystem
1414
import struct TSCBasic.AbsolutePath
1515
import var TSCBasic.localFileSystem
1616

17+
internal enum AndroidNDK {
18+
internal static func getOSName() -> String? {
19+
// The NDK is only available on macOS, linux and windows hosts currently.
20+
#if os(Windows)
21+
"windows"
22+
#elseif os(Linux)
23+
"linux"
24+
#elseif os(macOS)
25+
"darwin"
26+
#else
27+
nil
28+
#endif
29+
}
30+
31+
internal static func getDefaultSysrootPath(in env: [String:String]) -> AbsolutePath? {
32+
// The NDK is only available on an x86_64 hosts currently.
33+
#if arch(x86_64)
34+
guard let ndk = env["ANDROID_NDK_ROOT"], let os = getOSName() else { return nil }
35+
return try? AbsolutePath(validating: ndk)
36+
.appending(components: "toolchains", "llvm", "prebuilt")
37+
.appending(component: "\(os)-x86_64")
38+
.appending(component: "sysroot")
39+
#else
40+
return nil
41+
#endif
42+
}
43+
}
44+
1745
/// Toolchain for Unix-like systems.
1846
public final class GenericUnixToolchain: Toolchain {
1947
public let env: [String: String]
@@ -118,48 +146,19 @@ public final class GenericUnixToolchain: Toolchain {
118146
return "libclang_rt.\(sanitizer.libraryName)-\(targetTriple.archName)\(environment).a"
119147
}
120148

121-
private func getAndroidNDKHostOSSuffix() -> String? {
122-
#if os(Windows)
123-
"windows"
124-
#elseif os(Linux)
125-
"linux"
126-
#elseif os(macOS)
127-
"darwin"
128-
#else
129-
// The NDK is only available on macOS, linux and windows hosts.
130-
nil
131-
#endif
132-
}
133-
134-
func getAndroidNDKSysrootPath() throws -> AbsolutePath? {
135-
#if arch(x86_64)
136-
// The NDK's sysroot should be specified in the environment.
137-
guard let ndk = env["ANDROID_NDK_ROOT"],
138-
let osSuffix = getAndroidNDKHostOSSuffix() else {
139-
return nil
140-
}
141-
var sysroot: AbsolutePath =
142-
try AbsolutePath(validating: ndk)
143-
.appending(components: "toolchains", "llvm", "prebuilt")
144-
.appending(component: "\(osSuffix)-x86_64")
145-
.appending(component: "sysroot")
146-
return sysroot
147-
#else
148-
// The NDK is only available on an x86_64 host.
149-
return nil
150-
#endif
151-
}
152-
153149
public func addPlatformSpecificCommonFrontendOptions(
154150
commandLine: inout [Job.ArgTemplate],
155151
inputs: inout [TypedVirtualPath],
156152
frontendTargetInfo: FrontendTargetInfo,
157153
driver: inout Driver
158154
) throws {
159155
if driver.targetTriple.environment == .android {
160-
if let sysroot = try getAndroidNDKSysrootPath() {
156+
if let sysroot = driver.parsedOptions.getLastArgument(.sysroot)?.asSingle {
157+
commandLine.appendFlag("-sysroot")
158+
try commandLine.appendPath(VirtualPath(path: sysroot))
159+
} else if let sysroot = AndroidNDK.getDefaultSysrootPath(in: self.env) {
161160
commandLine.appendFlag("-sysroot")
162-
commandLine.appendFlag(sysroot.pathString)
161+
try commandLine.appendPath(VirtualPath(path: sysroot.pathString))
163162
}
164163
}
165164
}

Tests/SwiftDriverTests/SwiftDriverTests.swift

+59-1
Original file line numberDiff line numberDiff line change
@@ -7929,7 +7929,65 @@ final class SwiftDriverTests: XCTestCase {
79297929
]))
79307930
}
79317931
}
7932-
7932+
7933+
func testAndroidNDK() throws {
7934+
try withTemporaryDirectory { path in
7935+
do {
7936+
let sysroot = path.appending(component: "sysroot")
7937+
var driver = try Driver(args: [
7938+
"swiftc", "-target", "aarch64-unknown-linux-android", "-sysroot", sysroot.pathString, #file
7939+
])
7940+
let jobs = try driver.planBuild().removingAutolinkExtractJobs()
7941+
let frontend = try XCTUnwrap(jobs.first)
7942+
XCTAssertTrue(frontend.commandLine.contains(subsequence: [
7943+
.flag("-sysroot"),
7944+
.path(.absolute(sysroot))
7945+
]))
7946+
}
7947+
7948+
do {
7949+
var env = ProcessEnv.vars
7950+
env["ANDROID_NDK_ROOT"] = path.appending(component: "ndk").nativePathString(escaped: false)
7951+
7952+
let sysroot = path.appending(component: "sysroot")
7953+
var driver = try Driver(args: [
7954+
"swiftc", "-target", "aarch64-unknown-linux-android", "-sysroot", sysroot.pathString, #file
7955+
], env: env)
7956+
let jobs = try driver.planBuild().removingAutolinkExtractJobs()
7957+
let frontend = try XCTUnwrap(jobs.first)
7958+
XCTAssertTrue(frontend.commandLine.contains(subsequence: [
7959+
.flag("-sysroot"),
7960+
.path(.absolute(sysroot))
7961+
]))
7962+
}
7963+
7964+
do {
7965+
let sysroot = path.appending(component: "ndk")
7966+
7967+
var env = ProcessEnv.vars
7968+
env["ANDROID_NDK_ROOT"] = sysroot.nativePathString(escaped: false)
7969+
7970+
#if os(Windows)
7971+
let os = "windows"
7972+
#elseif os(macOS)
7973+
let os = "darwin"
7974+
#else
7975+
let os = "linux"
7976+
#endif
7977+
7978+
var driver = try Driver(args: [
7979+
"swiftc", "-target", "aarch64-unknown-linux-android", #file
7980+
], env: env)
7981+
let jobs = try driver.planBuild().removingAutolinkExtractJobs()
7982+
let frontend = try XCTUnwrap(jobs.first)
7983+
XCTAssertTrue(frontend.commandLine.contains(subsequence: [
7984+
.flag("-sysroot"),
7985+
.path(.absolute(sysroot.appending(components: "toolchains", "llvm", "prebuilt", "\(os)-x86_64", "sysroot"))),
7986+
]))
7987+
}
7988+
}
7989+
}
7990+
79337991
func testEmitAPIDescriptorEmitModule() throws {
79347992
try withTemporaryDirectory { path in
79357993
do {

0 commit comments

Comments
 (0)