From 112ae4ab1fe0e8cfedbbfa61297e6b99b870e415 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 12 Mar 2024 10:06:45 -0700 Subject: [PATCH 1/2] SwiftDriver: initial work to properly handle android cross-compilation The intent here is to permit the Windows/macOS style cross-compilation for Android. This involves passing `-sdk` with the path to the "Swift SDK" which overlays the system's native SDK (NDK). The `ANDROID_NDK_ROOT` is a well-defined environment variable (setup by the SDK installer as well as a general expectation for Android development) that identifies the root of the installation of the NDK. This allows us to locate the native SDK root (`--sysroot`) for driving the linker driver amongst other paths. --- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 20 +++++++++ .../GenericUnixToolchain+LinkerSupport.swift | 45 +++++++++++++++---- Sources/SwiftOptions/Options.swift | 2 + 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index 1c9868e8d..2168afe4e 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -182,6 +182,26 @@ extension Driver { try commandLine.appendAll(.F, .Fsystem, from: &parsedOptions) try commandLine.appendAll(.vfsoverlay, from: &parsedOptions) + if targetTriple.environment == .android { + if let ndk = env["ANDROID_NDK_ROOT"] { + var sysroot: AbsolutePath = + try AbsolutePath(validating: ndk) + .appending(components: "toolchains", "llvm", "prebuilt") +#if arch(x86_64) +#if os(Windows) + .appending(component: "windows-x86_64") +#elseif os(Linux) + .appending(component: "linux-x86_64") +#else + .appending(component: "darwin-x86_64") +#endif +#endif + .appending(component: "sysroot") + commandLine.appendFlag("-sysroot") + commandLine.appendFlag(sysroot.pathString) + } + } + if let gccToolchain = parsedOptions.getLastArgument(.gccToolchain) { appendXccFlag("--gcc-toolchain=\(gccToolchain.asSingle)") } diff --git a/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift b/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift index e17d8a88a..772b0c0f3 100644 --- a/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift +++ b/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift @@ -147,14 +147,25 @@ extension GenericUnixToolchain { } } + if let sdk = parsedOptions.getLastArgument(.sdk)?.asSingle ?? env["SDKROOT"], !sdk.isEmpty { + for libpath in targetInfo.runtimeLibraryImportPaths { + commandLine.appendFlag(.L) + commandLine.appendPath(VirtualPath.lookup(libpath.path)) + } + } + if !isEmbeddedEnabled && !parsedOptions.hasArgument(.nostartfiles) { - let swiftrtPath = VirtualPath.lookup(targetInfo.runtimeResourcePath.path) - .appending( - components: targetTriple.platformName() ?? "", - String(majorArchitectureName(for: targetTriple)), - "swiftrt.o" - ) - commandLine.appendPath(swiftrtPath) + let rsrc: VirtualPath + if let sdk = parsedOptions.getLastArgument(.sdk)?.asSingle ?? env["SDKROOT"], !sdk.isEmpty { + rsrc = try VirtualPath(path: AbsolutePath(validating: sdk) + .appending(components: "usr", "lib", "swift") + .pathString) + } else { + rsrc = VirtualPath.lookup(targetInfo.runtimeResourcePath.path) + } + let platform: String = targetTriple.platformName() ?? "" + let architecture: String = majorArchitectureName(for: targetTriple) + commandLine.appendPath(rsrc.appending(components: platform, architecture, "swiftrt.o")) } // If we are linking statically, we need to add all @@ -194,7 +205,25 @@ extension GenericUnixToolchain { commandLine.appendPath(try VirtualPath(path: opt.argument.asSingle)) } - if let path = targetInfo.sdkPath?.path { + if targetTriple.environment == .android { + if let ndk = env["ANDROID_NDK_ROOT"] { + var sysroot: AbsolutePath = + try AbsolutePath(validating: ndk) + .appending(components: "toolchains", "llvm", "prebuilt") +#if arch(x86_64) +#if os(Windows) + .appending(component: "windows-x86_64") +#elseif os(Linux) + .appending(component: "linux-x86_64") +#else + .appending(component: "darwin-x86_64") +#endif +#endif + .appending(component: "sysroot") + commandLine.appendFlag("--sysroot") + commandLine.appendPath(sysroot) + } + } else if let path = targetInfo.sdkPath?.path { commandLine.appendFlag("--sysroot") commandLine.appendPath(VirtualPath.lookup(path)) } diff --git a/Sources/SwiftOptions/Options.swift b/Sources/SwiftOptions/Options.swift index 3365d6713..770fafd96 100644 --- a/Sources/SwiftOptions/Options.swift +++ b/Sources/SwiftOptions/Options.swift @@ -788,6 +788,7 @@ extension Option { public static let swiftVersion: Option = Option("-swift-version", .separate, attributes: [.frontend, .moduleInterface], metaVar: "", helpText: "Interpret input according to a specific Swift language version number") public static let switchCheckingInvocationThresholdEQ: Option = Option("-switch-checking-invocation-threshold=", .joined, attributes: [.helpHidden, .frontend, .noDriver]) public static let symbolGraphMinimumAccessLevel: Option = Option("-symbol-graph-minimum-access-level", .separate, attributes: [.helpHidden, .frontend, .noInteractive, .supplementaryOutput], metaVar: "", helpText: "Include symbols with this access level or more when emitting a symbol graph") + public static let sysroot: Option = Option("-sysroot", .separate, attributes: [.frontend, .argumentIsPath], metaVar: "", helpText: "Native Platform sysroot") public static let S: Option = Option("-S", .flag, alias: Option.emitAssembly, attributes: [.frontend, .noInteractive], group: .modes) public static let tabWidth: Option = Option("-tab-width", .separate, attributes: [.noInteractive, .noBatch, .indent], metaVar: "", helpText: "Width of tab character.", group: .codeFormatting) public static let targetCpu: Option = Option("-target-cpu", .separate, attributes: [.frontend, .moduleInterface], helpText: "Generate code for a particular CPU variant") @@ -1651,6 +1652,7 @@ extension Option { Option.swiftVersion, Option.switchCheckingInvocationThresholdEQ, Option.symbolGraphMinimumAccessLevel, + Option.sysroot, Option.S, Option.tabWidth, Option.targetCpu, From 7e832532eb18bdc779950793826fa1bacfd41383 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 9 Jul 2024 14:55:41 -0700 Subject: [PATCH 2/2] review fixes --- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 20 -------- .../GenericUnixToolchain+LinkerSupport.swift | 24 ++-------- .../Toolchains/GenericUnixToolchain.swift | 46 +++++++++++++++++++ 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index 2168afe4e..1c9868e8d 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -182,26 +182,6 @@ extension Driver { try commandLine.appendAll(.F, .Fsystem, from: &parsedOptions) try commandLine.appendAll(.vfsoverlay, from: &parsedOptions) - if targetTriple.environment == .android { - if let ndk = env["ANDROID_NDK_ROOT"] { - var sysroot: AbsolutePath = - try AbsolutePath(validating: ndk) - .appending(components: "toolchains", "llvm", "prebuilt") -#if arch(x86_64) -#if os(Windows) - .appending(component: "windows-x86_64") -#elseif os(Linux) - .appending(component: "linux-x86_64") -#else - .appending(component: "darwin-x86_64") -#endif -#endif - .appending(component: "sysroot") - commandLine.appendFlag("-sysroot") - commandLine.appendFlag(sysroot.pathString) - } - } - if let gccToolchain = parsedOptions.getLastArgument(.gccToolchain) { appendXccFlag("--gcc-toolchain=\(gccToolchain.asSingle)") } diff --git a/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift b/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift index 772b0c0f3..d364a1451 100644 --- a/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift +++ b/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift @@ -147,7 +147,7 @@ extension GenericUnixToolchain { } } - if let sdk = parsedOptions.getLastArgument(.sdk)?.asSingle ?? env["SDKROOT"], !sdk.isEmpty { + if targetInfo.sdkPath != nil { for libpath in targetInfo.runtimeLibraryImportPaths { commandLine.appendFlag(.L) commandLine.appendPath(VirtualPath.lookup(libpath.path)) @@ -156,10 +156,9 @@ extension GenericUnixToolchain { if !isEmbeddedEnabled && !parsedOptions.hasArgument(.nostartfiles) { let rsrc: VirtualPath - if let sdk = parsedOptions.getLastArgument(.sdk)?.asSingle ?? env["SDKROOT"], !sdk.isEmpty { - rsrc = try VirtualPath(path: AbsolutePath(validating: sdk) - .appending(components: "usr", "lib", "swift") - .pathString) + // Prefer the swiftrt.o runtime file from the SDK if it's specified. + if let sdk = targetInfo.sdkPath.flatMap({ VirtualPath.lookup($0.path) }) { + rsrc = sdk.appending(components: "usr", "lib", "swift") } else { rsrc = VirtualPath.lookup(targetInfo.runtimeResourcePath.path) } @@ -206,20 +205,7 @@ extension GenericUnixToolchain { } if targetTriple.environment == .android { - if let ndk = env["ANDROID_NDK_ROOT"] { - var sysroot: AbsolutePath = - try AbsolutePath(validating: ndk) - .appending(components: "toolchains", "llvm", "prebuilt") -#if arch(x86_64) -#if os(Windows) - .appending(component: "windows-x86_64") -#elseif os(Linux) - .appending(component: "linux-x86_64") -#else - .appending(component: "darwin-x86_64") -#endif -#endif - .appending(component: "sysroot") + if let sysroot = try getAndroidNDKSysrootPath() { commandLine.appendFlag("--sysroot") commandLine.appendPath(sysroot) } diff --git a/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift b/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift index 7abe9d771..1b2d96808 100644 --- a/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift @@ -117,4 +117,50 @@ public final class GenericUnixToolchain: Toolchain { let environment = (targetTriple.environment == .android) ? "-android" : "" 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], + frontendTargetInfo: FrontendTargetInfo, + driver: inout Driver + ) throws { + if driver.targetTriple.environment == .android { + if let sysroot = try getAndroidNDKSysrootPath() { + commandLine.appendFlag("-sysroot") + commandLine.appendFlag(sysroot.pathString) + } + } + } }