Skip to content

Commit de216d1

Browse files
Add WebAssembly SDK recipe and make-wasm-sdk subcommand (#74)
* Add WebAssembly SDK recipe and `make-wasm-sdk` subcommand This patch introduces an experimental WebAssembly SDK recipe and `make-wasm-sdk` subcommand. The make command takes host Swift toolchain package, target Swift stdlib (`lib/swift{,_static}`), and WASI sysroot like follows: ```console swift run swift-sdk-generator make-wasm-sdk \ --target wasm32-unknown-wasi \ --host-swift-package-path Downloads/swift-DEVELOPMENT-SNAPSHOT-2024-01-12-a \ --target-swift-package-path Downloads/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-01-12-a \ --wasi-sysroot Downloads/wasi-sysroot ``` * Use `GeneratorOptions`'s common fields * rsync `lib/clang` into the SDK bundle The directory was missed to be taken from target toolchain but it wasn't revealed on local testing since the host toolchain unintentionally contained the directory. --------- Co-authored-by: Max Desiatov <[email protected]>
1 parent cf9e76a commit de216d1

File tree

3 files changed

+136
-1
lines changed

3 files changed

+136
-1
lines changed

Sources/GeneratorCLI/GeneratorCLI.swift

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ import ArgumentParser
1414
import Logging
1515
import ServiceLifecycle
1616
import SwiftSDKGenerator
17+
import struct SystemPackage.FilePath
1718

1819
@main
1920
struct GeneratorCLI: AsyncParsableCommand {
2021
static let configuration = CommandConfiguration(
2122
commandName: "swift-sdk-generator",
22-
subcommands: [MakeLinuxSDK.self],
23+
subcommands: [MakeLinuxSDK.self, MakeWasmSDK.self],
2324
defaultSubcommand: MakeLinuxSDK.self
2425
)
2526

@@ -238,6 +239,46 @@ extension GeneratorCLI {
238239
return false
239240
}
240241
}
242+
243+
struct MakeWasmSDK: AsyncParsableCommand {
244+
static let configuration = CommandConfiguration(
245+
commandName: "make-wasm-sdk",
246+
abstract: "Experimental: Generate a Swift SDK bundle for WebAssembly.",
247+
discussion: """
248+
The default `--target` triple is wasm32-unknown-wasi
249+
"""
250+
)
251+
252+
@OptionGroup
253+
var generatorOptions: GeneratorOptions
254+
255+
@Option(
256+
help: """
257+
Path to the WASI sysroot directory containing the WASI libc headers and libraries.
258+
"""
259+
)
260+
var wasiSysroot: String
261+
262+
func deriveTargetTriple(hostTriple: Triple) -> Triple {
263+
self.generatorOptions.target ?? Triple("wasm32-unknown-wasi")
264+
}
265+
266+
func run() async throws {
267+
guard let hostSwiftPackagePath = generatorOptions.hostSwiftPackagePath,
268+
let targetSwiftPackagePath = generatorOptions.targetSwiftPackagePath else {
269+
throw StringError("Missing expected argument '--host-swift-package-path' or '--target-swift-package-path'")
270+
}
271+
let recipe = WebAssemblyRecipe(
272+
hostSwiftPackagePath: FilePath(hostSwiftPackagePath),
273+
targetSwiftPackagePath: FilePath(targetSwiftPackagePath),
274+
wasiSysroot: FilePath(wasiSysroot),
275+
swiftVersion: generatorOptions.swiftVersion
276+
)
277+
let hostTriple = try self.generatorOptions.deriveHostTriple()
278+
let targetTriple = self.deriveTargetTriple(hostTriple: hostTriple)
279+
try await GeneratorCLI.run(recipe: recipe, hostTriple: hostTriple, targetTriple: targetTriple, options: generatorOptions)
280+
}
281+
}
241282
}
242283

243284
// FIXME: replace this with a call on `.formatted()` on `Duration` when it's available in swift-foundation.
@@ -269,3 +310,10 @@ struct SwiftSDKGeneratorService: Service {
269310
try await generator.run(recipe: recipe)
270311
}
271312
}
313+
314+
struct StringError: Error, CustomStringConvertible {
315+
let description: String
316+
init(_ description: String) {
317+
self.description = description
318+
}
319+
}

Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ public actor SwiftSDKGenerator {
167167
try await Shell.run("rsync -a \(source) \(destination)", shouldLogCommands: self.isVerbose)
168168
}
169169

