From 807d3bc9728396be0d40dd6a95c93f7d5b2c1e25 Mon Sep 17 00:00:00 2001 From: Marc Prud'hommeaux Date: Sat, 14 Feb 2026 15:20:24 -0500 Subject: [PATCH] Handle launching adb from Skip Gradle plugin when ANDROID_HOME is not set and adb is not in PATH --- .../SkipBuild/Commands/PluginCommand.swift | 20 ++++++++++--------- Sources/SkipBuild/SkipCommand.swift | 20 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/Sources/SkipBuild/Commands/PluginCommand.swift b/Sources/SkipBuild/Commands/PluginCommand.swift index a6629829..7237d541 100644 --- a/Sources/SkipBuild/Commands/PluginCommand.swift +++ b/Sources/SkipBuild/Commands/PluginCommand.swift @@ -321,7 +321,7 @@ class SkipBuildPlugin @Inject constructor(private val os: ExecOperations) : Plug val devicesOut = ByteArrayOutputStream() os.exec { commandLine = listOf( - "adb".withExecutableExtension(), + adbPath(), "devices") environment = getEnvWithAndroidSdk() standardOutput = devicesOut @@ -379,7 +379,7 @@ class SkipBuildPlugin @Inject constructor(private val os: ExecOperations) : Plug //warn("launching app ${activity} on device: ${device.serialNumber}") os.exec { commandLine = listOf( - "adb".withExecutableExtension(), + adbPath(), "-s", device.serialNumber, "shell", @@ -494,11 +494,13 @@ private fun Properties.skipEnv(key: String) : String { return value } -private fun String.withExecutableExtension() : String { +private fun adbPath() : String { + val androidSdkPath = System.getenv()["ANDROID_HOME"] ?: getAndroidSdkPath() + val adbPath = "${androidSdkPath}${File.separator}platform-tools${File.separator}adb" if (org.gradle.internal.os.OperatingSystem.current().isWindows) { - return this + ".exe" + return adbPath + ".exe" } else { - return this + return adbPath } } @@ -524,12 +526,12 @@ private fun getEnvWithAndroidSdk(): Map { val androidSdkPath = currentEnv["ANDROID_HOME"] ?: getAndroidSdkPath() if (androidSdkPath != null) { - val platformTools = "${androidSdkPath}${File.separator}platform-tools" + val platformTools = "${androidSdkPath}${File.separator}platform-tools" // adb, systrace, etc. val tools = "${androidSdkPath}${File.separator}tools" - val toolsBin = "${tools}${File.separator}bin" - + val toolsBin = "${tools}${File.separator}bin" // sdkmanager, avdmanager, etc. + val currentPath = currentEnv["PATH"] ?: "" - currentEnv["PATH"] = "${toolsBin}${File.pathSeparator}${currentPath}" + currentEnv["PATH"] = "${platformTools}${File.pathSeparator}${toolsBin}${File.pathSeparator}${currentPath}" currentEnv["ANDROID_HOME"] = androidSdkPath } diff --git a/Sources/SkipBuild/SkipCommand.swift b/Sources/SkipBuild/SkipCommand.swift index 447dac34..9ab79486 100644 --- a/Sources/SkipBuild/SkipCommand.swift +++ b/Sources/SkipBuild/SkipCommand.swift @@ -804,6 +804,14 @@ extension ProcessInfo { } } + // also add tool paths for the various Android tools in case they are not already in the PATH + if var path = env["PATH"] { + if let androidHome = ProcessInfo.androidHome { + path += ":\(androidHome)/platform-tools:\(androidHome)/tools/bin:\(androidHome)/emulator" + } + env["PATH"] = path + } + return env } @@ -1037,13 +1045,7 @@ struct ToolOptions: ParsableArguments { case "swift": return self.swift ?? ProcessInfo.processInfo.environment["SKIP_SWIFT_PATH"] case "xcodebuild": return self.xcodebuild ?? ProcessInfo.processInfo.environment["SKIP_XCODEBUILD_PATH"] case "gradle": return self.gradle ?? ProcessInfo.processInfo.environment["SKIP_GRADLE_PATH"] - case "adb": - if let adb = self.adb ?? ProcessInfo.processInfo.environment["SKIP_ADB_PATH"] { return adb } - if let androidHome = ProcessInfo.androidHome, - FileManager.default.fileExists(atPath: "\(androidHome)/platform-tools/adb") { - return "\(androidHome)/platform-tools/adb" - } - return nil + case "adb": return self.adb ?? ProcessInfo.processInfo.environment["SKIP_ADB_PATH"] case "emulator": return self.emulator ?? ProcessInfo.processInfo.environment["SKIP_EMULATOR_PATH"] ?? self.emulatorBinary case "java": return ProcessInfo.processInfo.environment["JAVA_HOME"]?.appending("/bin/java") ?? ProcessInfo.defaultJavaHome.appending("/bin/java") default: return nil @@ -1052,7 +1054,7 @@ struct ToolOptions: ParsableArguments { if let toolPath = customTool() { return toolPath } - return try URL.findCommandInPath(toolName: tool, withAdditionalPaths: ProcessInfo.isARM ? ["/opt/homebrew/bin"] : ["/usr/local/bin"]).path + return try URL.findCommandInPath(toolName: tool, withAdditionalPaths: [ProcessInfo.homebrewRoot + "/bin"]).path } /// Returns the path to the emulator binary @@ -1078,7 +1080,7 @@ public struct ToolLaunchError : LocalizedError { extension URL { /// Locates the given tool in the user's path public static func findCommandInPath(toolName: String, withAdditionalPaths extraPATH: [String]) throws -> URL { - let env = ProcessInfo.processInfo.environment + let env = ProcessInfo.processInfo.environmentWithDefaultToolPaths let path = env["PATH"] ?? "" let pathParts = path.split(separator: ":", omittingEmptySubsequences: true).map(String.init) for pathPart in pathParts + extraPATH {