Skip to content

Port a part of SystemExtras and WASI to Windows #104

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

Merged
merged 5 commits into from
Jul 21, 2024
Merged
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
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ jobs:
- uses: actions/checkout@v4
- run: python3 ./Vendor/checkout-dependency testsuite
- run: swift test
- run: ./CI/check-wasi-testsuite.ps1

build-cmake:
runs-on: ubuntu-20.04
Expand Down
6 changes: 6 additions & 0 deletions CI/check-wasi-testsuite.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
python.exe .\Vendor\checkout-dependency wasi-testsuite
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
python.exe -m pip install -r .\Vendor\wasi-testsuite\test-runner\requirements.txt
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
python.exe .\IntegrationTests\WASI\run-tests.py
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
2 changes: 1 addition & 1 deletion CI/check-wasi-testsuite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ SOURCE_DIR="$(cd $(dirname $0)/.. && pwd)"
cd $SOURCE_DIR && \
./Vendor/checkout-dependency wasi-testsuite && \
python3 -m pip install -r ./Vendor/wasi-testsuite/test-runner/requirements.txt && \
exec ./IntegrationTests/WASI/run-tests.sh
exec ./IntegrationTests/WASI/run-tests.py
)
5 changes: 4 additions & 1 deletion IntegrationTests/WASI/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
import shlex

# shlex.split() splits according to shell quoting rules
WASMKIT_CLI = shlex.split(os.getenv("TEST_RUNTIME_EXE", "wasmkit-cli"))
if sys.platform == "win32":
WASMKIT_CLI = [os.getenv("TEST_RUNTIME_EXE", "wasmkit-cli.exe")]
else:
WASMKIT_CLI = shlex.split(os.getenv("TEST_RUNTIME_EXE", "wasmkit-cli"))

parser = argparse.ArgumentParser()
parser.add_argument("--version", action="store_true")
Expand Down
37 changes: 37 additions & 0 deletions IntegrationTests/WASI/run-tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env python3

import os
import sys
import subprocess

def main():
source_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
build_dir = os.path.join(source_dir, ".build", "debug")
if sys.platform == "win32":
default_runtime_exe = "wasmkit-cli.exe"
skip_json_filename = "skip.windows.json"
else:
default_runtime_exe = "wasmkit-cli"
skip_json_filename = "skip.json"
default_runtime_exe = os.path.join(build_dir, default_runtime_exe)

if os.getenv("TEST_RUNTIME_EXE") is None and not os.path.exists(default_runtime_exe):
print("Building wasmkit-cli")
subprocess.run(["swift", "build", "--product", "wasmkit-cli"], check=True)

env = os.environ.copy()
env["TEST_RUNTIME_EXE"] = os.getenv("TEST_RUNTIME_EXE", default_runtime_exe)


subprocess.run(
[sys.executable, "./Vendor/wasi-testsuite/test-runner/wasi_test_runner.py",
"--test-suite", "./Vendor/wasi-testsuite/tests/assemblyscript/testsuite/",
"./Vendor/wasi-testsuite/tests/c/testsuite/",
"./Vendor/wasi-testsuite/tests/rust/testsuite/",
"--runtime-adapter", "IntegrationTests/WASI/adapter.py",
"--exclude-filter", os.path.join(source_dir, "IntegrationTests", "WASI", skip_json_filename),
*sys.argv[1:]],
env=env, check=True)

if __name__ == "__main__":
main()
15 changes: 0 additions & 15 deletions IntegrationTests/WASI/run-tests.sh

This file was deleted.

23 changes: 23 additions & 0 deletions IntegrationTests/WASI/skip.windows.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"WASI Assemblyscript tests": {
},
"WASI C tests": {
"fdopendir-with-access": "Not implemented",
"fopen-with-access": "Not implemented",
"lseek": "Not implemented",
"pread-with-access": "Not implemented",
"pwrite-with-access": "Not implemented",
"stat-dev-ino": "Not implemented"
},
"WASI Rust tests": {
"close_preopen": "Not implemented",
"dangling_fd": "Not implemented",
"dangling_symlink": "Not implemented",
"directory_seek": "Not implemented",
"fd_advise": "Not implemented",
"fd_filestat_set": "Not implemented",
"fd_flags_set": "Not implemented",
"fd_readdir": "Not implemented",
"interesting_paths": "Not implemented"
}
}
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ spectest:

.PHONY: wasitest
wasitest:
./IntegrationTests/WASI/run-tests.sh
./IntegrationTests/WASI/run-tests.py

### Utilities

