@@ -17,6 +17,7 @@ import XCTest
17
17
struct RuntimeTestHarness {
18
18
struct Configuration : Codable {
19
19
let swiftExecutablePath : URL
20
+ let wasiSwiftSDK : URL
20
21
21
22
var swiftCompilerExecutablePath : URL {
22
23
swiftExecutablePath. deletingLastPathComponent ( ) . appendingPathComponent ( " swiftc " )
@@ -53,9 +54,11 @@ struct RuntimeTestHarness {
53
54
throw XCTSkip ( """
54
55
Please create 'Tests/default.json' with this or similar contents:
55
56
{
56
- " swiftExecutablePath " : " /Library/Developer/Toolchains/swift-wasm-5.8.0-RELEASE.xctoolchain/usr/bin/swift "
57
+ " swiftExecutablePath " : " $HOME/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a.xctoolchain/usr/bin/swift " ,
58
+ " wasiSwiftSDK " : " $HOME/Library/org.swift.swiftpm/swift-sdks/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi.artifactbundle/DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi/wasm32-unknown-wasi "
57
59
}
58
60
61
+
59
62
or specify `configuration` parameter in your test code.
60
63
""" )
61
64
}
@@ -116,32 +119,70 @@ struct RuntimeTestHarness {
116
119
}
117
120
118
121
/// Build up WebAssembly module from the fixture and instantiate WasmKit runtime with the module.
119
- mutating func build( link: ( inout [ String : HostModule ] ) -> Void ) throws -> ( Runtime , ModuleInstance ) {
120
- defer { cleanupTemporaryFiles ( ) }
121
- let compiled = try compile ( inputFiles: collectGuestInputFiles ( ) )
122
-
123
- let wasi = try WASIBridgeToHost ( args: [ compiled. path] )
124
- var hostModules : [ String : HostModule ] = wasi. hostModules
125
- link ( & hostModules)
126
-
127
- let module = try parseWasm ( filePath: . init( compiled. path) )
128
- let runtime = Runtime ( hostModules: hostModules)
129
- let instance = try runtime. instantiate ( module: module)
130
- return ( runtime, instance)
122
+ mutating func build(
123
+ link: ( inout [ String : HostModule ] ) -> Void ,
124
+ run: ( Runtime , ModuleInstance ) throws -> Void
125
+ ) throws {
126
+ for compile in [ compileForEmbedded, compileForWASI] {
127
+ defer { cleanupTemporaryFiles ( ) }
128
+ let compiled = try compile ( collectGuestInputFiles ( ) )
129
+
130
+ let wasi = try WASIBridgeToHost ( args: [ compiled. path] )
131
+ var hostModules : [ String : HostModule ] = wasi. hostModules
132
+ link ( & hostModules)
133
+
134
+ let module = try parseWasm ( filePath: . init( compiled. path) )
135
+ let runtime = Runtime ( hostModules: hostModules)
136
+ let instance = try runtime. instantiate ( module: module)
137
+ try run ( runtime, instance)
138
+ }
139
+ }
140
+
141
+ func compileForEmbedded( inputFiles: [ String ] ) throws -> URL {
142
+ let embeddedSupport = Self . testsDirectory. appendingPathComponent ( " EmbeddedSupport " )
143
+
144
+ let libc = embeddedSupport. appendingPathComponent ( " MinLibc.c " )
145
+ let libcObjFile = Self . testsDirectory
146
+ . appendingPathComponent ( " Compiled " )
147
+ . appendingPathComponent ( " MinLibc.o " )
148
+ FileManager . default. createFile ( atPath: libcObjFile. deletingLastPathComponent ( ) . path, contents: nil , attributes: nil )
149
+ try compileToObj ( cInputFiles: [ libc. path] , arguments: [
150
+ " -target " , " wasm32-unknown-none-wasm " ,
151
+ // Enable bulk memory operations for `realloc`
152
+ " -mbulk-memory " ,
153
+ ] , outputPath: libcObjFile)
154
+
155
+ return try compile ( inputFiles: inputFiles + [ libcObjFile. path] , arguments: [
156
+ " -target " , " wasm32-unknown-none-wasm " ,
157
+ " -enable-experimental-feature " , " Embedded " ,
158
+ " -wmo " , " -Xcc " , " -fdeclspec " ,
159
+ " -Xfrontend " , " -disable-stack-protector " ,
160
+ " -Xlinker " , " --no-entry " , " -Xclang-linker " , " -nostdlib " ,
161
+ ] )
162
+ }
163
+
164
+ func compileForWASI( inputFiles: [ String ] ) throws -> URL {
165
+ return try compile ( inputFiles: inputFiles, arguments: [
166
+ " -target " , " wasm32-unknown-wasi " ,
167
+ " -static-stdlib " ,
168
+ " -Xclang-linker " , " -mexec-model=reactor " ,
169
+ " -resource-dir " , configuration. wasiSwiftSDK. appendingPathComponent ( " /swift.xctoolchain/usr/lib/swift_static " ) . path,
170
+ " -sdk " , configuration. wasiSwiftSDK. appendingPathComponent ( " WASI.sdk " ) . path,
171
+ " -Xclang-linker " , " -resource-dir " ,
172
+ " -Xclang-linker " , configuration. wasiSwiftSDK. appendingPathComponent ( " swift.xctoolchain/usr/lib/swift_static/clang " ) . path
173
+ ] )
131
174
}
132
175
133
176
/// Compile the given input Swift source files into core Wasm module
134
- func compile( inputFiles: [ String ] ) throws -> URL {
177
+ func compile( inputFiles: [ String ] , arguments : [ String ] ) throws -> URL {
135
178
let outputPath = Self . testsDirectory
136
179
. appendingPathComponent ( " Compiled " )
137
180
. appendingPathComponent ( " \( fixtureName) .core.wasm " )
138
181
try fileManager. createDirectory ( at: outputPath. deletingLastPathComponent ( ) , withIntermediateDirectories: true )
139
182
let process = Process ( )
140
183
process. launchPath = configuration. swiftCompilerExecutablePath. path
141
- process. arguments = inputFiles + [
142
- " -target " , " wasm32-unknown-wasi " ,
184
+ process. arguments = inputFiles + arguments + [
143
185
" -I \( Self . sourcesDirectory. appendingPathComponent ( " _CabiShims " ) . appendingPathComponent ( " include " ) . path) " ,
144
- " -Xclang-linker " , " -mexec-model=reactor " ,
145
186
// TODO: Remove `--export-all` linker option by replacing `@_cdecl` with `@_expose(wasm)`
146
187
" -Xlinker " , " --export-all " ,
147
188
" -o " , outputPath. path
@@ -161,7 +202,9 @@ struct RuntimeTestHarness {
161
202
"""
162
203
} . joined ( separator: " \n ==================== \n " )
163
204
let message = """
164
- Failed to execute \( process. arguments? . joined ( separator: " " ) ?? " " )
205
+ Failed to execute \(
206
+ ( [ configuration. swiftCompilerExecutablePath. path] + ( process. arguments ?? [ ] ) ) . joined ( separator: " " )
207
+ )
165
208
Exit status: \( process. terminationStatus)
166
209
Input files:
167
210
\( fileContents)
@@ -170,4 +213,28 @@ struct RuntimeTestHarness {
170
213
}
171
214
return outputPath
172
215
}
216
+
217
+ /// Compile the given input Swift source files into an object file
218
+ func compileToObj( cInputFiles: [ String ] , arguments: [ String ] , outputPath: URL ) throws {
219
+ let process = Process ( )
220
+ // Assume that clang is placed alongside swiftc
221
+ process. launchPath = configuration. swiftCompilerExecutablePath
222
+ . deletingLastPathComponent ( ) . appendingPathComponent ( " clang " ) . path
223
+ process. arguments = cInputFiles + arguments + [
224
+ " -c " ,
225
+ " -o " , outputPath. path
226
+ ]
227
+ process. environment = [ : ]
228
+ process. launch ( )
229
+ process. waitUntilExit ( )
230
+ guard process. terminationStatus == 0 else {
231
+ let message = """
232
+ Failed to execute \(
233
+ ( [ configuration. swiftCompilerExecutablePath. path] + ( process. arguments ?? [ ] ) ) . joined ( separator: " " )
234
+ )
235
+ Exit status: \( process. terminationStatus)
236
+ """
237
+ throw Error ( description: message)
238
+ }
239
+ }
173
240
}
0 commit comments