From 52db92a094c74311e903ba5b6e8a5181fa527f21 Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Wed, 6 May 2026 11:34:11 -0700 Subject: [PATCH] Executable.resolveExecutablePath should be async This method requires hitting the filesystem and should therefore be async as it can block Closes #263 --- Sources/Subprocess/Configuration.swift | 8 +++++--- Tests/SubprocessTests/LinterTests.swift | 6 +++--- Tests/SubprocessTests/TestSupport.swift | 6 ++++-- Tests/SubprocessTests/UnixTests.swift | 12 ++++++++---- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Sources/Subprocess/Configuration.swift b/Sources/Subprocess/Configuration.swift index 1a87c0ee..a8c6802a 100644 --- a/Sources/Subprocess/Configuration.swift +++ b/Sources/Subprocess/Configuration.swift @@ -290,9 +290,11 @@ public struct Executable: Sendable, Hashable { return .init(_config: .path(filePath)) } /// Resolves the full executable path using the given environment. - public func resolveExecutablePath(in environment: Environment) throws(SubprocessError) -> FilePath { - let path = try self.resolveExecutablePath(withPathValue: environment.pathValue()) - return FilePath(path) + public func resolveExecutablePath(in environment: Environment) async throws(SubprocessError) -> FilePath { + try await runOnBackgroundThread { () throws(SubprocessError) -> FilePath in + let path = try self.resolveExecutablePath(withPathValue: environment.pathValue()) + return FilePath(path) + } } } diff --git a/Tests/SubprocessTests/LinterTests.swift b/Tests/SubprocessTests/LinterTests.swift index bfac5a15..02a869f3 100644 --- a/Tests/SubprocessTests/LinterTests.swift +++ b/Tests/SubprocessTests/LinterTests.swift @@ -18,14 +18,14 @@ import SystemPackage #endif @testable import Subprocess -private func enableLintingTest() -> Bool { +private func enableLintingTest() async -> Bool { guard CommandLine.arguments.first(where: { $0.contains("/.build/") }) != nil else { return false } #if os(macOS) // Use xcrun do { - _ = try Executable.path("/usr/bin/xcrun") + _ = try await Executable.path("/usr/bin/xcrun") .resolveExecutablePath(in: .inherit) return true } catch { @@ -34,7 +34,7 @@ private func enableLintingTest() -> Bool { #else // Use swift-format directly do { - _ = try Executable.name("swift-format") + _ = try await Executable.name("swift-format") .resolveExecutablePath(in: .inherit) return true } catch { diff --git a/Tests/SubprocessTests/TestSupport.swift b/Tests/SubprocessTests/TestSupport.swift index 71f8fe8d..c5325770 100644 --- a/Tests/SubprocessTests/TestSupport.swift +++ b/Tests/SubprocessTests/TestSupport.swift @@ -89,8 +89,10 @@ extension Trait where Self == ConditionTrait { /// This test requires bash to run (instead of sh) static var requiresBash: Self { enabled( - if: (try? Executable.name("bash").resolveExecutablePath(in: .inherit)) != nil, - "This test requires bash (install `bash` package on Linux/BSD)" + "This test requires bash (install `bash` package on Linux/BSD)", + { + (try? await Executable.name("bash").resolveExecutablePath(in: .inherit)) != nil + } ) } } diff --git a/Tests/SubprocessTests/UnixTests.swift b/Tests/SubprocessTests/UnixTests.swift index 076c1c7a..60b8a0b3 100644 --- a/Tests/SubprocessTests/UnixTests.swift +++ b/Tests/SubprocessTests/UnixTests.swift @@ -120,8 +120,10 @@ extension SubprocessUnixTests { "This test requires root privileges" ), .enabled( - if: (try? Executable.name("ps").resolveExecutablePath(in: .inherit)) != nil, - "This test requires ps (install procps package on Debian or RedHat Linux distros)" + "This test requires ps (install procps package on Debian or RedHat Linux distros)", + { + (try? await Executable.name("ps").resolveExecutablePath(in: .inherit)) != nil + } ) ) func testSubprocessPlatformOptionsProcessGroupID() async throws { @@ -145,8 +147,10 @@ extension SubprocessUnixTests { @Test( .enabled( - if: (try? Executable.name("ps").resolveExecutablePath(in: .inherit)) != nil, - "This test requires ps (install procps package on Debian or RedHat Linux distros)" + "This test requires ps (install procps package on Debian or RedHat Linux distros)", + { + (try? await Executable.name("ps").resolveExecutablePath(in: .inherit)) != nil + } ) ) func testSubprocessPlatformOptionsCreateSession() async throws {