Skip to content

Commit 86e6f7c

Browse files
Merge pull request #100 from swiftwasm/katei/windows-support
Initial Windows Support
2 parents 2efefda + f569b0b commit 86e6f7c

File tree

13 files changed

+192
-101
lines changed

13 files changed

+192
-101
lines changed

.github/workflows/main.yml

+10-4
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ jobs:
5656

5757
steps:
5858
- uses: actions/checkout@v3
59-
with:
60-
submodules: recursive
6159
- id: setup-swiftwasm
6260
uses: swiftwasm/setup-swiftwasm@v1
6361
with:
@@ -75,14 +73,22 @@ jobs:
7573
- run: ./CI/check-spectest.sh
7674
- run: ./CI/check-wasi-testsuite.sh
7775

76+
build-windows:
77+
runs-on: windows-latest
78+
steps:
79+
- uses: compnerd/gha-setup-swift@main
80+
with:
81+
branch: swift-5.10.1-release
82+
tag: 5.10.1-RELEASE
83+
- uses: actions/checkout@v4
84+
- run: swift test
85+
7886
build-cmake:
7987
runs-on: ubuntu-20.04
8088
container:
8189
image: swift:5.8-focal
8290
steps:
8391
- uses: actions/checkout@v4
84-
with:
85-
submodules: recursive
8692
- name: Install Ninja
8793
run: apt-get update && apt-get install -y ninja-build
8894
- name: Install CMake

Package.swift

