Skip to content

Generate Embedded Swift SDK for WASI #208

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 9 commits into from
May 15, 2025
1 change: 0 additions & 1 deletion Sources/Helpers/Vendor/QueryEngine/CacheKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,3 @@ extension Array: CacheKey where Element == FilePath.Component {
map(\.string).joined(separator: "\n").hash(with: &hashFunction)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ extension Triple.Arch {
}

extension SwiftSDKGenerator {
public func run(recipe: SwiftSDKRecipe) async throws {
package func run(recipe: SwiftSDKRecipe) async throws {
try await withQueryEngine(OSFileSystem(), self.logger, cacheLocation: self.engineCachePath) {
engine in
let httpClientType: HTTPClientProtocol.Type
Expand All @@ -58,13 +58,29 @@ extension SwiftSDKGenerator {

let toolsetJSONPath = try await self.generateToolsetJSON(recipe: recipe)

try await generateDestinationJSON(
toolsetPath: toolsetJSONPath,
sdkDirPath: swiftSDKProduct.sdkDirPath,
recipe: recipe
)
var artifacts = try await [
self.artifactID: generateSwiftSDKMetadata(
toolsetPath: toolsetJSONPath,
sdkDirPath: swiftSDKProduct.sdkDirPath,
recipe: recipe
)
]

if recipe.shouldSupportEmbeddedSwift {
let toolsetJSONPath = try await self.generateToolsetJSON(recipe: recipe, isForEmbeddedSwift: true)

try await generateArtifactBundleManifest(hostTriples: swiftSDKProduct.hostTriples)
artifacts["\(self.artifactID)-embedded"] = try await generateSwiftSDKMetadata(
toolsetPath: toolsetJSONPath,
sdkDirPath: swiftSDKProduct.sdkDirPath,
recipe: recipe,
isForEmbeddedSwift: true
)
}

try await generateArtifactBundleManifest(
hostTriples: swiftSDKProduct.hostTriples,
artifacts: artifacts
)

// Extra spaces added for readability for the user
print(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ private let encoder: JSONEncoder = {
}()

extension SwiftSDKGenerator {
func generateToolsetJSON(recipe: SwiftSDKRecipe) throws -> FilePath {
func generateToolsetJSON(recipe: SwiftSDKRecipe, isForEmbeddedSwift: Bool = false) throws -> FilePath {
logger.info("Generating toolset JSON file...")

let toolsetJSONPath = pathsConfiguration.swiftSDKRootPath.appending("toolset.json")
let toolsetJSONPath = pathsConfiguration.swiftSDKRootPath.appending(
"\(isForEmbeddedSwift ? "embedded-" : "")toolset.json"
)

var relativeToolchainBinDir = pathsConfiguration.toolchainBinDirPath

Expand All @@ -37,18 +39,27 @@ extension SwiftSDKGenerator {
}

var toolset = Toolset(rootPath: relativeToolchainBinDir.string)
recipe.applyPlatformOptions(toolset: &toolset, targetTriple: self.targetTriple)
recipe.applyPlatformOptions(
toolset: &toolset,
targetTriple: self.targetTriple,
isForEmbeddedSwift: isForEmbeddedSwift
)
try writeFile(at: toolsetJSONPath, encoder.encode(toolset))

return toolsetJSONPath
}

func generateDestinationJSON(toolsetPath: FilePath, sdkDirPath: FilePath, recipe: SwiftSDKRecipe)
throws
{
logger.info("Generating destination JSON file...")
func generateSwiftSDKMetadata(
toolsetPath: FilePath,
sdkDirPath: FilePath,
recipe: SwiftSDKRecipe,
isForEmbeddedSwift: Bool = false
) throws -> FilePath {
logger.info("Generating Swift SDK metadata JSON file...")

let destinationJSONPath = pathsConfiguration.swiftSDKRootPath.appending("swift-sdk.json")
let destinationJSONPath = pathsConfiguration.swiftSDKRootPath.appending(
"\(isForEmbeddedSwift ? "embedded-" : "")swift-sdk.json"
)

var relativeToolchainBinDir = pathsConfiguration.toolchainBinDirPath
var relativeSDKDir = sdkDirPath
Expand All @@ -67,30 +78,31 @@ extension SwiftSDKGenerator {
)
}

var metadata = SwiftSDKMetadataV4.TripleProperties(
sdkRootPath: relativeSDKDir.string,
toolsetPaths: [relativeToolsetPath.string]
var metadata = SwiftSDKMetadataV4(
targetTriples: [
self.targetTriple.triple: .init(
sdkRootPath: relativeSDKDir.string,
toolsetPaths: [relativeToolsetPath.string]
)
]
)

recipe.applyPlatformOptions(
metadata: &metadata,
paths: pathsConfiguration,
targetTriple: self.targetTriple
targetTriple: self.targetTriple,
isForEmbeddedSwift: isForEmbeddedSwift
)

try writeFile(
at: destinationJSONPath,
encoder.encode(
SwiftSDKMetadataV4(
targetTriples: [
self.targetTriple.triple: metadata
]
)
)
encoder.encode(metadata)
)

return destinationJSONPath
}

func generateArtifactBundleManifest(hostTriples: [Triple]?) throws {
func generateArtifactBundleManifest(hostTriples: [Triple]?, artifacts: [String: FilePath]) throws {
logger.info("Generating .artifactbundle info JSON file...")

let artifactBundleManifestPath = pathsConfiguration.artifactBundlePath.appending("info.json")
Expand All @@ -100,18 +112,18 @@ extension SwiftSDKGenerator {
encoder.encode(
ArtifactsArchiveMetadata(
schemaVersion: "1.0",
artifacts: [
artifactID: .init(
artifacts: artifacts.mapValues {
.init(
type: .swiftSDK,
version: self.bundleVersion,
variants: [
.init(
path: FilePath(artifactID).appending(self.targetTriple.triple).string,
path: $0.string,
supportedTriples: hostTriples.map { $0.map(\.triple) }
)
]
)
]
}
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ struct DestinationV3: Encodable {
}

/// Represents v4 schema of `swift-sdk.json` (previously `destination.json`) files used for cross-compilation.
public struct SwiftSDKMetadataV4: Encodable {
public struct TripleProperties: Encodable {
package struct SwiftSDKMetadataV4: Encodable {
package struct TripleProperties: Encodable {
/// Path relative to `swift-sdk.json` containing SDK root.
var sdkRootPath: String

Expand All @@ -98,5 +98,5 @@ public struct SwiftSDKMetadataV4: Encodable {
let schemaVersion = "4.0"

/// Mapping of triple strings to corresponding properties of such target triple.
let targetTriples: [String: TripleProperties]
var targetTriples: [String: TripleProperties]
}
30 changes: 16 additions & 14 deletions Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ import Logging

import struct SystemPackage.FilePath

public struct LinuxRecipe: SwiftSDKRecipe {
public enum TargetSwiftSource: Sendable {
package struct LinuxRecipe: SwiftSDKRecipe {
package enum TargetSwiftSource: Sendable {
case docker(baseSwiftDockerImage: String)
case localPackage(FilePath)
case remoteTarball
}

public enum HostSwiftSource: Sendable, Equatable {
package enum HostSwiftSource: Sendable, Equatable {
case localPackage(FilePath)
case remoteTarball
case preinstalled
Expand All @@ -35,7 +35,7 @@ public struct LinuxRecipe: SwiftSDKRecipe {
let targetSwiftSource: TargetSwiftSource
let hostSwiftSource: HostSwiftSource
let versionsConfiguration: VersionsConfiguration
public let logger: Logger
package let logger: Logger

var shouldUseDocker: Bool {
if case .docker = self.targetSwiftSource {
Expand All @@ -44,7 +44,7 @@ public struct LinuxRecipe: SwiftSDKRecipe {
return false
}

public init(
package init(
targetTriple: Triple,
hostTriple: Triple,
linuxDistribution: LinuxDistribution,
Expand Down Expand Up @@ -98,7 +98,7 @@ public struct LinuxRecipe: SwiftSDKRecipe {
)
}

public init(
package init(
mainTargetTriple: Triple,
mainHostTriple: Triple,
linuxDistribution: LinuxDistribution,
Expand All @@ -116,7 +116,7 @@ public struct LinuxRecipe: SwiftSDKRecipe {
self.logger = logger
}

public func applyPlatformOptions(toolset: inout Toolset, targetTriple: Triple) {
package func applyPlatformOptions(toolset: inout Toolset, targetTriple: Triple, isForEmbeddedSwift: Bool) {
if self.hostSwiftSource == .preinstalled {
toolset.rootPath = nil
}
Expand Down Expand Up @@ -146,20 +146,22 @@ public struct LinuxRecipe: SwiftSDKRecipe {
toolset.librarian = Toolset.ToolProperties(path: "llvm-ar")
}

public func applyPlatformOptions(
metadata: inout SwiftSDKMetadataV4.TripleProperties,
package func applyPlatformOptions(
metadata: inout SwiftSDKMetadataV4,
paths: PathsConfiguration,
targetTriple: Triple
targetTriple: Triple,
isForEmbeddedSwift: Bool
) {
var relativeSDKDir = self.sdkDirPath(paths: paths)
guard relativeSDKDir.removePrefix(paths.swiftSDKRootPath) else {
fatalError("The SDK directory path must be a subdirectory of the Swift SDK root path.")
}
metadata.swiftResourcesPath = relativeSDKDir.appending("usr/lib/swift").string
metadata.swiftStaticResourcesPath = relativeSDKDir.appending("usr/lib/swift_static").string
metadata.targetTriples[targetTriple.triple]?.swiftResourcesPath = relativeSDKDir.appending("usr/lib/swift").string
metadata.targetTriples[targetTriple.triple]?.swiftStaticResourcesPath =
relativeSDKDir.appending("usr/lib/swift_static").string
}

public var defaultArtifactID: String {
package var defaultArtifactID: String {
"""
\(self.versionsConfiguration.swiftVersion)_\(self.linuxDistribution.name.rawValue)_\(
self.linuxDistribution
Expand Down Expand Up @@ -219,7 +221,7 @@ public struct LinuxRecipe: SwiftSDKRecipe {
return [self.mainHostTriple]
}

public func makeSwiftSDK(
package func makeSwiftSDK(
generator: SwiftSDKGenerator,
engine: QueryEngine,
httpClient client: some HTTPClientProtocol
Expand Down
30 changes: 20 additions & 10 deletions Sources/SwiftSDKGenerator/SwiftSDKRecipes/SwiftSDKRecipe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,25 @@ import Logging

import struct SystemPackage.FilePath

public struct SwiftSDKProduct {
package struct SwiftSDKProduct {
let sdkDirPath: FilePath
/// Array of supported host triples. `nil` indicates the SDK can be universally used.
let hostTriples: [Triple]?
}

/// A protocol describing a set of platform specific instructions to make a Swift SDK
public protocol SwiftSDKRecipe: Sendable {
package protocol SwiftSDKRecipe: Sendable {
/// Update the given toolset with platform specific options
func applyPlatformOptions(
toolset: inout Toolset,
targetTriple: Triple
targetTriple: Triple,
isForEmbeddedSwift: Bool
)
func applyPlatformOptions(
metadata: inout SwiftSDKMetadataV4.TripleProperties,
metadata: inout SwiftSDKMetadataV4,
paths: PathsConfiguration,
targetTriple: Triple
targetTriple: Triple,
isForEmbeddedSwift: Bool
)

/// The default identifier of the Swift SDK
Expand All @@ -45,15 +47,23 @@ public protocol SwiftSDKRecipe: Sendable {
generator: SwiftSDKGenerator,
engine: QueryEngine,
httpClient: some HTTPClientProtocol
) async throws
-> SwiftSDKProduct
) async throws -> SwiftSDKProduct

var shouldSupportEmbeddedSwift: Bool { get }
}

extension SwiftSDKRecipe {
public func applyPlatformOptions(toolset: inout Toolset, targetTriple: Triple) {}
public func applyPlatformOptions(
package func applyPlatformOptions(
toolset: inout Toolset,
targetTriple: Triple,
isForEmbeddedSwift: Bool
) {}
package func applyPlatformOptions(
metadata: inout SwiftSDKMetadataV4.TripleProperties,
paths: PathsConfiguration,
targetTriple: Triple
targetTriple: Triple,
isForEmbeddedSwift: Bool
) {}

package var shouldSupportEmbeddedSwift: Bool { false }
}
Loading