diff --git a/Sources/Helpers/Vendor/QueryEngine/CacheKey.swift b/Sources/Helpers/Vendor/QueryEngine/CacheKey.swift index 6904828a..ac961ca8 100644 --- a/Sources/Helpers/Vendor/QueryEngine/CacheKey.swift +++ b/Sources/Helpers/Vendor/QueryEngine/CacheKey.swift @@ -182,4 +182,3 @@ extension Array: CacheKey where Element == FilePath.Component { map(\.string).joined(separator: "\n").hash(with: &hashFunction) } } - diff --git a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Entrypoint.swift b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Entrypoint.swift index e5c9cf64..eecdd99c 100644 --- a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Entrypoint.swift +++ b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Entrypoint.swift @@ -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 @@ -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( diff --git a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift index e35ae019..9fb9db25 100644 --- a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift +++ b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift @@ -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 @@ -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 @@ -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") @@ -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) } ) ] ) - ] + } ) ) ) diff --git a/Sources/SwiftSDKGenerator/Serialization/SwiftSDKMetadata.swift b/Sources/SwiftSDKGenerator/Serialization/SwiftSDKMetadata.swift index b6937119..ad855ea4 100644 --- a/Sources/SwiftSDKGenerator/Serialization/SwiftSDKMetadata.swift +++ b/Sources/SwiftSDKGenerator/Serialization/SwiftSDKMetadata.swift @@ -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 @@ -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] } diff --git a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift index 9c767d7c..af3ecc44 100644 --- a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift +++ b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift @@ -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 @@ -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 { @@ -44,7 +44,7 @@ public struct LinuxRecipe: SwiftSDKRecipe { return false } - public init( + package init( targetTriple: Triple, hostTriple: Triple, linuxDistribution: LinuxDistribution, @@ -98,7 +98,7 @@ public struct LinuxRecipe: SwiftSDKRecipe { ) } - public init( + package init( mainTargetTriple: Triple, mainHostTriple: Triple, linuxDistribution: LinuxDistribution, @@ -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 } @@ -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 @@ -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 diff --git a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/SwiftSDKRecipe.swift b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/SwiftSDKRecipe.swift index 5161519d..6b114512 100644 --- a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/SwiftSDKRecipe.swift +++ b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/SwiftSDKRecipe.swift @@ -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 @@ -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 } } diff --git a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/WebAssemblyRecipe.swift b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/WebAssemblyRecipe.swift index f3b16dc1..de9a8d9c 100644 --- a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/WebAssemblyRecipe.swift +++ b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/WebAssemblyRecipe.swift @@ -15,24 +15,24 @@ import Logging import struct SystemPackage.FilePath -public struct WebAssemblyRecipe: SwiftSDKRecipe { +package struct WebAssemblyRecipe: SwiftSDKRecipe { let hostSwiftPackage: HostToolchainPackage? let targetSwiftPackagePath: FilePath let wasiSysroot: FilePath let swiftVersion: String - public let logger: Logger + package let logger: Logger - public struct HostToolchainPackage: Sendable { + package struct HostToolchainPackage: Sendable { let path: FilePath let triple: Triple - public init(path: FilePath, triple: Triple) { + package init(path: FilePath, triple: Triple) { self.path = path self.triple = triple } } - public init( + package init( hostSwiftPackage: HostToolchainPackage?, targetSwiftPackagePath: FilePath, wasiSysroot: FilePath, @@ -46,13 +46,35 @@ public struct WebAssemblyRecipe: SwiftSDKRecipe { self.logger = logger } - public var defaultArtifactID: String { + package var defaultArtifactID: String { "\(self.swiftVersion)_wasm" } - public func applyPlatformOptions(toolset: inout Toolset, targetTriple: Triple) { + package let shouldSupportEmbeddedSwift = true + + package func applyPlatformOptions(toolset: inout Toolset, targetTriple: Triple, isForEmbeddedSwift: Bool) { // We only support static linking for WebAssembly for now, so make it the default. toolset.swiftCompiler = Toolset.ToolProperties(extraCLIOptions: ["-static-stdlib"]) + + if isForEmbeddedSwift { + let ccOptions = ["-D__EMBEDDED_SWIFT__"] + toolset.cCompiler = Toolset.ToolProperties(extraCLIOptions: ccOptions) + toolset.cxxCompiler = Toolset.ToolProperties(extraCLIOptions: ccOptions) + + toolset.swiftCompiler?.extraCLIOptions?.append( + contentsOf: [ + "-enable-experimental-feature", "Embedded", "-wmo", + ] + ) + + toolset.swiftCompiler?.extraCLIOptions?.append( + // libraries required for concurrency + contentsOf: ["-lc++", "-lswift_Concurrency", "-lswift_ConcurrencyDefaultExecutor"].flatMap { + ["-Xlinker", $0] + } + ) + } + if targetTriple.environmentName == "threads" { // Enable features required for threading support let ccOptions = [ @@ -82,10 +104,11 @@ public struct WebAssemblyRecipe: SwiftSDKRecipe { } } - public func applyPlatformOptions( - metadata: inout SwiftSDKMetadataV4.TripleProperties, + package func applyPlatformOptions( + metadata: inout SwiftSDKMetadataV4, paths: PathsConfiguration, - targetTriple: Triple + targetTriple: Triple, + isForEmbeddedSwift: Bool ) { var relativeToolchainDir = paths.toolchainDirPath guard relativeToolchainDir.removePrefix(paths.swiftSDKRootPath) else { @@ -93,12 +116,25 @@ public struct WebAssemblyRecipe: SwiftSDKRecipe { "The toolchain bin directory path must be a subdirectory of the Swift SDK root path." ) } - metadata.swiftStaticResourcesPath = + + var tripleProperties = metadata.targetTriples[targetTriple.triple]! + tripleProperties.swiftStaticResourcesPath = relativeToolchainDir.appending("usr/lib/swift_static").string - metadata.swiftResourcesPath = metadata.swiftStaticResourcesPath + tripleProperties.swiftResourcesPath = + isForEmbeddedSwift + ? relativeToolchainDir.appending("usr/lib/swift").string + : tripleProperties.swiftStaticResourcesPath + + var finalTriple = targetTriple + if isForEmbeddedSwift { + metadata.targetTriples.removeValue(forKey: targetTriple.triple) + finalTriple = Triple("wasm32-unknown-wasip1") + } + + metadata.targetTriples[finalTriple.triple] = tripleProperties } - public func makeSwiftSDK( + package func makeSwiftSDK( generator: SwiftSDKGenerator, engine: QueryEngine, httpClient: some HTTPClientProtocol diff --git a/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/LinuxRecipeTests.swift b/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/LinuxRecipeTests.swift index ddda1deb..b57807e5 100644 --- a/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/LinuxRecipeTests.swift +++ b/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/LinuxRecipeTests.swift @@ -85,7 +85,8 @@ final class LinuxRecipeTests: XCTestCase { var toolset = Toolset(rootPath: nil) recipe.applyPlatformOptions( toolset: &toolset, - targetTriple: testCase.targetTriple + targetTriple: testCase.targetTriple, + isForEmbeddedSwift: false ) XCTAssertEqual(toolset.swiftCompiler?.extraCLIOptions, testCase.expectedSwiftCompilerOptions) XCTAssertEqual(toolset.linker?.path, testCase.expectedLinkerPath) @@ -103,7 +104,8 @@ final class LinuxRecipeTests: XCTestCase { var toolset = Toolset(rootPath: "swift.xctoolchain") recipe.applyPlatformOptions( toolset: &toolset, - targetTriple: Triple("x86_64-unknown-linux-gnu") + targetTriple: Triple("x86_64-unknown-linux-gnu"), + isForEmbeddedSwift: false ) XCTAssertEqual(toolset.rootPath, nil) XCTAssertEqual( diff --git a/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/WebAssemblyRecipe.swift b/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/WebAssemblyRecipe.swift index 228f4f5a..dff94993 100644 --- a/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/WebAssemblyRecipe.swift +++ b/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/WebAssemblyRecipe.swift @@ -33,7 +33,8 @@ final class WebAssemblyRecipeTests: XCTestCase { var toolset = Toolset(rootPath: nil) recipe.applyPlatformOptions( toolset: &toolset, - targetTriple: Triple("wasm32-unknown-wasi") + targetTriple: Triple("wasm32-unknown-wasi"), + isForEmbeddedSwift: false ) XCTAssertEqual(toolset.swiftCompiler?.extraCLIOptions, ["-static-stdlib"]) XCTAssertNil(toolset.cCompiler) @@ -41,12 +42,36 @@ final class WebAssemblyRecipeTests: XCTestCase { XCTAssertNil(toolset.linker) } + func testEmbeddedToolOptions() { + let recipe = self.createRecipe() + var toolset = Toolset(rootPath: nil) + recipe.applyPlatformOptions( + toolset: &toolset, + targetTriple: Triple("wasm32-unknown-wasi"), + isForEmbeddedSwift: true + ) + XCTAssertEqual( + toolset.swiftCompiler?.extraCLIOptions, + [ + "-static-stdlib", + "-enable-experimental-feature", "Embedded", "-wmo", + ] + + ["-lc++", "-lswift_Concurrency", "-lswift_ConcurrencyDefaultExecutor"].flatMap { + ["-Xlinker", $0] + } + ) + XCTAssertEqual(toolset.cCompiler?.extraCLIOptions, ["-D__EMBEDDED_SWIFT__"]) + XCTAssertEqual(toolset.cxxCompiler?.extraCLIOptions, ["-D__EMBEDDED_SWIFT__"]) + XCTAssertNil(toolset.linker) + } + func testToolOptionsWithThreads() { let recipe = self.createRecipe() var toolset = Toolset(rootPath: nil) recipe.applyPlatformOptions( toolset: &toolset, - targetTriple: Triple("wasm32-unknown-wasip1-threads") + targetTriple: Triple("wasm32-unknown-wasip1-threads"), + isForEmbeddedSwift: false ) XCTAssertEqual( toolset.swiftCompiler?.extraCLIOptions,