Skip to content

Fix ArgumentParser build failure for WASI (WebAssembly System Interface) #794

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ jobs:
- name: Build
run: cmake --build .cmake-build

wasm-build:
name: Wasm Build
runs-on: ubuntu-latest
container:
image: swift:6.1-noble
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Install Swift SDKs for WebAssembly
run: |
# TODO: We can replace these Swift SDKs with the swift.org one once it supports Foundation.
swift sdk install https://github.com/swiftwasm/swift/releases/download/swift-wasm-6.1-RELEASE/swift-wasm-6.1-RELEASE-wasm32-unknown-wasi.artifactbundle.zip --checksum 7550b4c77a55f4b637c376f5d192f297fe185607003a6212ad608276928db992
swift sdk install https://github.com/swiftwasm/swift/releases/download/swift-wasm-6.1-RELEASE/swift-wasm-6.1-RELEASE-wasm32-unknown-wasip1-threads.artifactbundle.zip --checksum 0dd273be28741f8e1eb00682c39bdc956361ed24b5572e183dd8a4e9d1c5f6ec
swift sdk list
- name: Build
run: |
swift build --swift-sdk wasm32-unknown-wasi --target ArgumentParser
swift build --swift-sdk wasm32-unknown-wasip1-threads --target ArgumentParser

soundness:
name: Soundness
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
Expand Down
10 changes: 10 additions & 0 deletions Sources/ArgumentParser/Parsable Properties/CompletionKind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public struct CompletionKind {
case directory
case shellCommand(String)
case custom(@Sendable ([String], Int, String) -> [String])
#if !canImport(Dispatch)
@available(*, unavailable, message: "DispatchSemaphore is unavailable")
#endif
case customAsync(@Sendable ([String], Int, String) async -> [String])
case customDeprecated(@Sendable ([String]) -> [String])
}
Expand Down Expand Up @@ -181,11 +184,18 @@ public struct CompletionKind {
///
/// The same as `custom(@Sendable @escaping ([String], Int, String) -> [String])`,
/// except that the closure is asynchronous.
#if !canImport(Dispatch)
@available(*, unavailable, message: "DispatchSemaphore is unavailable")
#endif
@available(macOS 10.15, macCatalyst 13, iOS 13, tvOS 13, watchOS 6, *)
public static func custom(
_ completion: @Sendable @escaping ([String], Int, String) async -> [String]
) -> CompletionKind {
#if !canImport(Dispatch)
fatalError("DispatchSemaphore is unavailable")
#else
CompletionKind(kind: .customAsync(completion))
#endif
}

/// Deprecated; only kept for backwards compatibility.
Expand Down
15 changes: 15 additions & 0 deletions Sources/ArgumentParser/Parsing/CommandParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
//===----------------------------------------------------------------------===//

#if swift(>=6.0)
#if canImport(Dispatch)
@preconcurrency private import class Dispatch.DispatchSemaphore
#endif
internal import class Foundation.NSLock
internal import class Foundation.ProcessInfo
#else
#if canImport(Dispatch)
@preconcurrency import class Dispatch.DispatchSemaphore
#endif
import class Foundation.NSLock
import class Foundation.ProcessInfo
#endif
Expand Down Expand Up @@ -474,12 +478,16 @@ extension CommandParser {
completingPrefix
)
case .customAsync(let complete):
#if canImport(Dispatch)
if #available(macOS 10.15, macCatalyst 13, iOS 13, tvOS 13, watchOS 6, *)
{
completions = try asyncCustomCompletions(from: args, complete: complete)
} else {
throw ParserError.invalidState
}
#else
throw ParserError.invalidState
#endif
case .customDeprecated(let complete):
completions = complete(args)
default:
Expand Down Expand Up @@ -528,11 +536,17 @@ private func parseCustomCompletionArguments(
return (Array(args), completingArgumentIndex, completingPrefix)
}

#if !canImport(Dispatch)
@available(*, unavailable, message: "DispatchSemaphore is unavailable")
#endif
@available(macOS 10.15, macCatalyst 13, iOS 13, tvOS 13, watchOS 6, *)
private func asyncCustomCompletions(
from args: [String],
complete: @escaping @Sendable ([String], Int, String) async -> [String]
) throws -> [String] {
#if !canImport(Dispatch)
throw ParserError.invalidState
#else
let (args, completingArgumentIndex, completingPrefix) =
try parseCustomCompletionArguments(from: args)

Expand All @@ -550,6 +564,7 @@ private func asyncCustomCompletions(

semaphore.wait()
return completionsBox.value
#endif
}

// Helper class to make values sendable across concurrency boundaries
Expand Down