Skip to content

Conversation

@cmcgee1024
Copy link
Member

The client can provide a Swift SDK manifest path along with a triple in the run destination to synthesize a Swift Build SDK. This serves as a fallback for cases where Swift Build cannot match an SDK for it.

Note that in this first implementation the platform is hard coded to webassembly, and so only SDK's that align with WASM, such as static linking of compiler runtime pieces, and some Unix semantics will work.

The client can provide a Swift SDK manifest path along with a
triple in the run destination to synthesize a Swift Build SDK.
This serves as a fallback for cases where Swift Build cannot
match an SDK for it.

Note that in this first implementation the platform is hard
coded to webassembly, and so only SDK's that align with WASM,
such as static linking of compiler runtime pieces, and some
Unix semantics will work.
@cmcgee1024
Copy link
Member Author

@swift-ci please test

"SWIFT_RESOURCE_DIR": .plString(swiftResourceDir.str), // Resource dir for compiling Swift
"CLANG_RESOURCE_DIR": .plString(clangResourceDir.str), // Resource dir for linking C/C++/Obj-C
"SDKROOT": .plString(sysroot.str),
"OTHER_LDFLAGS": .plArray(["$(OTHER_SWIFT_FLAGS)"]), // The extra swift compiler settings in JSON are intended to go to the linker driver too
Copy link
Collaborator

@owenv owenv Dec 9, 2025

Choose a reason for hiding this comment

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

If we need to include swift flags here, we should use extraSwiftCompilerSettings instead of $(OTHER_SWIFT_FLAGS), because $(OTHER_SWIFT_FLAGS) may include other project/command line flags which are only intended for the compiler

@cmcgee1024
Copy link
Member Author

@swift-ci please test

@cmcgee1024
Copy link
Member Author

@swift-ci please test

Copy link
Collaborator

@jakepetroules jakepetroules left a comment

Choose a reason for hiding this comment

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

Looking good! :)

Left another round of feedback.

