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 {