Expand Down
77 changes: 31 additions & 46 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ let package = Package(
name: "WasmKit",
targets: ["WasmKit"]
),
.library(
name: "WasmKitWASI",
targets: ["WasmKitWASI"]
),
.library(
name: "WASI",
targets: ["WASI"]
),
.library(
name: "WasmParser",
targets: ["WasmParser"]
Expand All @@ -33,6 +41,7 @@ let package = Package(
name: "CLI",
dependencies: [
"WasmKit",
"WasmKitWASI",
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "SystemPackage", package: "swift-system"),
],
Expand All @@ -47,6 +56,7 @@ let package = Package(
dependencies: [
"WasmParser",
"WasmTypes",
"SystemExtras",
.product(name: "SystemPackage", package: "swift-system"),
],
exclude: ["CMakeLists.txt"]
Expand All @@ -60,6 +70,27 @@ let package = Package(
],
exclude: ["CMakeLists.txt"]
),
.target(
name: "WASI",
dependencies: ["WasmTypes", "SystemExtras"],
exclude: ["CMakeLists.txt"]
),
.target(
name: "WasmKitWASI",
dependencies: ["WasmKit", "WASI"],
exclude: ["CMakeLists.txt"]
),
.testTarget(
name: "WASITests",
dependencies: ["WASI"]
),
.target(
name: "SystemExtras",
dependencies: [
.product(name: "SystemPackage", package: "swift-system")
],
exclude: ["CMakeLists.txt"]
),
.executableTarget(
name: "Spectest",
dependencies: [
Expand Down Expand Up @@ -115,52 +146,6 @@ 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"]
),
])
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 build tool plugins only for non-Windows platforms
package.products.append(contentsOf: [
.plugin(name: "WITOverlayPlugin", targets: ["WITOverlayPlugin"]),
Expand Down
13 changes: 0 additions & 13 deletions Sources/CLI/Run/Run.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import ArgumentParser
import SystemPackage
#if canImport(WasmKitWASI)
import WasmKitWASI
#endif
import WasmKit

struct Run: ParsableCommand {
Expand Down Expand Up @@ -80,7 +78,6 @@ 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(
Expand All @@ -100,15 +97,8 @@ 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 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
Expand All @@ -123,9 +113,6 @@ 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
}

func instantiateNonWASI(module: Module, interceptor: RuntimeInterceptor?) throws -> (() throws -> Void)? {
Expand Down
23 changes: 23 additions & 0 deletions Sources/SystemExtras/FileAtOperations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Darwin
import Glibc
#elseif os(Windows)
import ucrt
import WinSDK
#else
#error("Unsupported Platform")
#endif
Expand Down Expand Up @@ -52,6 +53,7 @@ extension FileDescriptor {
#endif
*/

#if !os(Windows)
/// Indicates the operation removes directory
///
/// If you specify this option and the file path you pass to
Expand All @@ -61,6 +63,7 @@ extension FileDescriptor {
/// The corresponding C constant is `AT_REMOVEDIR`.
@_alwaysEmitIntoClient
public static var removeDirectory: AtOptions { AtOptions(rawValue: _AT_REMOVEDIR) }
#endif
}

/// Opens or creates a file relative to a directory file descriptor
Expand Down Expand Up @@ -126,6 +129,9 @@ extension FileDescriptor {
permissions: FilePermissions?,
retryOnInterrupt: Bool
) -> Result<FileDescriptor, Errno> {
#if os(Windows)
return .failure(Errno(rawValue: ERROR_NOT_SUPPORTED))
#else
let oFlag = mode.rawValue | options.rawValue
let descOrError: Result<CInt, Errno> = valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
if let permissions = permissions {
Expand All @@ -136,6 +142,7 @@ extension FileDescriptor {
return system_openat(self.rawValue, path, oFlag)
}
return descOrError.map { FileDescriptor(rawValue: $0) }
#endif
}

/// Returns attributes information about a file relative to a directory file descriptor
Expand Down Expand Up @@ -168,11 +175,15 @@ extension FileDescriptor {

@usableFromInline
internal func _attributes(at path: UnsafePointer<CInterop.PlatformChar>, options: AtOptions) -> Result<Attributes, Errno> {
#if os(Windows)
return .failure(Errno(rawValue: ERROR_NOT_SUPPORTED))
#else
var stat: stat = stat()
return nothingOrErrno(retryOnInterrupt: false) {
system_fstatat(self.rawValue, path, &stat, options.rawValue)
}
.map { Attributes(rawValue: stat) }
#endif
}

/// Remove a file entry relative to a directory file descriptor
Expand Down Expand Up @@ -205,9 +216,13 @@ extension FileDescriptor {
internal func _remove(
at path: UnsafePointer<CInterop.PlatformChar>, options: AtOptions
) -> Result<(), Errno> {
#if os(Windows)
return .failure(Errno(rawValue: ERROR_NOT_SUPPORTED))
#else
return nothingOrErrno(retryOnInterrupt: false) {
system_unlinkat(self.rawValue, path, options.rawValue)
}
#endif
}

/// Create a directory relative to a directory file descriptor
Expand Down Expand Up @@ -244,9 +259,13 @@ extension FileDescriptor {
internal func _createDirectory(
at path: UnsafePointer<CInterop.PlatformChar>, permissions: FilePermissions
) -> Result<(), Errno> {
#if os(Windows)
return .failure(Errno(rawValue: ERROR_NOT_SUPPORTED))
#else
return nothingOrErrno(retryOnInterrupt: false) {
system_mkdirat(self.rawValue, path, permissions.rawValue)
}
#endif
}

/// Create a symbolic link relative to a directory file descriptor
Expand Down Expand Up @@ -285,8 +304,12 @@ extension FileDescriptor {
original: UnsafePointer<CInterop.PlatformChar>,
link: UnsafePointer<CInterop.PlatformChar>
) -> Result<(), Errno> {
#if os(Windows)
return .failure(Errno(rawValue: ERROR_NOT_SUPPORTED))
#else
return nothingOrErrno(retryOnInterrupt: false) {
system_symlinkat(original, self.rawValue, link)
}
#endif
}
}
Loading
Loading