Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Sources/SWBCore/Settings/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3714,7 +3714,15 @@ private class SettingsBuilder: ProjectMatchLookup {
}
else {
// The target specifies an SDK not for the destination platform, but claims to support the destination platform.
requiredSDKCanonicalName = getLatestSDKCanonicalName(for: destinationPlatform)
if destinationUsesSwiftSDK {
// Synthesized Swift SDKs (e.g. wasm) are registered with the manifest path
// as their canonical name, not the platform name. Pushing
// platform.sdkCanonicalName (e.g. "webassembly") here would cause the
// subsequent SDK lookup to fail with "unable to find sdk '<platform>'".
requiredSDKCanonicalName = destinationSDK.canonicalName
} else {
requiredSDKCanonicalName = getLatestSDKCanonicalName(for: destinationPlatform)
}
}
}
else if destinationPlatformIsDeviceOrSimulator {
Expand Down
174 changes: 174 additions & 0 deletions Tests/SWBWebAssemblyPlatformTests/SWBWebAssemblyPlatformTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,178 @@ fileprivate struct SWBWebAssemblyPlatformTests: CoreBasedTests {
}
}
}

/// Regression test: a wasm app target depending on a library with platform specialization
/// must not fail with `unable to find sdk 'webassembly'`.
///
/// Two code paths in swift-build push `SDKROOT = platform.sdkCanonicalName` (= `"webassembly"`)
/// for Swift-SDK-backed builds, neither of which has a matching SDK in the registry when the
/// only wasm sysroot comes from a synthesized Swift SDK (whose canonical name is the manifest
/// path, not the platform name):
///
/// 1. `SpecializationParameters.imposed(on:workspaceContext:)`
/// in `swift-build/Sources/SWBCore/DependencyResolution.swift` — exercised by `MyLibrary`,
/// which is reachable via specialization from `MyApp`.
/// 2. `addRunDestinationSettingsPlatformSDK` in

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What package triggers this case?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expectation is package targets should always have PIF with SDKROOT: auto

@MaxDesiatov MaxDesiatov Apr 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repro steps are given in the linked issue, but essentially all packages targeting Wasm and depending on JavaScriptKit (which is arguably the vast majority of SwiftPM packages for Wasm) are broken and not buildable with SwiftBuild at this point #1325

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those packages don't produce a non-auto SDKROOT though, so the change to the else branch of addRunDestinationSettingsPlatformSDK should be a no-op