{
Name = CLANG_SDKROOT_LINKER_INPUT;
Type = Path;
DefaultValue = "$(SDKROOT)";
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this one needs to be $(SYSROOT:default=$(SDKROOT))

{
Name = SWIFTC_SDKROOT_LINKER_INPUT;
Type = Path;
DefaultValue = "$(SYSROOT:default=$(SDKROOT))";
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't seem right -- based on discussions around how the various platform-targeting flags would work, the consensus was:

  • When targeting platforms where Swift SDK and sysroot are the same (Apple platforms), use -sdk only
  • When targeting Windows, use -sdk only since the sysroot concept doesn't exist
  • For all others, use both -sdk and -sysroot

Wasm falls into bullet 3, so I think the sdk flag needs to be based on $(SDKROOT), while the $(SYSROOT) var would be used to pass -sysroot. That means you should be able to remove this override and use the one from the base Ld.xcspec.

Copy link
Member Author

Choose a reason for hiding this comment

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

The UnixLd.xcspec is getting in the way between Ld.xcspec and this WasmLd.xcspec. If I remove this then it will prevent it from adding the required "-sdk" argument to the linker driver.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The point is that the DefaultValue here is not correct -- SYSROOT should not influence the -sdk flag. Only SDKROOT should. Meaning you need this change:

Suggested change
DefaultValue = "$(SYSROOT:default=$(SDKROOT))";
DefaultValue = "$(SDKROOT)";

and which consequently makes this equivalent to the base definition.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, I've adjusted the WasmLD.xcspec to consider only the SDKROOT, and not the SYSROOT.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You should still remove the SWIFTC_SDKROOT_LINKER_INPUT definition from WasmLd.xcspec though, since it's now identical to the base definition in Ld.xcspec and is therefore a no-op.

Copy link
Member Author

Choose a reason for hiding this comment

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

There's still the interim UnixLd.xcspec that interferes, so this is needed to override that until it is fixed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ahh, I missed that. That is certainly a bit confusing that the Ld.xcspec setting is passing -sdk while the same setting in UnixLd.xcspec is passing -sysroot. Something there seems not quite right; I think we need to have two settings in UnixLd.xcspec, one passing -sdk and one passing -sysroot, since that model applies to all Unix-like platforms (except Apple).

@cmcgee1024
Copy link
Member Author

@swift-ci please test

@cmcgee1024
Copy link
Member Author

@swift-ci please test

Copy link
Collaborator

@owenv owenv left a comment

Choose a reason for hiding this comment

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

This is looking good to me, I think it's pretty much good as a first step to land once we get some basic tests in place. Left one minor comment/question

@cmcgee1024
Copy link
Member Author

@swift-ci please test

Copy link
Collaborator

@jakepetroules jakepetroules left a comment

Choose a reason for hiding this comment

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

Looking pretty good, mostly some style feedback plus a small blocking issue around the Linux triples.

public init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

do {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of do-catch, conditional decoding would be the right way to do this:

if let buildTarget = try container.decodeIfPresent(BuildTarget.self, forKey: .buildTarget) {
    self.buildTarget = buildTarget
} else {
    ...
}


// Some versions of the message don't have this field
do {
self.hostTargetedPlatform = try container.decode(String?.self, forKey: .hostTargetedPlatform)
Copy link
Collaborator

Choose a reason for hiding this comment

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

correct/idiomatic way to decode optionals is decodeIfPresent:

Suggested change
self.hostTargetedPlatform = try container.decode(String?.self, forKey: .hostTargetedPlatform)
self.hostTargetedPlatform = try container.decodeIfPresent(String.self, forKey: .hostTargetedPlatform)

(Also, just to be clear RE "Some versions of the message don't have this field" -- this isn't an Optional solely for backwards compatibility, it's an Optional on purpose because this property is supposed to be nil for most requests)

public init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

do {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comments as in MessageSupport.swift re decodeIfPresent instead of do-catch, and decodeIfPresent vs decode(String?)

// Handle the message payload from earlier versions that didn't have the buildTarget enumeration
let platform = try container.decode(String.self, forKey: .platform)
let sdk: String = try container.decode(String.self, forKey: .sdk)
let sdkVariant: String? = try container.decode(String?.self, forKey: .sdkVariant)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let sdkVariant: String? = try container.decode(String?.self, forKey: .sdkVariant)
let sdkVariant: String? = try container.decodeIfPresent(String.self, forKey: .sdkVariant)

func findPlatform(for triple: String, core: Core) -> Result<Platform,FindPlatformError> {
let llvmTriple = try? LLVMTriple(triple)

guard let llvmTriple else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd do:

let llvmTriple: LLVMTriple
do {
    llvmTriple = try LLVMTriple(triple)
} catch {
    return .failure(.message("\(error)"))
}

The error that LLVMTriple itself throws, contains a little bit richer information which can tell you a bit more about why a triple might be invalid, and will allow that to propagate to the user as LLVMTripleError evolves over time.

}

var platformNames = OrderedSet<String>()
for platformExtension in core.pluginManager.extensions(of: PlatformInfoExtensionPoint.self) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: this is fine from a correctness perspective, but I suggested the compactMap way before because that maintains immutability (let) and the ordering here is never relevant, so you don't really need the OrderedSet. In terms of keeping the error message deterministic you could just call .sorted() on the Set when constructing the string.

func platformName(triple: LLVMTriple) -> String? {
// FIXME this shouldn't be the webassembly platform here
if triple.system == "linux" {
return "webassembly"
Copy link
Collaborator

Choose a reason for hiding this comment

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

blocking: Linux needs to return the Linux platform, not WebAssembly.

Suggested change
return "webassembly"
return "linux"

If you want to focus this PR just on WebAssembly, and not handle Linux at the moment, that's fine too, and you can delete this implementation of func platformName -- but whatever the case we must not create an incorrect association between Linux triples and the WebAssembly platform.


func platformName(triple: LLVMTriple) -> String? {
// FIXME this shouldn't be the webassembly platform here
if triple.system == "linux" {
Copy link
Collaborator

Choose a reason for hiding this comment

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

blocking: You need to check that the triple.environment has a "gnu" prefix or is "musl", since this should not match Android triples.

Suggested change
if triple.system == "linux" {
if triple.system == "linux" && (triple.environment.hasPrefix("gnu") || triple.environment == "musl") {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants