From 12d9c5ae35ef002b17a2a200d0bcbc1c81920f25 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 1 Jun 2024 09:41:45 +0900 Subject: [PATCH 1/9] Windows Support in SystemExtras --- Sources/SystemExtras/Clock.swift | 3 ++ Sources/SystemExtras/Constants.swift | 2 ++ Sources/SystemExtras/FileAtOperations.swift | 2 ++ Sources/SystemExtras/Syscalls.swift | 13 ++++--- Sources/SystemExtras/Vendor/Utils.swift | 38 +++++++++------------ 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/Sources/SystemExtras/Clock.swift b/Sources/SystemExtras/Clock.swift index 2ec17edf..3727df7a 100644 --- a/Sources/SystemExtras/Clock.swift +++ b/Sources/SystemExtras/Clock.swift @@ -12,6 +12,8 @@ import ucrt import SystemPackage +#if !os(Windows) + @frozen public struct Clock: RawRepresentable { @@ -110,3 +112,4 @@ extension Clock { } } } +#endif diff --git a/Sources/SystemExtras/Constants.swift b/Sources/SystemExtras/Constants.swift index c44c5d9b..9aade11c 100644 --- a/Sources/SystemExtras/Constants.swift +++ b/Sources/SystemExtras/Constants.swift @@ -12,12 +12,14 @@ import ucrt import SystemPackage +#if !os(Windows) @_alwaysEmitIntoClient internal var _AT_EACCESS: CInt { AT_EACCESS } @_alwaysEmitIntoClient internal var _AT_SYMLINK_NOFOLLOW: CInt { AT_SYMLINK_NOFOLLOW } @_alwaysEmitIntoClient internal var _AT_SYMLINK_FOLLOW: CInt { AT_SYMLINK_FOLLOW } +#endif @_alwaysEmitIntoClient internal var _AT_REMOVEDIR: CInt { AT_REMOVEDIR } #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) diff --git a/Sources/SystemExtras/FileAtOperations.swift b/Sources/SystemExtras/FileAtOperations.swift index d7dbfa3f..ace751be 100644 --- a/Sources/SystemExtras/FileAtOperations.swift +++ b/Sources/SystemExtras/FileAtOperations.swift @@ -26,6 +26,7 @@ extension FileDescriptor { self.rawValue = rawValue } + #if !os(Windows) /// Indicates the operation does't follow symlinks /// /// If you specify this option and the file you pass to @@ -35,6 +36,7 @@ extension FileDescriptor { /// The corresponding C constant is `AT_SYMLINK_NOFOLLOW`. @_alwaysEmitIntoClient public static var noFollow: AtOptions { AtOptions(rawValue: _AT_SYMLINK_NOFOLLOW) } + #endif /* FIXME: Disabled until CSystem will include "linux/fcntl.h" #if os(Linux) diff --git a/Sources/SystemExtras/Syscalls.swift b/Sources/SystemExtras/Syscalls.swift index 87c850a6..3ce7831d 100644 --- a/Sources/SystemExtras/Syscalls.swift +++ b/Sources/SystemExtras/Syscalls.swift @@ -71,11 +71,6 @@ internal func system_unlinkat( return unlinkat(fd, path, flags) } -// futimens -internal func system_futimens(_ fd: Int32, _ times: UnsafePointer) -> CInt { - return futimens(fd, times) -} - // ftruncate internal func system_ftruncate(_ fd: Int32, _ size: off_t) -> CInt { return ftruncate(fd, size) @@ -115,11 +110,18 @@ internal func system_readdir(_ dirp: CInterop.DirP) -> UnsafeMutablePointer) -> CInt { + return futimens(fd, times) +} + // clock_gettime internal func system_clock_gettime(_ id: CInterop.ClockId, _ tp: UnsafeMutablePointer) -> CInt { return clock_gettime(id, tp) @@ -129,3 +131,4 @@ internal func system_clock_gettime(_ id: CInterop.ClockId, _ tp: UnsafeMutablePo internal func system_clock_getres(_ id: CInterop.ClockId, _ tp: UnsafeMutablePointer) -> CInt { return clock_getres(id, tp) } +#endif diff --git a/Sources/SystemExtras/Vendor/Utils.swift b/Sources/SystemExtras/Vendor/Utils.swift index f22d551d..f86e5415 100644 --- a/Sources/SystemExtras/Vendor/Utils.swift +++ b/Sources/SystemExtras/Vendor/Utils.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift System open source project - Copyright (c) 2020 Apple Inc. and the Swift System project authors + Copyright (c) 2020 - 2021 Apple Inc. and the Swift System project authors Licensed under Apache License v2.0 with Runtime Library Exception See https://swift.org/LICENSE.txt for license information @@ -12,38 +12,22 @@ import SystemPackage -#if canImport(Darwin) -import Darwin -#endif - -#if canImport(Glibc) -import Glibc -#endif - -#if canImport(ucrt) -import ucrt -#endif - -#if canImport(WASILibc) -import WASILibc -#endif - // Results in errno if i == -1 -// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +@available(/*System 0.0.1: macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0*/iOS 8, *) private func valueOrErrno( _ i: I ) -> Result { - i == -1 ? .failure(Errno(rawValue: errno)) : .success(i) + i == -1 ? .failure(Errno(rawValue: system_errno)) : .success(i) } -// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +@available(/*System 0.0.1: macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0*/iOS 8, *) private func nothingOrErrno( _ i: I ) -> Result<(), Errno> { valueOrErrno(i).map { _ in () } } -// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +@available(/*System 0.0.1: macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0*/iOS 8, *) internal func valueOrErrno( retryOnInterrupt: Bool, _ f: () -> I ) -> Result { @@ -57,7 +41,7 @@ internal func valueOrErrno( } while true } -// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +@available(/*System 0.0.1: macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0*/iOS 8, *) internal func nothingOrErrno( retryOnInterrupt: Bool, _ f: () -> I ) -> Result<(), Errno> { @@ -148,3 +132,13 @@ extension MutableCollection where Element: Equatable { } } } + +internal func _withOptionalUnsafePointerOrNull( + to value: T?, + _ body: (UnsafePointer?) throws -> R +) rethrows -> R { + guard let value = value else { + return try body(nil) + } + return try withUnsafePointer(to: value, body) +} From 8488805b0a822a6784b54a016a5b8264ee5f9e38 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 1 Jul 2024 17:30:13 +0900 Subject: [PATCH 2/9] Exclude WIT-related targets from Windows build for now Because buildTool plugin seems not working on Windows now with Swift 5.10 --- Package.swift | 76 +++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/Package.swift b/Package.swift index 56e78074..1a95452a 100644 --- a/Package.swift +++ b/Package.swift @@ -23,16 +23,10 @@ let package = Package( name: "WASI", targets: ["WASI"] ), - .library( - name: "WIT", targets: ["WIT"] - ), .executable( name: "wasmkit-cli", targets: ["CLI"] ), - .library(name: "_CabiShims", targets: ["_CabiShims"]), - .plugin(name: "WITOverlayPlugin", targets: ["WITOverlayPlugin"]), - .plugin(name: "WITExtractorPlugin", targets: ["WITExtractorPlugin"]), ], targets: [ .executableTarget( @@ -92,6 +86,45 @@ let package = Package( .product(name: "SystemPackage", package: "swift-system"), ] ), + .testTarget( + name: "WasmKitTests", + dependencies: ["WasmKit"] + ), + .testTarget( + name: "WasmParserTests", + dependencies: ["WasmParser"] + ), + .testTarget( + name: "WASITests", + dependencies: ["WASI"] + ), + ], + swiftLanguageVersions: [.v5] +) + +if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { + package.dependencies += [ + .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.2"), + .package(url: "https://github.com/apple/swift-system", .upToNextMinor(from: "1.2.1")), + .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.3.0"), + .package(url: "https://github.com/apple/swift-format.git", from: "510.1.0"), + ] +} else { + package.dependencies += [ + .package(path: "../swift-argument-parser"), + .package(path: "../swift-system"), + ] +} + +#if !os(Windows) + package.products.append(contentsOf: [ + .library(name: "WIT", targets: ["WIT"]), + .library(name: "_CabiShims", targets: ["_CabiShims"]), + .plugin(name: "WITOverlayPlugin", targets: ["WITOverlayPlugin"]), + .plugin(name: "WITExtractorPlugin", targets: ["WITExtractorPlugin"]), + ]) + + package.targets.append(contentsOf: [ .target(name: "WIT"), .testTarget(name: "WITTests", dependencies: ["WIT"]), .target(name: "WITOverlayGenerator", dependencies: ["WIT"]), @@ -130,32 +163,5 @@ let package = Package( .product(name: "ArgumentParser", package: "swift-argument-parser"), ] ), - .testTarget( - name: "WasmKitTests", - dependencies: ["WasmKit"] - ), - .testTarget( - name: "WasmParserTests", - dependencies: ["WasmParser"] - ), - .testTarget( - name: "WASITests", - dependencies: ["WASI"] - ), - ], - swiftLanguageVersions: [.v5] -) - -if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { - package.dependencies += [ - .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.2"), - .package(url: "https://github.com/apple/swift-system", .upToNextMinor(from: "1.2.1")), - .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.3.0"), - .package(url: "https://github.com/apple/swift-format.git", from: "510.1.0"), - ] -} else { - package.dependencies += [ - .package(path: "../swift-argument-parser"), - .package(path: "../swift-system"), - ] -} + ]) +#endif From 45a723e133898eb3a7799de8dfedb9947766f37f Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 1 Jul 2024 17:47:47 +0900 Subject: [PATCH 3/9] Exclude WASI from Windows build too --- Package.swift | 82 ++++++++++++------- Sources/CLI/Run/Run.swift | 12 +++ Sources/SystemExtras/Constants.swift | 16 ++-- Sources/SystemExtras/Syscalls.swift | 4 + .../WasmKit/Execution/Runtime/Profiler.swift | 3 + 5 files changed, 81 insertions(+), 36 deletions(-) diff --git a/Package.swift b/Package.swift index 1a95452a..8b86fe98 100644 --- a/Package.swift +++ b/Package.swift @@ -15,14 +15,6 @@ let package = Package( name: "WasmParser", targets: ["WasmParser"] ), - .library( - name: "WasmKitWASI", - targets: ["WasmKitWASI"] - ), - .library( - name: "WASI", - targets: ["WASI"] - ), .executable( name: "wasmkit-cli", targets: ["CLI"] @@ -33,7 +25,6 @@ let package = Package( name: "CLI", dependencies: [ "WasmKit", - "WasmKitWASI", .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "SystemPackage", package: "swift-system"), ], @@ -43,16 +34,10 @@ let package = Package( name: "WasmTypes", exclude: ["CMakeLists.txt"] ), - .target( - name: "WASI", - dependencies: ["WasmTypes", "SystemExtras"], - exclude: ["CMakeLists.txt"] - ), .target( name: "WasmKit", dependencies: [ "WasmParser", - "SystemExtras", "WasmTypes", .product(name: "SystemPackage", package: "swift-system"), ], @@ -66,18 +51,6 @@ let package = Package( ], exclude: ["CMakeLists.txt"] ), - .target( - name: "WasmKitWASI", - dependencies: ["WasmKit", "WASI"], - exclude: ["CMakeLists.txt"] - ), - .target( - name: "SystemExtras", - dependencies: [ - .product(name: "SystemPackage", package: "swift-system") - ], - exclude: ["CMakeLists.txt"] - ), .executableTarget( name: "Spectest", dependencies: [ @@ -94,10 +67,6 @@ let package = Package( name: "WasmParserTests", dependencies: ["WasmParser"] ), - .testTarget( - name: "WASITests", - dependencies: ["WASI"] - ), ], swiftLanguageVersions: [.v5] ) @@ -117,6 +86,57 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { } #if !os(Windows) + // Add WASI-related products and targets + package.products.append(contentsOf: [ + .library( + name: "WasmKitWASI", + targets: ["WasmKitWASI"] + ), + .library( + name: "WASI", + targets: ["WASI"] + ), + ]) + package.targets.append(contentsOf: [ + .target( + name: "WASI", + dependencies: ["WasmTypes", "SystemExtras"], + exclude: ["CMakeLists.txt"] + ), + .target( + name: "WasmKitWASI", + dependencies: ["WasmKit", "WASI"], + exclude: ["CMakeLists.txt"] + ), + .target( + name: "SystemExtras", + dependencies: [ + .product(name: "SystemPackage", package: "swift-system") + ], + exclude: ["CMakeLists.txt"] + ), + .testTarget( + name: "WASITests", + dependencies: ["WASI"] + ), + .testTarget( + name: "WASITests", + dependencies: ["WASI"] + ), + ]) + let targetDependenciesToAdd = [ + "CLI": ["WasmKitWASI"], + "WasmKit": ["SystemExtras"], + ] + for (targetName, dependencies) in targetDependenciesToAdd { + if let target = package.targets.first(where: { $0.name == targetName }) { + target.dependencies += dependencies.map { .target(name: $0) } + } else { + fatalError("Target \(targetName) not found!?") + } + } + + // Add WIT-related products and targets package.products.append(contentsOf: [ .library(name: "WIT", targets: ["WIT"]), .library(name: "_CabiShims", targets: ["_CabiShims"]), diff --git a/Sources/CLI/Run/Run.swift b/Sources/CLI/Run/Run.swift index e2be5457..dc0ca6fb 100644 --- a/Sources/CLI/Run/Run.swift +++ b/Sources/CLI/Run/Run.swift @@ -1,6 +1,8 @@ import ArgumentParser import SystemPackage +#if canImport(WasmKitWASI) import WasmKitWASI +#endif import WasmKit struct Run: ParsableCommand { @@ -78,6 +80,7 @@ struct Run: ParsableCommand { } } + #if canImport(SystemExtras) func deriveInterceptor() throws -> (interceptor: GuestTimeProfiler, finalize: () -> Void)? { guard let outputPath = self.profileOutput else { return nil } let fileHandle = try FileDescriptor.open( @@ -97,8 +100,16 @@ struct Run: ParsableCommand { } ) } + #else + func deriveInterceptor() throws -> (interceptor: RuntimeInterceptor, finalize: () -> Void)? { + nil + } + #endif func instantiateWASI(module: Module, interceptor: RuntimeInterceptor?) throws -> () throws -> Void { + #if os(Windows) + fatalError("WASI is not supported on Windows") + #else // Flatten environment variables into a dictionary (Respect the last value if a key is duplicated) let environment = environment.reduce(into: [String: String]()) { $0[$1.key] = $1.value @@ -113,6 +124,7 @@ struct Run: ParsableCommand { let exitCode = try wasi.start(moduleInstance, runtime: runtime) throw ExitCode(Int32(exitCode)) } + #endif } func instantiateNonWASI(module: Module, interceptor: RuntimeInterceptor?) throws -> (() throws -> Void)? { diff --git a/Sources/SystemExtras/Constants.swift b/Sources/SystemExtras/Constants.swift index 9aade11c..6d5f2bfd 100644 --- a/Sources/SystemExtras/Constants.swift +++ b/Sources/SystemExtras/Constants.swift @@ -19,9 +19,9 @@ internal var _AT_EACCESS: CInt { AT_EACCESS } internal var _AT_SYMLINK_NOFOLLOW: CInt { AT_SYMLINK_NOFOLLOW } @_alwaysEmitIntoClient internal var _AT_SYMLINK_FOLLOW: CInt { AT_SYMLINK_FOLLOW } -#endif @_alwaysEmitIntoClient internal var _AT_REMOVEDIR: CInt { AT_REMOVEDIR } +#endif #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) @_alwaysEmitIntoClient internal var _AT_REALDEV: CInt { AT_REALDEV } @@ -37,17 +37,20 @@ internal var _AT_NO_AUTOMOUNT: CInt { AT_NO_AUTOMOUNT } #endif */ +#if !os(Windows) @_alwaysEmitIntoClient internal var _F_GETFL: CInt { F_GETFL } @_alwaysEmitIntoClient internal var _O_DSYNC: CInt { O_DSYNC } @_alwaysEmitIntoClient internal var _O_SYNC: CInt { O_SYNC } +#endif #if os(Linux) @_alwaysEmitIntoClient internal var _O_RSYNC: CInt { O_RSYNC } #endif +#if !os(Windows) @_alwaysEmitIntoClient internal var _UTIME_NOW: CInt { #if os(Linux) @@ -89,23 +92,26 @@ internal var _DT_LNK: CInt { CInt(DT_LNK) } internal var _DT_SOCK: CInt { CInt(DT_SOCK) } @_alwaysEmitIntoClient internal var _DT_WHT: CInt { CInt(DT_WHT) } +#endif @_alwaysEmitIntoClient internal var _S_IFMT: CInterop.Mode { S_IFMT } @_alwaysEmitIntoClient -internal var _S_IFIFO: CInterop.Mode { S_IFIFO } -@_alwaysEmitIntoClient internal var _S_IFCHR: CInterop.Mode { S_IFCHR } @_alwaysEmitIntoClient internal var _S_IFDIR: CInterop.Mode { S_IFDIR } @_alwaysEmitIntoClient -internal var _S_IFBLK: CInterop.Mode { S_IFBLK } -@_alwaysEmitIntoClient internal var _S_IFREG: CInterop.Mode { S_IFREG } +#if !os(Windows) +@_alwaysEmitIntoClient +internal var _S_IFIFO: CInterop.Mode { S_IFIFO } +@_alwaysEmitIntoClient +internal var _S_IFBLK: CInterop.Mode { S_IFBLK } @_alwaysEmitIntoClient internal var _S_IFLNK: CInterop.Mode { S_IFLNK } @_alwaysEmitIntoClient internal var _S_IFSOCK: CInterop.Mode { S_IFSOCK } +#endif #if os(Linux) @_alwaysEmitIntoClient diff --git a/Sources/SystemExtras/Syscalls.swift b/Sources/SystemExtras/Syscalls.swift index 3ce7831d..e902651f 100644 --- a/Sources/SystemExtras/Syscalls.swift +++ b/Sources/SystemExtras/Syscalls.swift @@ -90,6 +90,9 @@ internal func system_symlinkat( return symlinkat(oldPath, newDirFd, newPath) } +// ucrt does not provide `opendir` API +#if !os(Windows) + extension CInterop { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) public typealias DirP = UnsafeMutablePointer @@ -109,6 +112,7 @@ internal func system_fdopendir(_ fd: Int32) -> CInterop.DirP? { internal func system_readdir(_ dirp: CInterop.DirP) -> UnsafeMutablePointer? { return readdir(dirp) } +#endif #if !os(Windows) diff --git a/Sources/WasmKit/Execution/Runtime/Profiler.swift b/Sources/WasmKit/Execution/Runtime/Profiler.swift index ae4553d7..b0dc2af4 100644 --- a/Sources/WasmKit/Execution/Runtime/Profiler.swift +++ b/Sources/WasmKit/Execution/Runtime/Profiler.swift @@ -1,3 +1,4 @@ +#if canImport(SystemExtras) import SystemExtras import SystemPackage @@ -116,3 +117,5 @@ private enum JSON { return output } } + +#endif From 3da34d659d9a06f3cdfb89d88af749339ccfe536 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 1 Jul 2024 21:23:55 +0900 Subject: [PATCH 4/9] Port Spectest for Windows --- Sources/Spectest/Spectest.swift | 2 +- Sources/Spectest/TestCase.swift | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Sources/Spectest/Spectest.swift b/Sources/Spectest/Spectest.swift index 2fed3c8f..888155dd 100644 --- a/Sources/Spectest/Spectest.swift +++ b/Sources/Spectest/Spectest.swift @@ -41,7 +41,7 @@ struct Spectest: AsyncParsableCommand { let rootPath: String let filePath = FilePath(path) - if (try? FileDescriptor.open(filePath, FileDescriptor.AccessMode.readOnly, options: .directory)) != nil { + if isDirectory(filePath) { rootPath = path } else { rootPath = URL(fileURLWithPath: path).deletingLastPathComponent().path diff --git a/Sources/Spectest/TestCase.swift b/Sources/Spectest/TestCase.swift index 5e80e617..333cfb23 100644 --- a/Sources/Spectest/TestCase.swift +++ b/Sources/Spectest/TestCase.swift @@ -79,13 +79,6 @@ struct TestCase { let content: Content let path: String - private static func isDirectory(_ path: FilePath) -> Bool { - let fd = try? FileDescriptor.open(path, FileDescriptor.AccessMode.readOnly, options: .directory) - let isDirectory = fd != nil - try? fd?.close() - return isDirectory - } - static func load(include: [String], exclude: [String], in path: String, log: ((String) -> Void)? = nil) throws -> [TestCase] { let fileManager = FileManager.default let filePath = FilePath(path) @@ -563,3 +556,20 @@ extension Swift.Error { return "unknown error: \(self)" } } + +#if os(Windows) +import WinSDK +#endif +internal func isDirectory(_ path: FilePath) -> Bool { + #if os(Windows) + return path.withPlatformString { + let result = GetFileAttributesW($0) + return result != INVALID_FILE_ATTRIBUTES && result & DWORD(FILE_ATTRIBUTE_DIRECTORY) != 0 + } + #else + let fd = try? FileDescriptor.open(path, FileDescriptor.AccessMode.readOnly, options: .directory) + let isDirectory = fd != nil + try? fd?.close() + return isDirectory + #endif +} From bfa006b23fea65b58d63aee0bbf8fbe5bcd7ffc1 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 1 Jul 2024 23:23:40 +0900 Subject: [PATCH 5/9] Fix feature detection for spectest for Windows path --- Sources/Spectest/TestCase.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Sources/Spectest/TestCase.swift b/Sources/Spectest/TestCase.swift index 333cfb23..37439de6 100644 --- a/Sources/Spectest/TestCase.swift +++ b/Sources/Spectest/TestCase.swift @@ -412,9 +412,9 @@ extension TestCase.Command { } } - private func deriveFeatureSet(rootPath: String) -> WasmFeatureSet { + private func deriveFeatureSet(rootPath: FilePath) -> WasmFeatureSet { var features = WasmFeatureSet.default - if rootPath.hasSuffix("/proposals/memory64") { + if rootPath.ends(with: "proposals/memory64") { features.insert(.memory64) // memory64 doesn't expect reference-types proposal // and it depends on the fact reference-types is disabled @@ -424,9 +424,10 @@ extension TestCase.Command { } private func parseModule(rootPath: String, filename: String) throws -> Module { - let url = URL(fileURLWithPath: rootPath).appendingPathComponent(filename) + let rootPath = FilePath(rootPath) + let path = rootPath.appending(filename) - let module = try parseWasm(filePath: FilePath(url.path), features: deriveFeatureSet(rootPath: rootPath)) + let module = try parseWasm(filePath: path, features: deriveFeatureSet(rootPath: rootPath)) return module } From eb27bde141dd699ece0d3f871c558dbecccc4dc3 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 1 Jul 2024 23:24:40 +0900 Subject: [PATCH 6/9] Fix module file reading on Windows `open` on Windows uses "text-mode", so we need to explicitly specify O_BINARY. --- Sources/WasmKit/ModuleParser.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sources/WasmKit/ModuleParser.swift b/Sources/WasmKit/ModuleParser.swift index 92a819d4..26834de7 100644 --- a/Sources/WasmKit/ModuleParser.swift +++ b/Sources/WasmKit/ModuleParser.swift @@ -4,7 +4,15 @@ import WasmParser /// Parse a given file as a WebAssembly binary format file /// > Note: public func parseWasm(filePath: FilePath, features: WasmFeatureSet = .default) throws -> Module { - let fileHandle = try FileDescriptor.open(filePath, .readOnly) + #if os(Windows) + // TODO: Upstream `O_BINARY` to `SystemPackage + let accessMode = FileDescriptor.AccessMode( + rawValue: FileDescriptor.AccessMode.readOnly.rawValue | O_BINARY + ) + #else + let accessMode: FileDescriptor.AccessMode = .readOnly + #endif + let fileHandle = try FileDescriptor.open(filePath, accessMode) defer { try? fileHandle.close() } let stream = try FileHandleStream(fileHandle: fileHandle) let module = try parseModule(stream: stream, features: features) From d7bf655419a2d7c7e6ecc58505ecce8a0a3bef15 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 1 Jul 2024 23:43:06 +0900 Subject: [PATCH 7/9] Include as many target/product as possible --- Package.swift | 48 ++++++++++++++---------------- Sources/CLI/Run/Run.swift | 7 +++-- Sources/WasmKit/ModuleParser.swift | 3 ++ 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/Package.swift b/Package.swift index 8b86fe98..3907e35e 100644 --- a/Package.swift +++ b/Package.swift @@ -15,10 +15,14 @@ let package = Package( name: "WasmParser", targets: ["WasmParser"] ), + .library( + name: "WIT", targets: ["WIT"] + ), .executable( name: "wasmkit-cli", targets: ["CLI"] ), + .library(name: "_CabiShims", targets: ["_CabiShims"]), ], targets: [ .executableTarget( @@ -59,6 +63,24 @@ let package = Package( .product(name: "SystemPackage", package: "swift-system"), ] ), + .target(name: "WIT"), + .testTarget(name: "WITTests", dependencies: ["WIT"]), + .target(name: "WITOverlayGenerator", dependencies: ["WIT"]), + .target(name: "_CabiShims"), + .target(name: "WITExtractor"), + .testTarget( + name: "WITExtractorTests", + dependencies: ["WITExtractor", "WIT"] + ), + .executableTarget( + name: "WITTool", + dependencies: [ + "WIT", + "WITOverlayGenerator", + "WITExtractor", + .product(name: "ArgumentParser", package: "swift-argument-parser"), + ] + ), .testTarget( name: "WasmKitTests", dependencies: ["WasmKit"] @@ -119,10 +141,6 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { name: "WASITests", dependencies: ["WASI"] ), - .testTarget( - name: "WASITests", - dependencies: ["WASI"] - ), ]) let targetDependenciesToAdd = [ "CLI": ["WasmKitWASI"], @@ -136,19 +154,13 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { } } - // Add WIT-related products and targets + // Add build tool plugins only for non-Windows platforms package.products.append(contentsOf: [ - .library(name: "WIT", targets: ["WIT"]), - .library(name: "_CabiShims", targets: ["_CabiShims"]), .plugin(name: "WITOverlayPlugin", targets: ["WITOverlayPlugin"]), .plugin(name: "WITExtractorPlugin", targets: ["WITExtractorPlugin"]), ]) package.targets.append(contentsOf: [ - .target(name: "WIT"), - .testTarget(name: "WITTests", dependencies: ["WIT"]), - .target(name: "WITOverlayGenerator", dependencies: ["WIT"]), - .target(name: "_CabiShims"), .plugin(name: "WITOverlayPlugin", capability: .buildTool(), dependencies: ["WITTool"]), .plugin(name: "GenerateOverlayForTesting", capability: .buildTool(), dependencies: ["WITTool"]), .testTarget( @@ -157,11 +169,6 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { exclude: ["Fixtures", "Compiled", "Generated"], plugins: [.plugin(name: "GenerateOverlayForTesting")] ), - .target(name: "WITExtractor"), - .testTarget( - name: "WITExtractorTests", - dependencies: ["WITExtractor", "WIT"] - ), .plugin( name: "WITExtractorPlugin", capability: .command( @@ -174,14 +181,5 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { name: "WITExtractorPluginTests", exclude: ["Fixtures"] ), - .executableTarget( - name: "WITTool", - dependencies: [ - "WIT", - "WITOverlayGenerator", - "WITExtractor", - .product(name: "ArgumentParser", package: "swift-argument-parser"), - ] - ), ]) #endif diff --git a/Sources/CLI/Run/Run.swift b/Sources/CLI/Run/Run.swift index dc0ca6fb..54bbeddb 100644 --- a/Sources/CLI/Run/Run.swift +++ b/Sources/CLI/Run/Run.swift @@ -101,15 +101,14 @@ struct Run: ParsableCommand { ) } #else + // GuestTimeProfiler is not available without SystemExtras func deriveInterceptor() throws -> (interceptor: RuntimeInterceptor, finalize: () -> Void)? { nil } #endif func instantiateWASI(module: Module, interceptor: RuntimeInterceptor?) throws -> () throws -> Void { - #if os(Windows) - fatalError("WASI is not supported on Windows") - #else + #if canImport(WasmKitWASI) // Flatten environment variables into a dictionary (Respect the last value if a key is duplicated) let environment = environment.reduce(into: [String: String]()) { $0[$1.key] = $1.value @@ -124,6 +123,8 @@ struct Run: ParsableCommand { let exitCode = try wasi.start(moduleInstance, runtime: runtime) throw ExitCode(Int32(exitCode)) } + #else + fatalError("WASI is not supported on this platform") #endif } diff --git a/Sources/WasmKit/ModuleParser.swift b/Sources/WasmKit/ModuleParser.swift index 26834de7..d6119883 100644 --- a/Sources/WasmKit/ModuleParser.swift +++ b/Sources/WasmKit/ModuleParser.swift @@ -1,5 +1,8 @@ import SystemPackage import WasmParser +#if os(Windows) +import ucrt +#endif /// Parse a given file as a WebAssembly binary format file /// > Note: From f2f70f1a812353ca9c0f3554f1eca763fed0564f Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 1 Jul 2024 23:59:54 +0900 Subject: [PATCH 8/9] Add Windows build job --- .github/workflows/main.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7d657fc6..82866d95 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,8 +56,6 @@ jobs: steps: - uses: actions/checkout@v3 - with: - submodules: recursive - id: setup-swiftwasm uses: swiftwasm/setup-swiftwasm@v1 with: @@ -75,14 +73,22 @@ jobs: - run: ./CI/check-spectest.sh - run: ./CI/check-wasi-testsuite.sh + build-windows: + runs-on: windows-latest + steps: + - uses: compnerd/gha-setup-swift@main + with: + branch: swift-5.10.1-release + tag: 5.10.1-RELEASE + - uses: actions/checkout@v4 + - run: swift test + build-cmake: runs-on: ubuntu-20.04 container: image: swift:5.8-focal steps: - uses: actions/checkout@v4 - with: - submodules: recursive - name: Install Ninja run: apt-get update && apt-get install -y ninja-build - name: Install CMake From f569b0bd67da24f9b272c8dd63ef0e0705068373 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jul 2024 00:08:40 +0900 Subject: [PATCH 9/9] Fix `withTemporaryDirectory` compilation on Windows --- Tests/WITExtractorTests/TestSupport.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/WITExtractorTests/TestSupport.swift b/Tests/WITExtractorTests/TestSupport.swift index e4766929..a4ebd66d 100644 --- a/Tests/WITExtractorTests/TestSupport.swift +++ b/Tests/WITExtractorTests/TestSupport.swift @@ -47,9 +47,18 @@ struct TestSupport { let templatePath = tempdir.appendingPathComponent("WasmKit.XXXXXX") var template = [UInt8](templatePath.path.utf8).map({ Int8($0) }) + [Int8(0)] + #if os(Windows) + if _mktemp_s(&template, template.count) != 0 { + throw Error(errno: errno) + } + if _mkdir(template) != 0 { + throw Error(errno: errno) + } + #else if mkdtemp(&template) == nil { throw Error(errno: errno) } + #endif let path = String(cString: template) defer { _ = try? FileManager.default.removeItem(atPath: path) }