@@ -17,6 +17,7 @@ import XCTest
1717struct RuntimeTestHarness {
1818 struct Configuration : Codable {
1919 let swiftExecutablePath : URL
20+ let wasiSwiftSDKPath : URL
2021
2122 var swiftCompilerExecutablePath : URL {
2223 swiftExecutablePath. deletingLastPathComponent ( ) . appendingPathComponent ( " swiftc " )
@@ -53,9 +54,11 @@ struct RuntimeTestHarness {
5354 throw XCTSkip ( """
5455 Please create 'Tests/default.json' with this or similar contents:
5556 {
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+ " wasiSwiftSDKPath " : " $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 "
5759 }
5860
61+
5962 or specify `configuration` parameter in your test code.
6063 """ )
6164 }
@@ -116,32 +119,70 @@ struct RuntimeTestHarness {
116119 }
117120
118121 /// 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. wasiSwiftSDKPath. appendingPathComponent ( " /swift.xctoolchain/usr/lib/swift_static " ) . path,
170+ " -sdk " , configuration. wasiSwiftSDKPath. appendingPathComponent ( " WASI.sdk " ) . path,
171+ " -Xclang-linker " , " -resource-dir " ,
172+ " -Xclang-linker " , configuration. wasiSwiftSDKPath. appendingPathComponent ( " swift.xctoolchain/usr/lib/swift_static/clang " ) . path
173+ ] )
131174 }
132175
133176 /// 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 {
135178 let outputPath = Self . testsDirectory
136179 . appendingPathComponent ( " Compiled " )
137180 . appendingPathComponent ( " \( fixtureName) .core.wasm " )
138181 try fileManager. createDirectory ( at: outputPath. deletingLastPathComponent ( ) , withIntermediateDirectories: true )
139182 let process = Process ( )
140183 process. launchPath = configuration. swiftCompilerExecutablePath. path
141- process. arguments = inputFiles + [
142- " -target " , " wasm32-unknown-wasi " ,
184+ process. arguments = inputFiles + arguments + [
143185 " -I \( Self . sourcesDirectory. appendingPathComponent ( " _CabiShims " ) . appendingPathComponent ( " include " ) . path) " ,
144- " -Xclang-linker " , " -mexec-model=reactor " ,
145186 // TODO: Remove `--export-all` linker option by replacing `@_cdecl` with `@_expose(wasm)`
146187 " -Xlinker " , " --export-all " ,
147188 " -o " , outputPath. path
@@ -161,7 +202,9 @@ struct RuntimeTestHarness {
161202 """
162203 } . joined ( separator: " \n ==================== \n " )
163204 let message = """
164- Failed to execute \( process. arguments? . joined ( separator: " " ) ?? " " )
205+ Failed to execute \(
206+ ( [ configuration. swiftCompilerExecutablePath. path] + ( process. arguments ?? [ ] ) ) . joined ( separator: " " )
207+ )
165208 Exit status: \( process. terminationStatus)
166209 Input files:
167210 \( fileContents)
@@ -170,4 +213,28 @@ struct RuntimeTestHarness {
170213 }
171214 return outputPath
172215 }
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+ }
173240}
0 commit comments