diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5970a6d4..ba2d1920 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -305,7 +305,7 @@ jobs: curl -L https://github.com/Kitware/CMake/releases/download/v3.29.2/cmake-3.29.2-linux-x86_64.tar.gz | tar xz --strip-component 1 -C /usr/local/ - run: cmake -G Ninja -B ./build - run: cmake --build ./build - - run: ./build/bin/wasmkit-cli --version + - run: ./build/bin/wasmkit --version build-wasi: runs-on: ubuntu-24.04 diff --git a/CMakeLists.txt b/CMakeLists.txt index e6289e5e..048419d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,16 +52,6 @@ add_compile_definitions( include(FetchContent) -find_package(SwiftSystem CONFIG) -if(NOT SwiftSystem_FOUND) - message("-- Vending SwiftSystem") - FetchContent_Declare(SwiftSystem - GIT_REPOSITORY https://github.com/apple/swift-system - GIT_TAG 1.5.0 - ) - FetchContent_MakeAvailable(SwiftSystem) -endif() - option(WASMKIT_BUILD_CLI "Build wasmkit-cli" ON) if(WASMKIT_BUILD_CLI) @@ -77,6 +67,17 @@ if(WASMKIT_BUILD_CLI) endif() endif() +find_package(SwiftSystem CONFIG) +if(NOT SwiftSystem_FOUND) + message("-- Vending SwiftSystem") + FetchContent_Declare(SwiftSystem + GIT_REPOSITORY https://github.com/apple/swift-system + GIT_TAG 1.5.0 + ) + FetchContent_MakeAvailable(SwiftSystem) +endif() + + add_subdirectory(Sources) add_subdirectory(Tests) add_subdirectory(cmake/modules) diff --git a/Package.swift b/Package.swift index aa82983a..09b47cb4 100644 --- a/Package.swift +++ b/Package.swift @@ -22,6 +22,14 @@ let package = Package( targets: [ .executableTarget( name: "CLI", + dependencies: [ + "CLICommands" + ], + exclude: ["CMakeLists.txt"] + ), + + .target( + name: "CLICommands", dependencies: [ "WAT", "WasmKit", diff --git a/Package@swift-6.1.swift b/Package@swift-6.1.swift index 22925888..9f9e556b 100644 --- a/Package@swift-6.1.swift +++ b/Package@swift-6.1.swift @@ -6,8 +6,8 @@ import class Foundation.ProcessInfo let DarwinPlatforms: [Platform] = [.macOS, .iOS, .watchOS, .tvOS, .visionOS] -let cliTarget = Target.executableTarget( - name: "CLI", +let cliCommandsTarget = Target.target( + name: "CLICommands", dependencies: [ "WAT", "WasmKit", @@ -36,7 +36,12 @@ let package = Package( "WasmDebuggingSupport", ], targets: [ - cliTarget, + cliCommandsTarget, + .executableTarget( + name: "CLI", + dependencies: ["CLICommands"], + exclude: ["CMakeLists.txt"] + ), .target( name: "WasmKit", dependencies: [ @@ -193,7 +198,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { ), ]) - cliTarget.dependencies.append(contentsOf: [ + cliCommandsTarget.dependencies.append(contentsOf: [ .product(name: "Logging", package: "swift-log", condition: .when(traits: ["WasmDebuggingSupport"])), .product(name: "NIOCore", package: "swift-nio", condition: .when(traits: ["WasmDebuggingSupport"])), .product(name: "NIOPosix", package: "swift-nio", condition: .when(traits: ["WasmDebuggingSupport"])), diff --git a/Sources/CLI/CLI.swift b/Sources/CLI/CLI.swift index 14d53166..bf410ea6 100644 --- a/Sources/CLI/CLI.swift +++ b/Sources/CLI/CLI.swift @@ -1,4 +1,5 @@ import ArgumentParser +import CLICommands @main struct CLI: AsyncParsableCommand { diff --git a/Sources/CLI/CMakeLists.txt b/Sources/CLI/CMakeLists.txt index bba5cda1..5b473505 100644 --- a/Sources/CLI/CMakeLists.txt +++ b/Sources/CLI/CMakeLists.txt @@ -1,12 +1,13 @@ -add_executable(wasmkit-cli - Commands/Explore.swift - Commands/Run.swift - Commands/Wat2wasm.swift +add_executable(wasmkit CLI.swift ) -target_link_wasmkit_libraries(wasmkit-cli PUBLIC - ArgumentParser WAT WasmKitWASI) +target_compile_options(wasmkit PRIVATE + -package-name WasmKitPackage +) + +target_link_wasmkit_libraries(wasmkit PUBLIC + CLICommands) -install(TARGETS wasmkit-cli +install(TARGETS wasmkit RUNTIME DESTINATION bin) diff --git a/Sources/CLI/Commands/Parse.swift b/Sources/CLI/Commands/Parse.swift deleted file mode 100644 index 121edc72..00000000 --- a/Sources/CLI/Commands/Parse.swift +++ /dev/null @@ -1,20 +0,0 @@ -import SystemPackage -import WAT -import WasmKit - -/// Parses a `.wasm` or `.wat` module. -func parseWasm(filePath: FilePath) throws -> Module { - if filePath.extension == "wat", #available(macOS 11.0, iOS 14.0, macCatalyst 14.0, tvOS 14.0, visionOS 1.0, watchOS 7.0, *) { - let fileHandle = try FileDescriptor.open(filePath, .readOnly) - defer { try? fileHandle.close() } - - let size = try fileHandle.seek(offset: 0, from: .end) - - let wat = try String(unsafeUninitializedCapacity: Int(size)) { - try fileHandle.read(fromAbsoluteOffset: 0, into: .init($0)) - } - return try WasmKit.parseWasm(bytes: wat2wasm(wat)) - } else { - return try WasmKit.parseWasm(filePath: filePath) - } -} diff --git a/Sources/CLICommands/CMakeLists.txt b/Sources/CLICommands/CMakeLists.txt new file mode 100644 index 00000000..910c4120 --- /dev/null +++ b/Sources/CLICommands/CMakeLists.txt @@ -0,0 +1,20 @@ +add_wasmkit_library(CLICommands + Explore.swift + Run.swift + Wat2wasm.swift +) + +target_link_wasmkit_libraries(CLICommands PUBLIC + WAT WasmKitWASI) + +add_dependencies( + CLICommands + + ArgumentParser +) + +target_link_libraries(CLICommands + + PUBLIC + ArgumentParser +) diff --git a/Sources/CLI/DebuggerServer.swift b/Sources/CLICommands/DebuggerServer.swift similarity index 100% rename from Sources/CLI/DebuggerServer.swift rename to Sources/CLICommands/DebuggerServer.swift diff --git a/Sources/CLI/Commands/Explore.swift b/Sources/CLICommands/Explore.swift similarity index 91% rename from Sources/CLI/Commands/Explore.swift rename to Sources/CLICommands/Explore.swift index 9af9d949..df424c94 100644 --- a/Sources/CLI/Commands/Explore.swift +++ b/Sources/CLICommands/Explore.swift @@ -2,9 +2,9 @@ import ArgumentParser import SystemPackage @_spi(OnlyForCLI) import WasmKit -struct Explore: ParsableCommand { +package struct Explore: ParsableCommand { - static let configuration = CommandConfiguration( + package static let configuration = CommandConfiguration( abstract: "Explore the compiled functions of a WebAssembly module", discussion: """ This command will parse a WebAssembly module and dump the compiled functions. @@ -22,7 +22,9 @@ struct Explore: ParsableCommand { } } - func run() throws { + package init() {} + + package func run() throws { let module = try parseWasm(filePath: FilePath(path)) // Instruction dumping requires token threading model for now let configuration = EngineConfiguration(threadingModel: .token) diff --git a/Sources/CLI/Commands/Run.swift b/Sources/CLICommands/Run.swift similarity index 90% rename from Sources/CLI/Commands/Run.swift rename to Sources/CLICommands/Run.swift index 32769a72..4c1ce43c 100644 --- a/Sources/CLI/Commands/Run.swift +++ b/Sources/CLICommands/Run.swift @@ -1,5 +1,6 @@ import ArgumentParser import SystemPackage +import WAT import WasmKit import WasmKitWASI @@ -7,8 +8,8 @@ import WasmKitWASI import os.signpost #endif -struct Run: AsyncParsableCommand { - static let configuration = CommandConfiguration( +package struct Run: AsyncParsableCommand { + package static let configuration = CommandConfiguration( abstract: "Run a WebAssembly module", discussion: """ This command will parse a WebAssembly module and run it. @@ -128,7 +129,9 @@ struct Run: AsyncParsableCommand { ) var arguments: [String] = [] - func run() async throws { + package init() {} + + package func run() async throws { #if WasmDebuggingSupport if let debuggerPort { @@ -257,23 +260,7 @@ struct Run: AsyncParsableCommand { } func instantiateNonWASI(module: Module, interceptor: EngineInterceptor?) throws -> (() throws -> Void)? { - let functionName = arguments.first - let arguments = arguments.dropFirst() - - var parameters: [Value] = [] - for argument in arguments { - let parameter: Value - let type = argument.prefix { $0 != ":" } - let value = argument.drop { $0 != ":" }.dropFirst() - switch type { - case "i32": parameter = Value(signed: Int32(value)!) - case "i64": parameter = Value(signed: Int64(value)!) - case "f32": parameter = .f32(Float32(value)!.bitPattern) - case "f64": parameter = .f64(Float64(value)!.bitPattern) - default: fatalError("unknown type") - } - parameters.append(parameter) - } + let (functionName, parameters) = Run.parseInvocation(arguments: self.arguments) guard let functionName else { log("Error: No function specified to run in a given module.") return nil @@ -311,3 +298,44 @@ struct Run: AsyncParsableCommand { } } } + +/// Parses a `.wasm` or `.wat` module. +func parseWasm(filePath: FilePath) throws -> Module { + if filePath.extension == "wat", #available(macOS 11.0, iOS 14.0, macCatalyst 14.0, tvOS 14.0, visionOS 1.0, watchOS 7.0, *) { + let fileHandle = try FileDescriptor.open(filePath, .readOnly) + defer { try? fileHandle.close() } + + let size = try fileHandle.seek(offset: 0, from: .end) + + let wat = try String(unsafeUninitializedCapacity: Int(size)) { + try fileHandle.read(fromAbsoluteOffset: 0, into: .init($0)) + } + return try WasmKit.parseWasm(bytes: wat2wasm(wat)) + } else { + return try WasmKit.parseWasm(filePath: filePath) + } +} + +extension Run { + package static func parseInvocation(arguments: [String]) -> (functionName: String?, parameters: [Value]) { + let functionName = arguments.first + let arguments = arguments.dropFirst() + + var parameters: [Value] = [] + for argument in arguments { + let parameter: Value + let type = argument.prefix { $0 != ":" } + let value = argument.drop { $0 != ":" }.dropFirst() + switch type { + case "i32": parameter = Value(signed: Int32(value)!) + case "i64": parameter = Value(signed: Int64(value)!) + case "f32": parameter = .f32(Float32(value)!.bitPattern) + case "f64": parameter = .f64(Float64(value)!.bitPattern) + default: fatalError("unknown type") + } + parameters.append(parameter) + } + + return (functionName, parameters) + } +} diff --git a/Sources/CLI/Commands/Wat2wasm.swift b/Sources/CLICommands/Wat2wasm.swift similarity index 95% rename from Sources/CLI/Commands/Wat2wasm.swift rename to Sources/CLICommands/Wat2wasm.swift index aa47735f..7f3f61e6 100644 --- a/Sources/CLI/Commands/Wat2wasm.swift +++ b/Sources/CLICommands/Wat2wasm.swift @@ -3,8 +3,8 @@ import SystemPackage import WAT import WasmKit -struct Wat2wasm: ParsableCommand { - static let configuration = CommandConfiguration( +package struct Wat2wasm: ParsableCommand { + package static let configuration = CommandConfiguration( abstract: "Assemble WebAssembly text into a WebAssembly binary", discussion: """ Parse a file in WebAssembly Text Format (`.wat`), \ @@ -58,7 +58,9 @@ struct Wat2wasm: ParsableCommand { ) var output: String? - func run() throws { + package init() {} + + package func run() throws { let filePath = FilePath(path) guard filePath.extension == "wat" else { throw Error.unknownFileExtension(filePath.extension) } let fileHandle = try FileDescriptor.open(filePath, .readOnly) diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index 1f773267..5e28ac12 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -9,4 +9,5 @@ add_subdirectory(WAT) if(WASMKIT_BUILD_CLI) add_subdirectory(CLI) + add_subdirectory(CLICommands) endif()