From 7eb175bb47412420582be061b345af4453e19a38 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sun, 18 Aug 2024 12:00:06 -0700 Subject: [PATCH] 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. --- .../GenericUnixToolchain+LinkerSupport.swift | 9 ++- .../Toolchains/GenericUnixToolchain.swift | 67 +++++++++---------- Tests/SwiftDriverTests/SwiftDriverTests.swift | 60 ++++++++++++++++- 3 files changed, 98 insertions(+), 38 deletions(-) diff --git a/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift b/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift index c0308b58d..4b4a1bd3f 100644 --- a/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift +++ b/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift @@ -218,9 +218,12 @@ extension GenericUnixToolchain { } if targetTriple.environment == .android { - if let sysroot = try getAndroidNDKSysrootPath() { - commandLine.appendFlag("--sysroot") - commandLine.appendPath(sysroot) + if let sysroot = parsedOptions.getLastArgument(.sysroot)?.asSingle { + commandLine.appendFlag("-sysroot") + try commandLine.appendPath(VirtualPath(path: sysroot)) + } else if let sysroot = AndroidNDK.getDefaultSysrootPath(in: self.env) { + commandLine.appendFlag("-sysroot") + try commandLine.appendPath(VirtualPath(path: sysroot.pathString)) } } else if let path = targetInfo.sdkPath?.path { commandLine.appendFlag("--sysroot") diff --git a/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift b/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift index 1b2d96808..105104a78 100644 --- a/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift @@ -14,6 +14,34 @@ import protocol TSCBasic.FileSystem import struct TSCBasic.AbsolutePath import var TSCBasic.localFileSystem +internal enum AndroidNDK { + internal static func getOSName() -> String? { + // The NDK is only available on macOS, linux and windows hosts currently. +#if os(Windows) + "windows" +#elseif os(Linux) + "linux" +#elseif os(macOS) + "darwin" +#else + nil +#endif + } + + internal static func getDefaultSysrootPath(in env: [String:String]) -> AbsolutePath? { + // The NDK is only available on an x86_64 hosts currently. +#if arch(x86_64) + guard let ndk = env["ANDROID_NDK_ROOT"], let os = getOSName() else { return nil } + return try? AbsolutePath(validating: ndk) + .appending(components: "toolchains", "llvm", "prebuilt") + .appending(component: "\(os)-x86_64") + .appending(component: "sysroot") +#else + return nil +#endif + } +} + /// Toolchain for Unix-like systems. public final class GenericUnixToolchain: Toolchain { public let env: [String: String] @@ -118,38 +146,6 @@ public final class GenericUnixToolchain: Toolchain { return "libclang_rt.\(sanitizer.libraryName)-\(targetTriple.archName)\(environment).a" } - private func getAndroidNDKHostOSSuffix() -> String? { -#if os(Windows) - "windows" -#elseif os(Linux) - "linux" -#elseif os(macOS) - "darwin" -#else - // The NDK is only available on macOS, linux and windows hosts. - nil -#endif - } - - func getAndroidNDKSysrootPath() throws -> AbsolutePath? { -#if arch(x86_64) - // The NDK's sysroot should be specified in the environment. - guard let ndk = env["ANDROID_NDK_ROOT"], - let osSuffix = getAndroidNDKHostOSSuffix() else { - return nil - } - var sysroot: AbsolutePath = - try AbsolutePath(validating: ndk) - .appending(components: "toolchains", "llvm", "prebuilt") - .appending(component: "\(osSuffix)-x86_64") - .appending(component: "sysroot") - return sysroot -#else - // The NDK is only available on an x86_64 host. - return nil -#endif - } - public func addPlatformSpecificCommonFrontendOptions( commandLine: inout [Job.ArgTemplate], inputs: inout [TypedVirtualPath], @@ -157,9 +153,12 @@ public final class GenericUnixToolchain: Toolchain { driver: inout Driver ) throws { if driver.targetTriple.environment == .android { - if let sysroot = try getAndroidNDKSysrootPath() { + if let sysroot = driver.parsedOptions.getLastArgument(.sysroot)?.asSingle { + commandLine.appendFlag("-sysroot") + try commandLine.appendPath(VirtualPath(path: sysroot)) + } else if let sysroot = AndroidNDK.getDefaultSysrootPath(in: self.env) { commandLine.appendFlag("-sysroot") - commandLine.appendFlag(sysroot.pathString) + try commandLine.appendPath(VirtualPath(path: sysroot.pathString)) } } } diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index d57954e04..d074ceea0 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -7929,7 +7929,65 @@ final class SwiftDriverTests: XCTestCase { ])) } } - + + func testAndroidNDK() throws { + try withTemporaryDirectory { path in + do { + let sysroot = path.appending(component: "sysroot") + var driver = try Driver(args: [ + "swiftc", "-target", "aarch64-unknown-linux-android", "-sysroot", sysroot.pathString, #file + ]) + let jobs = try driver.planBuild().removingAutolinkExtractJobs() + let frontend = try XCTUnwrap(jobs.first) + XCTAssertTrue(frontend.commandLine.contains(subsequence: [ + .flag("-sysroot"), + .path(.absolute(sysroot)) + ])) + } + + do { + var env = ProcessEnv.vars + env["ANDROID_NDK_ROOT"] = path.appending(component: "ndk").nativePathString(escaped: false) + + let sysroot = path.appending(component: "sysroot") + var driver = try Driver(args: [ + "swiftc", "-target", "aarch64-unknown-linux-android", "-sysroot", sysroot.pathString, #file + ], env: env) + let jobs = try driver.planBuild().removingAutolinkExtractJobs() + let frontend = try XCTUnwrap(jobs.first) + XCTAssertTrue(frontend.commandLine.contains(subsequence: [ + .flag("-sysroot"), + .path(.absolute(sysroot)) + ])) + } + + do { + let sysroot = path.appending(component: "ndk") + + var env = ProcessEnv.vars + env["ANDROID_NDK_ROOT"] = sysroot.nativePathString(escaped: false) + +#if os(Windows) + let os = "windows" +#elseif os(macOS) + let os = "darwin" +#else + let os = "linux" +#endif + + var driver = try Driver(args: [ + "swiftc", "-target", "aarch64-unknown-linux-android", #file + ], env: env) + let jobs = try driver.planBuild().removingAutolinkExtractJobs() + let frontend = try XCTUnwrap(jobs.first) + XCTAssertTrue(frontend.commandLine.contains(subsequence: [ + .flag("-sysroot"), + .path(.absolute(sysroot.appending(components: "toolchains", "llvm", "prebuilt", "\(os)-x86_64", "sysroot"))), + ])) + } + } + } + func testEmitAPIDescriptorEmitModule() throws { try withTemporaryDirectory { path in do {