170+
func rsyncContents(from source: FilePath, to destination: FilePath) async throws {
171+
try self.createDirectoryIfNeeded(at: destination)
172+
try await Shell.run("rsync -a \(source)/ \(destination)", shouldLogCommands: self.isVerbose)
173+
}
174+
170175
func createSymlink(at source: FilePath, pointingTo destination: FilePath) throws {
171176
try self.fileManager.createSymbolicLink(
172177
atPath: source.string,
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2022-2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import AsyncHTTPClient
14+
import GeneratorEngine
15+
import struct SystemPackage.FilePath
16+
17+
public struct WebAssemblyRecipe: SwiftSDKRecipe {
18+
let hostSwiftPackagePath: FilePath
19+
let targetSwiftPackagePath: FilePath
20+
let wasiSysroot: FilePath
21+
let swiftVersion: String
22+
23+
public init(
24+
hostSwiftPackagePath: FilePath,
25+
targetSwiftPackagePath: FilePath,
26+
wasiSysroot: FilePath,
27+
swiftVersion: String
28+
) {
29+
self.hostSwiftPackagePath = hostSwiftPackagePath
30+
self.targetSwiftPackagePath = targetSwiftPackagePath
31+
self.wasiSysroot = wasiSysroot
32+
self.swiftVersion = swiftVersion
33+
}
34+
35+
public var defaultArtifactID: String {
36+
"\(self.swiftVersion)_wasm"
37+
}
38+
39+
public func applyPlatformOptions(toolset: inout Toolset) {
40+
// We only support static linking for WebAssembly for now, so make it the default.
41+
toolset.swiftCompiler = Toolset.ToolProperties(extraCLIOptions: ["-static-stdlib"])
42+
}
43+
44+
public func makeSwiftSDK(
45+
generator: SwiftSDKGenerator,
46+
engine: Engine,
47+
httpClient: HTTPClient
48+
) async throws -> SwiftSDKProduct {
49+
let pathsConfiguration = generator.pathsConfiguration
50+
logGenerationStep("Copying Swift binaries for the host triple...")
51+
try await generator.rsync(from: self.hostSwiftPackagePath.appending("usr"), to: pathsConfiguration.toolchainDirPath)
52+
try await self.copyTargetSwift(from: self.targetSwiftPackagePath.appending("usr/lib"), generator: generator)
53+
54+
let autolinkExtractPath = generator.pathsConfiguration.toolchainBinDirPath.appending("swift-autolink-extract")
55+
56+
// WebAssembly object file requires `swift-autolink-extract`
57+
if await !generator.doesFileExist(at: autolinkExtractPath) {
58+
logGenerationStep("Fixing `swift-autolink-extract` symlink...")
59+
try await generator.createSymlink(at: autolinkExtractPath, pointingTo: "swift")
60+
}
61+
62+
// Copy the WASI sysroot into the SDK bundle.
63+
let sdkDirPath = pathsConfiguration.swiftSDKRootPath.appending("WASI.sdk")
64+
try await generator.rsyncContents(from: self.wasiSysroot, to: sdkDirPath)
65+
66+
return SwiftSDKProduct(sdkDirPath: sdkDirPath)
67+
}
68+
69+
func copyTargetSwift(from distributionPath: FilePath, generator: SwiftSDKGenerator) async throws {
70+
let pathsConfiguration = generator.pathsConfiguration
71+
logGenerationStep("Copying Swift core libraries for the target triple into Swift SDK bundle...")
72+
for (pathWithinPackage, pathWithinSwiftSDK) in [
73+
("clang", pathsConfiguration.toolchainDirPath.appending("usr/lib")),
74+
("swift/wasi", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift")),
75+
("swift_static/wasi", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static")),
76+
("swift_static/shims", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static")),
77+
("swift_static/CoreFoundation", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static")),
78+
] {
79+
try await generator.rsync(from: distributionPath.appending(pathWithinPackage), to: pathWithinSwiftSDK)
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)