+77-53
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,6 @@ let package = Package(
1515
name: "WasmParser",
1616
targets: ["WasmParser"]
1717
),
18-
.library(
19-
name: "WasmKitWASI",
20-
targets: ["WasmKitWASI"]
21-
),
22-
.library(
23-
name: "WASI",
24-
targets: ["WASI"]
25-
),
2618
.library(
2719
name: "WIT", targets: ["WIT"]
2820
),
@@ -31,15 +23,12 @@ let package = Package(
3123
targets: ["CLI"]
3224
),
3325
.library(name: "_CabiShims", targets: ["_CabiShims"]),
34-
.plugin(name: "WITOverlayPlugin", targets: ["WITOverlayPlugin"]),
35-
.plugin(name: "WITExtractorPlugin", targets: ["WITExtractorPlugin"]),
3626
],
3727
targets: [
3828
.executableTarget(
3929
name: "CLI",
4030
dependencies: [
4131
"WasmKit",
42-
"WasmKitWASI",
4332
.product(name: "ArgumentParser", package: "swift-argument-parser"),
4433
.product(name: "SystemPackage", package: "swift-system"),
4534
],
@@ -49,16 +38,10 @@ let package = Package(
4938
name: "WasmTypes",
5039
exclude: ["CMakeLists.txt"]
5140
),
52-
.target(
53-
name: "WASI",
54-
dependencies: ["WasmTypes", "SystemExtras"],
55-
exclude: ["CMakeLists.txt"]
56-
),
5741
.target(
5842
name: "WasmKit",
5943
dependencies: [
6044
"WasmParser",
61-
"SystemExtras",
6245
"WasmTypes",
6346
.product(name: "SystemPackage", package: "swift-system"),
6447
],
@@ -72,18 +55,6 @@ let package = Package(
7255
],
7356
exclude: ["CMakeLists.txt"]
7457
),
75-
.target(
76-
name: "WasmKitWASI",
77-
dependencies: ["WasmKit", "WASI"],
78-
exclude: ["CMakeLists.txt"]
79-
),
80-
.target(
81-
name: "SystemExtras",
82-
dependencies: [
83-
.product(name: "SystemPackage", package: "swift-system")
84-
],
85-
exclude: ["CMakeLists.txt"]
86-
),
8758
.executableTarget(
8859
name: "Spectest",
8960
dependencies: [
@@ -96,31 +67,11 @@ let package = Package(
9667
.testTarget(name: "WITTests", dependencies: ["WIT"]),
9768
.target(name: "WITOverlayGenerator", dependencies: ["WIT"]),
9869
.target(name: "_CabiShims"),
99-
.plugin(name: "WITOverlayPlugin", capability: .buildTool(), dependencies: ["WITTool"]),
100-
.plugin(name: "GenerateOverlayForTesting", capability: .buildTool(), dependencies: ["WITTool"]),
101-
.testTarget(
102-
name: "WITOverlayGeneratorTests",
103-
dependencies: ["WITOverlayGenerator", "WasmKit", "WasmKitWASI"],
104-
exclude: ["Fixtures", "Compiled", "Generated"],
105-
plugins: [.plugin(name: "GenerateOverlayForTesting")]
106-
),
10770
.target(name: "WITExtractor"),
10871
.testTarget(
10972
name: "WITExtractorTests",
11073
dependencies: ["WITExtractor", "WIT"]
11174
),
112-
.plugin(
113-
name: "WITExtractorPlugin",
114-
capability: .command(
115-
intent: .custom(verb: "extract-wit", description: "Extract WIT definition from Swift module"),
116-
permissions: []
117-
),
118-
dependencies: ["WITTool"]
119-
),
120-
.testTarget(
121-
name: "WITExtractorPluginTests",
122-
exclude: ["Fixtures"]
123-
),
12475
.executableTarget(
12576
name: "WITTool",
12677
dependencies: [
@@ -138,10 +89,6 @@ let package = Package(
13889
name: "WasmParserTests",
13990
dependencies: ["WasmParser"]
14091
),
141-
.testTarget(
142-
name: "WASITests",
143-
dependencies: ["WASI"]
144-
),
14592
],
14693
swiftLanguageVersions: [.v5]
14794
)
@@ -159,3 +106,80 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
159106
.package(path: "../swift-system"),
160107
]
161108
}
109+
110+
#if !os(Windows)
111+
// Add WASI-related products and targets
112+
package.products.append(contentsOf: [
113+
.library(
114+
name: "WasmKitWASI",
115+
targets: ["WasmKitWASI"]
116+
),
117+
.library(
118+
name: "WASI",
119+
targets: ["WASI"]
120+
),
121+
])
122+
package.targets.append(contentsOf: [
123+
.target(
124+
name: "WASI",
125+
dependencies: ["WasmTypes", "SystemExtras"],
126+
exclude: ["CMakeLists.txt"]
127+
),
128+
.target(
129+
name: "WasmKitWASI",
130+
dependencies: ["WasmKit", "WASI"],
131+
exclude: ["CMakeLists.txt"]
132+
),
133+
.target(
134+
name: "SystemExtras",
135+
dependencies: [
136+
.product(name: "SystemPackage", package: "swift-system")
137+
],
138+
exclude: ["CMakeLists.txt"]
139+
),
140+
.testTarget(
141+
name: "WASITests",
142+
dependencies: ["WASI"]
143+
),
144+
])
145+
let targetDependenciesToAdd = [
146+
"CLI": ["WasmKitWASI"],
147+
"WasmKit": ["SystemExtras"],
148+
]
149+
for (targetName, dependencies) in targetDependenciesToAdd {
150+
if let target = package.targets.first(where: { $0.name == targetName }) {
151+
target.dependencies += dependencies.map { .target(name: $0) }
152+
} else {
153+
fatalError("Target \(targetName) not found!?")
154+
}
155+
}
156+
157+
// Add build tool plugins only for non-Windows platforms
158+
package.products.append(contentsOf: [
159+
.plugin(name: "WITOverlayPlugin", targets: ["WITOverlayPlugin"]),
160+
.plugin(name: "WITExtractorPlugin", targets: ["WITExtractorPlugin"]),
161+
])
162+
163+
package.targets.append(contentsOf: [
164+
.plugin(name: "WITOverlayPlugin", capability: .buildTool(), dependencies: ["WITTool"]),
165+
.plugin(name: "GenerateOverlayForTesting", capability: .buildTool(), dependencies: ["WITTool"]),
166+
.testTarget(
167+
name: "WITOverlayGeneratorTests",
168+
dependencies: ["WITOverlayGenerator", "WasmKit", "WasmKitWASI"],
169+
exclude: ["Fixtures", "Compiled", "Generated"],
170+
plugins: [.plugin(name: "GenerateOverlayForTesting")]
171+
),
172+
.plugin(
173+
name: "WITExtractorPlugin",
174+
capability: .command(
175+
intent: .custom(verb: "extract-wit", description: "Extract WIT definition from Swift module"),
176+
permissions: []
177+
),
178+
dependencies: ["WITTool"]
179+
),
180+
.testTarget(
181+
name: "WITExtractorPluginTests",
182+
exclude: ["Fixtures"]
183+
),
184+
])
185+
#endif

Sources/CLI/Run/Run.swift

+13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import ArgumentParser
22
import SystemPackage
3+
#if canImport(WasmKitWASI)
34
import WasmKitWASI
5+
#endif
46
import WasmKit
57

68
struct Run: ParsableCommand {
@@ -78,6 +80,7 @@ struct Run: ParsableCommand {
7880
}
7981
}
8082

83+
#if canImport(SystemExtras)
8184
func deriveInterceptor() throws -> (interceptor: GuestTimeProfiler, finalize: () -> Void)? {
8285
guard let outputPath = self.profileOutput else { return nil }
8386
let fileHandle = try FileDescriptor.open(
@@ -97,8 +100,15 @@ struct Run: ParsableCommand {
97100
}
98101
)
99102
}
103+
#else
104+
// GuestTimeProfiler is not available without SystemExtras
105+
func deriveInterceptor() throws -> (interceptor: RuntimeInterceptor, finalize: () -> Void)? {
106+
nil
107+
}
108+
#endif
100109

101110
func instantiateWASI(module: Module, interceptor: RuntimeInterceptor?) throws -> () throws -> Void {
111+
#if canImport(WasmKitWASI)
102112
// Flatten environment variables into a dictionary (Respect the last value if a key is duplicated)
103113
let environment = environment.reduce(into: [String: String]()) {
104114
$0[$1.key] = $1.value
@@ -113,6 +123,9 @@ struct Run: ParsableCommand {
113123
let exitCode = try wasi.start(moduleInstance, runtime: runtime)
114124
throw ExitCode(Int32(exitCode))
115125
}
126+
#else
127+
fatalError("WASI is not supported on this platform")
128+
#endif
116129
}
117130

118131
func instantiateNonWASI(module: Module, interceptor: RuntimeInterceptor?) throws -> (() throws -> Void)? {

Sources/Spectest/Spectest.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ struct Spectest: AsyncParsableCommand {
4141

4242
let rootPath: String
4343
let filePath = FilePath(path)
44-
if (try? FileDescriptor.open(filePath, FileDescriptor.AccessMode.readOnly, options: .directory)) != nil {
44+
if isDirectory(filePath) {
4545
rootPath = path
4646
} else {
4747
rootPath = URL(fileURLWithPath: path).deletingLastPathComponent().path

Sources/Spectest/TestCase.swift

+22-11
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,6 @@ struct TestCase {
7979
let content: Content
8080
let path: String
8181

82-
private static func isDirectory(_ path: FilePath) -> Bool {
83-
let fd = try? FileDescriptor.open(path, FileDescriptor.AccessMode.readOnly, options: .directory)
84-
let isDirectory = fd != nil
85-
try? fd?.close()
86-
return isDirectory
87-
}
88-
8982
static func load(include: [String], exclude: [String], in path: String, log: ((String) -> Void)? = nil) throws -> [TestCase] {
9083
let fileManager = FileManager.default
9184
let filePath = FilePath(path)
@@ -419,9 +412,9 @@ extension TestCase.Command {
419412
}
420413
}
421414

422-
private func deriveFeatureSet(rootPath: String) -> WasmFeatureSet {
415+
private func deriveFeatureSet(rootPath: FilePath) -> WasmFeatureSet {
423416
var features = WasmFeatureSet.default
424-
if rootPath.hasSuffix("/proposals/memory64") {
417+
if rootPath.ends(with: "proposals/memory64") {
425418
features.insert(.memory64)
426419
// memory64 doesn't expect reference-types proposal
427420
// and it depends on the fact reference-types is disabled
@@ -431,9 +424,10 @@ extension TestCase.Command {
431424
}
432425

433426
private func parseModule(rootPath: String, filename: String) throws -> Module {
434-
let url = URL(fileURLWithPath: rootPath).appendingPathComponent(filename)
427+
let rootPath = FilePath(rootPath)
428+
let path = rootPath.appending(filename)
435429

436-
let module = try parseWasm(filePath: FilePath(url.path), features: deriveFeatureSet(rootPath: rootPath))
430+
let module = try parseWasm(filePath: path, features: deriveFeatureSet(rootPath: rootPath))
437431
return module
438432
}
439433

@@ -563,3 +557,20 @@ extension Swift.Error {
563557
return "unknown error: \(self)"
564558
}
565559
}
560+
561+
#if os(Windows)
562+
import WinSDK
563+
#endif
564+
internal func isDirectory(_ path: FilePath) -> Bool {
565+
#if os(Windows)
566+
return path.withPlatformString {
567+
let result = GetFileAttributesW($0)
568+
return result != INVALID_FILE_ATTRIBUTES && result & DWORD(FILE_ATTRIBUTE_DIRECTORY) != 0
569+
}
570+
#else
571+
let fd = try? FileDescriptor.open(path, FileDescriptor.AccessMode.readOnly, options: .directory)
572+
let isDirectory = fd != nil
573+
try? fd?.close()
574+
return isDirectory
575+
#endif
576+
}

Sources/SystemExtras/Clock.swift

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import ucrt
1212

1313
import SystemPackage
1414

15+
#if !os(Windows)
16+
1517
@frozen
1618
public struct Clock: RawRepresentable {
1719

@@ -110,3 +112,4 @@ extension Clock {
110112
}
111113
}
112114
}
115+
#endif

0 commit comments

Comments
 (0)