/// `swift-build/Sources/SWBCore/Settings/Settings.swift` else branch — exercised by
/// `MyMacOSLib`, whose configured SDK (`macosx`) does not match the destination platform
/// (`webassembly`) but whose `SUPPORTED_PLATFORMS` still includes wasm.
@Test(.requireSDKs(.host))
func wasmSwiftSDKDependencySpecialization() async throws {
try await withTemporaryDirectory { (tmpDir: Path) in
let clangCompilerPath = try await self.clangCompilerPath
let swiftCompilerPath = try await self.swiftCompilerPath
let swiftVersion = try await self.swiftVersion
let testProject = try await TestProject(
"aProject",
groupTree: TestGroup(
"SomeFiles", path: "Sources",
children: [
TestFile("App.swift"),
TestFile("Lib.swift"),
TestFile("MacLib.swift"),
]),
targets: [
TestStandardTarget(
"MyApp",
type: .commandLineTool,
buildConfigurations: [
TestBuildConfiguration("Debug",
buildSettings: [
"PRODUCT_NAME": "$(TARGET_NAME)",
"SDKROOT": "auto",
"SUPPORTED_PLATFORMS": "$(AVAILABLE_PLATFORMS)",
"CLANG_ENABLE_MODULES": "YES",
"SWIFT_EXEC": swiftCompilerPath.str,
"SWIFT_VERSION": swiftVersion,
"CC": clangCompilerPath.str,
"CLANG_EXPLICIT_MODULES_LIBCLANG_PATH": libClangPath.str,
"CLANG_USE_RESPONSE_FILE": "NO",
]),
],
buildPhases: [
TestSourcesBuildPhase([TestBuildFile("App.swift")]),
],
dependencies: ["MyLibrary", "MyMacOSLib"]
),
// Exercises DependencyResolution.swift `SpecializationParameters.imposed(on:)`:
// gets specialized to wasm via the dependency from MyApp.
TestStandardTarget(
"MyLibrary",
type: .staticLibrary,
buildConfigurations: [
TestBuildConfiguration("Debug",
buildSettings: [
"PRODUCT_NAME": "$(TARGET_NAME)",
"SDKROOT": "auto",
"SUPPORTED_PLATFORMS": "$(AVAILABLE_PLATFORMS)",
"ALLOW_TARGET_PLATFORM_SPECIALIZATION": "YES",
"CLANG_ENABLE_MODULES": "YES",
"SWIFT_EXEC": swiftCompilerPath.str,
"SWIFT_VERSION": swiftVersion,
"CC": clangCompilerPath.str,
"CLANG_EXPLICIT_MODULES_LIBCLANG_PATH": libClangPath.str,
"CLANG_USE_RESPONSE_FILE": "NO",
]),
],
buildPhases: [
TestSourcesBuildPhase([TestBuildFile("Lib.swift")]),
]),
// Exercises Settings.swift `addRunDestinationSettingsPlatformSDK` else branch:
// configured SDK ("macosx") differs from destination platform ("webassembly")
// but SUPPORTED_PLATFORMS includes wasm, so the SDK gets re-targeted.
// No ALLOW_TARGET_PLATFORM_SPECIALIZATION here — that flag would short-circuit
// the early-return guard at Settings.swift:3600 and skip the else branch.
TestStandardTarget(
"MyMacOSLib",
type: .staticLibrary,
buildConfigurations: [
TestBuildConfiguration("Debug",
buildSettings: [
"PRODUCT_NAME": "$(TARGET_NAME)",
"SDKROOT": "macosx",
"SUPPORTED_PLATFORMS": "$(AVAILABLE_PLATFORMS)",
"CLANG_ENABLE_MODULES": "YES",
"SWIFT_EXEC": swiftCompilerPath.str,
"SWIFT_VERSION": swiftVersion,
"CC": clangCompilerPath.str,
"CLANG_EXPLICIT_MODULES_LIBCLANG_PATH": libClangPath.str,
"CLANG_USE_RESPONSE_FILE": "NO",
]),
],
buildPhases: [
TestSourcesBuildPhase([TestBuildFile("MacLib.swift")]),
]),
])
// Use a dedicated core for this test so the SDKs it registers do not impact other tests
let core = try await Self.makeCore()
let tester = try TaskConstructionTester(core, testProject)

let sdkManifestContents = """
{
"schemaVersion" : "4.0",
"targetTriples" : {
"wasm32-unknown-wasip1" : {
"sdkRootPath" : "WASI.sdk",
"swiftResourcesPath" : "swift.xctoolchain/usr/lib/swift_static",
"swiftStaticResourcesPath" : "swift.xctoolchain/usr/lib/swift_static",
"toolsetPaths" : [ "toolset.json" ]
}
}
}
"""
let sdkManifestDir = tmpDir
try localFS.createDirectory(sdkManifestDir)
let sdkManifestPath = sdkManifestDir.join("swift-sdk.json")
try await localFS.writeFileContents(sdkManifestPath, waitForNewTimestamp: false, body: { $0.write(sdkManifestContents) })
try await localFS.writeFileContents(sdkManifestDir.join("toolset.json"), waitForNewTimestamp: false, body: { stream in
stream.write("""
{
"rootPath" : "swift.xctoolchain/usr/bin",
"schemaVersion" : "1.0",
"swiftCompiler" : { "extraCLIOptions" : [ "-static-stdlib" ] }
}
""")
})

let sysroot = sdkManifestDir.join("WASI.sdk")
let sdkroot = sdkManifestDir.join("WASI.sdk")

let destination = try RunDestinationInfo(sdkManifestPath: sdkManifestPath, triple: "wasm32-unknown-wasip1", targetArchitecture: "wasm32", supportedArchitectures: ["wasm32"], disableOnlyActiveArch: false, core: core)
let parameters = BuildParameters(configuration: "Debug", activeRunDestination: destination)

// Path 1: build MyApp — exercises DependencyResolution.swift `SpecializationParameters.imposed(on:)`
// because MyLibrary is reached via dependency from MyApp, which pre-imposes SDKROOT.
// Without the DependencyResolution fix, MyLibrary's specialized configuration would
// fail with `unable to find sdk 'webassembly'`.
await tester.checkBuild(parameters, runDestination: nil, targetName: "MyApp", fs: localFS) { results in
results.checkTask(.matchTargetName("MyLibrary"), .matchRuleType("SwiftDriver Compilation")) { task in
task.checkCommandLineContains([
["-static-stdlib"],
["-sdk", sdkroot.str],
["-sysroot", sysroot.str],
["-target", "wasm32-unknown-wasip1"],
].reduce([], +))
}

results.checkNoErrors()
}

// Path 2: build MyMacOSLib standalone — exercises Settings.swift `addRunDestinationSettingsPlatformSDK`
// else branch because no dependency imposes SDKROOT, and the target's own SDK ("macosx")
// doesn't match the destination platform ("webassembly"). Without the Settings.swift fix,
// this branch would push SDKROOT="webassembly" and the lookup would fail.
await tester.checkBuild(parameters, runDestination: nil, targetName: "MyMacOSLib", fs: localFS) { results in
results.checkTask(.matchTargetName("MyMacOSLib"), .matchRuleType("SwiftDriver Compilation")) { task in
task.checkCommandLineContains([
["-sdk", sdkroot.str],
["-sysroot", sysroot.str],
["-target", "wasm32-unknown-wasip1"],
].reduce([], +))
}

results.checkNoErrors()
}
}
}
}
Loading