From 8716d0f11111d1ae405bf5f193dec6a7b90a9c3c Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Wed, 13 Aug 2025 14:28:51 -0700 Subject: [PATCH] Create one Android SDK per Swift SDK This matches what WebAssembly is doing, and avoids an issue where multiple installed Android Swift SDKs would cause the build to fail. --- Sources/SWBAndroidPlatform/Plugin.swift | 66 ++++++++++++------- .../RunDestinationTestSupport.swift | 2 +- .../SWBAndroidPlatformTests.swift | 2 +- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/Sources/SWBAndroidPlatform/Plugin.swift b/Sources/SWBAndroidPlatform/Plugin.swift index 117e7c6e..0f1efa1c 100644 --- a/Sources/SWBAndroidPlatform/Plugin.swift +++ b/Sources/SWBAndroidPlatform/Plugin.swift @@ -149,26 +149,44 @@ struct AndroidPlatformExtension: PlatformInfoExtension { hostOperatingSystem: context.hostOperatingSystem )) ?? [] - let swiftSettings: [String: PropertyListItem] - // FIXME: We need a way to narrow down the list, possibly by passing down a Swift SDK identifier from SwiftPM - // The resource path should be the same for all triples in an Android Swift SDK - if let androidSwiftSDK = androidSwiftSDKs.only, let swiftResourceDir = Set(androidSwiftSDK.targetTriples.values.map { tripleProperties in androidSwiftSDK.path.join(tripleProperties.swiftResourcesPath) }).only { - swiftSettings = [ - "SWIFT_LIBRARY_PATH": .plString(swiftResourceDir.join("android").str), - "SWIFT_RESOURCE_DIR": .plString(swiftResourceDir.str), - "SWIFT_TARGET_TRIPLE": .plString("$(CURRENT_ARCH)-unknown-$(SWIFT_PLATFORM_TARGET_PREFIX)$(LLVM_TARGET_TRIPLE_SUFFIX)"), - "LIBRARY_SEARCH_PATHS": "$(inherited) $(SWIFT_RESOURCE_DIR)/../$(__ANDROID_TRIPLE_$(CURRENT_ARCH))", - ].merging(abis.map { - ("__ANDROID_TRIPLE_\($0.value.llvm_triple.arch)", .plString($0.value.triple)) - }, uniquingKeysWith: { _, new in new }) - } else { - swiftSettings = [:] - } + return try androidSwiftSDKs.map { androidSwiftSDK in + let perArchSwiftResourceDirs = try Dictionary(grouping: androidSwiftSDK.targetTriples, by: { try LLVMTriple($0.key).arch }).mapValues { + let paths = Set($0.compactMap { $0.value.swiftResourcesPath }) + guard let path = paths.only else { + throw StubError.error("The resource path should be the same for all triples of the same architecture in an Android Swift SDK") + } + return Path(path) + } + + return sdk( + canonicalName: androidSwiftSDK.identifier, + androidPlatform: androidPlatform, + androidNdk: androidNdk, + defaultProperties: defaultProperties, + customProperties: [ + "SWIFT_TARGET_TRIPLE": .plString("$(CURRENT_ARCH)-unknown-$(SWIFT_PLATFORM_TARGET_PREFIX)$(LLVM_TARGET_TRIPLE_SUFFIX)"), + "LIBRARY_SEARCH_PATHS": "$(inherited) $(SWIFT_RESOURCE_DIR)/../$(__ANDROID_TRIPLE_$(CURRENT_ARCH))", + ].merging(perArchSwiftResourceDirs.map { + [ + ("SWIFT_LIBRARY_PATH[arch=\($0.key)]", .plString($0.value.join("android").str)), + ("SWIFT_RESOURCE_DIR[arch=\($0.key)]", .plString($0.value.str)), + ] + }.flatMap { $0 }, uniquingKeysWith: { _, new in new }).merging(abis.map { + ("__ANDROID_TRIPLE_\($0.value.llvm_triple.arch)", .plString($0.value.triple)) + }, uniquingKeysWith: { _, new in new })) + } + [ + // Fallback SDK for when there are no Swift SDKs (Android SDK is still usable for C/C++-only code) + sdk(androidPlatform: androidPlatform, androidNdk: androidNdk, defaultProperties: defaultProperties) + ] + } - return [(androidNdk.sysroot.path, androidPlatform, [ + private func sdk(canonicalName: String? = nil, androidPlatform: Platform, androidNdk: AndroidSDK.NDK, defaultProperties: [String: PropertyListItem], customProperties: [String: PropertyListItem] = [:]) -> (path: Path, platform: SWBCore.Platform?, data: [String: PropertyListItem]) { + return (androidNdk.sysroot.path, androidPlatform, [ "Type": .plString("SDK"), - "Version": .plString("0.0.0"), - "CanonicalName": .plString("android"), + "Version": .plString(androidNdk.version.description), + "CanonicalName": .plString(canonicalName ?? "android\(androidNdk.version.description)"), + // "android.ndk" is an alias for the "Android SDK without a Swift SDK" scenario in order for tests to deterministically pick a single Android destination regardless of how many Android Swift SDKs may be installed. + "Aliases": .plArray([.plString("android")] + (canonicalName == nil ? [.plString("android.ndk")] : [])), "IsBaseSDK": .plBool(true), "DefaultProperties": .plDict([ "PLATFORM_NAME": .plString("android"), @@ -178,14 +196,14 @@ struct AndroidPlatformExtension: PlatformInfoExtension { // FIXME: Make this configurable in a better way so we don't need to push build settings at the SDK definition level "LLVM_TARGET_TRIPLE_OS_VERSION": .plString("$(SWIFT_PLATFORM_TARGET_PREFIX)"), "LLVM_TARGET_TRIPLE_SUFFIX": .plString("-android$($(DEPLOYMENT_TARGET_SETTING_NAME))"), - ].merging(swiftSettings, uniquingKeysWith: { _, new in new })), + ].merging(customProperties, uniquingKeysWith: { _, new in new })), "SupportedTargets": .plDict([ "android": .plDict([ - "Archs": .plArray(abis.map { .plString($0.value.llvm_triple.arch) }), + "Archs": .plArray(androidNdk.abis.map { .plString($0.value.llvm_triple.arch) }), "DeploymentTargetSettingName": .plString("ANDROID_DEPLOYMENT_TARGET"), - "DefaultDeploymentTarget": .plString("\(deploymentTargetRange.min)"), - "MinimumDeploymentTarget": .plString("\(deploymentTargetRange.min)"), - "MaximumDeploymentTarget": .plString("\(deploymentTargetRange.max)"), + "DefaultDeploymentTarget": .plString("\(androidNdk.deploymentTargetRange.min)"), + "MinimumDeploymentTarget": .plString("\(androidNdk.deploymentTargetRange.min)"), + "MaximumDeploymentTarget": .plString("\(androidNdk.deploymentTargetRange.max)"), "LLVMTargetTripleEnvironment": .plString("android"), // FIXME: androideabi for armv7! "LLVMTargetTripleSys": .plString("linux"), "LLVMTargetTripleVendor": .plString("none"), @@ -194,7 +212,7 @@ struct AndroidPlatformExtension: PlatformInfoExtension { "Toolchains": .plArray([ .plString("android") ]) - ])] + ]) } } diff --git a/Sources/SWBTestSupport/RunDestinationTestSupport.swift b/Sources/SWBTestSupport/RunDestinationTestSupport.swift index 69b83184..9041f82d 100644 --- a/Sources/SWBTestSupport/RunDestinationTestSupport.swift +++ b/Sources/SWBTestSupport/RunDestinationTestSupport.swift @@ -281,7 +281,7 @@ extension _RunDestinationInfo { /// A run destination targeting Android generic device, using the public SDK. package static var android: Self { - return .init(platform: "android", sdk: "android", sdkVariant: "android", targetArchitecture: "undefined_arch", supportedArchitectures: ["armv7", "aarch64", "riscv64", "i686", "x86_64"], disableOnlyActiveArch: true) + return .init(platform: "android", sdk: "android.ndk", sdkVariant: "android", targetArchitecture: "undefined_arch", supportedArchitectures: ["armv7", "aarch64", "riscv64", "i686", "x86_64"], disableOnlyActiveArch: true) } /// A run destination targeting QNX generic device, using the public SDK. diff --git a/Tests/SWBAndroidPlatformTests/SWBAndroidPlatformTests.swift b/Tests/SWBAndroidPlatformTests/SWBAndroidPlatformTests.swift index 117588f9..3a808d48 100644 --- a/Tests/SWBAndroidPlatformTests/SWBAndroidPlatformTests.swift +++ b/Tests/SWBAndroidPlatformTests/SWBAndroidPlatformTests.swift @@ -39,7 +39,7 @@ fileprivate struct AndroidBuildOperationTests: CoreBasedTests { "CODE_SIGNING_ALLOWED": "NO", "DEFINES_MODULE": "YES", "PRODUCT_NAME": "$(TARGET_NAME)", - "SDKROOT": "android", + "SDKROOT": "android.ndk", "SUPPORTED_PLATFORMS": "android", "ANDROID_DEPLOYMENT_TARGET": "22.0", "ANDROID_DEPLOYMENT_TARGET[arch=riscv64]": "35.0",