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
93 changes: 93 additions & 0 deletions Sources/SWBCore/SDKRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,9 @@ public protocol SDKRegistryLookup: Sendable {
/// - returns: The found `SDK`, or `nil` if no SDK with the given name could be found or `name` was in an invalid format.
func lookup(_ name: String, activeRunDestination: RunDestinationInfo?) throws -> SDK?

/// Synthesize an SDK for the given platform with the given manifest JSON file path
func synthesizedSDK(platform: Platform, sdkManifestPath: String, triple: String) throws -> SDK?

/// Look up the SDK with the given path. If the registry is immutable, then this will only return the SDK if it was loaded when the registry was created; only mutable registries can discover and load new SDKs after that point.
/// - parameter path: Absolute path of the SDK to look up.
/// - returns: The found `SDK`, or `nil` if no SDK was found at the given path.
Expand Down Expand Up @@ -1153,6 +1156,96 @@ public final class SDKRegistry: SDKRegistryLookup, CustomStringConvertible, Send
return sdk
}

public func synthesizedSDK(platform: Platform, sdkManifestPath: String, triple: String) throws -> SDK? {
// Let's check the active run destination to see if there's an SDK path that we should be using
let llvmTriple = try LLVMTriple(triple)

let host = hostOperatingSystem

// Don't allow re-registering the same SDK
if let existing = sdksByPath[Path(sdkManifestPath)] {
return existing
}

if let swiftSDK = try SwiftSDK(identifier: sdkManifestPath, version: "1.0.0", path: Path(sdkManifestPath), fs: localFS) {
let defaultProperties: [String: PropertyListItem] = [
"SDK_STAT_CACHE_ENABLE": "NO",

"GENERATE_TEXT_BASED_STUBS": "NO",
"GENERATE_INTERMEDIATE_TEXT_BASED_STUBS": "NO",

// TODO check how this impacts operation on Windows
"CHOWN": "/usr/bin/chown",
Copy link
Collaborator

Choose a reason for hiding this comment

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

No such concept of CHOWN, so shouldn't matter. addSpecificChangePermissionTasks bails out early for Windows targets.


// TODO are these going to be appropriate for all kinds of SDK's?
// SwiftSDK _could_ have tool entries for these, so use them if they are available
"LIBTOOL": .plString(host.imageFormat.executableName(basename: "llvm-lib")),
"AR": .plString(host.imageFormat.executableName(basename: "llvm-ar")),
]

for (sdkTriple, tripleProperties) in swiftSDK.targetTriples {
guard triple == sdkTriple else {
continue
}

let toolsets = try tripleProperties.loadToolsets(sdk: swiftSDK, fs: localFS)

let sysroot = swiftSDK.path.join(tripleProperties.sdkRootPath)

// TODO support dynamic resources path
let swiftResourceDir = swiftSDK.path.join(tripleProperties.swiftStaticResourcesPath)
let clangResourceDir = swiftSDK.path.join(tripleProperties.clangStaticResourcesPath)

let tripleSystem = llvmTriple.system + (llvmTriple.systemVersion?.description ?? "")

// TODO handle tripleProperties.toolSearchPaths

let extraSwiftCompilerSettings = Array(toolsets.map( { $0.swiftCompiler?.extraCLIOptions ?? [] }).flatMap( { $0 }))
let headerSearchPaths: [PropertyListItem] = ["$(inherited)"] + (tripleProperties.includeSearchPaths ?? []).map( { PropertyListItem.plString($0) } )
let librarySearchPaths: [PropertyListItem] = ["$(inherited)"] + (tripleProperties.librarySearchPaths ?? []).map( { PropertyListItem.plString($0) } )

let sdk = registerSDK(sysroot, sysroot, platform, .plDict([
"Type": .plString("SDK"),
"Version": .plString(swiftSDK.version),
"CanonicalName": .plString(swiftSDK.identifier),
"Aliases": [],
"IsBaseSDK": .plBool(true),
"DefaultProperties": .plDict([
"PLATFORM_NAME": .plString(platform.name),
].merging(defaultProperties, uniquingKeysWith: { _, new in new })),
"CustomProperties": .plDict([
"LIBRARY_SEARCH_PATHS": .plArray(librarySearchPaths),
"HEADER_SEARCH_PATHS": .plArray(headerSearchPaths),
"OTHER_SWIFT_FLAGS": .plArray(["$(inherited)"] + extraSwiftCompilerSettings.map( {.plString($0)} )),
"SWIFTC_RESOURCE_DIR": .plString(swiftResourceDir.str), // Resource dir for linking Swift
"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(["$(inherited)"] + extraSwiftCompilerSettings.map( {.plString($0)} )), // The extra swift compiler settings in JSON are intended to go to the linker driver too
]),
"SupportedTargets": .plDict([
platform.name: .plDict([
"Archs": .plArray([.plString(llvmTriple.arch)]),
"LLVMTargetTripleEnvironment": .plString(llvmTriple.environment ?? ""),
"LLVMTargetTripleSys": .plString(tripleSystem),
"LLVMTargetTripleVendor": .plString(llvmTriple.vendor),
])
]),
// TODO: Leave compatible toolchain information in Swift SDKs
// "Toolchains": .plArray([])
]))

if let sdk {
try sdk.loadExtendedInfo(delegate.namespace)
sdksByPath[Path(sdkManifestPath)] = sdk
return sdk
}
}
}

return nil
}

public func lookup(nameOrPath key: String, basePath: Path, activeRunDestination: RunDestinationInfo?) throws -> SDK? {
#if !os(Windows)
// TODO: Turn this validation back on once our path handling is cleaned up a bit more
Expand Down
84 changes: 67 additions & 17 deletions Sources/SWBCore/Settings/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1803,11 +1803,24 @@ private class SettingsBuilder: ProjectMatchLookup {
}

do {
sdk = try project.map { try sdkRegistry.lookup(nameOrPath: sdkroot, basePath: $0.sourceRoot, activeRunDestination: parameters.activeRunDestination) } ?? nil
sdk = try project.map {
switch parameters.activeRunDestination?.buildTarget {
case let .swiftSDK(sdkManifestPath: sdkManifestPath, triple: triple):
// FIXME platform should be decided by the triple
guard let platform = core.platformRegistry.lookup(name: "webassembly") else {
errors.append("unable to find platform 'webassembly'")
return nil
}
return try sdkRegistry.synthesizedSDK(platform: platform, sdkManifestPath: sdkManifestPath, triple: triple)
default:
return try sdkRegistry.lookup(nameOrPath: sdkroot, basePath: $0.sourceRoot, activeRunDestination: parameters.activeRunDestination)
}
} ?? nil
} catch {
sdk = nil
sdkLookupErrors.append(error)
}

if let s = sdk {
// Evaluate the SDK variant, if there is one.
let sdkVariantName: String
Expand Down Expand Up @@ -3512,23 +3525,46 @@ private class SettingsBuilder: ProjectMatchLookup {

// Destination info: since runDestination.{platform,sdk} were set by the IDE, we expect them to resolve in Swift Build correctly
guard let runDestination = self.parameters.activeRunDestination else { return }
guard let destinationPlatform: Platform = self.core.platformRegistry.lookup(name: runDestination.platform) else {
self.errors.append("unable to resolve run destination platform: '\(runDestination.platform)'")
return
}

let destinationPlatform: Platform
let destinationSDK: SDK
do {
guard let sdk = try sdkRegistry.lookup(runDestination.sdk, activeRunDestination: runDestination) else {
self.errors.append("unable to resolve run destination SDK: '\(runDestination.sdk)'")
switch runDestination.buildTarget {
case let .swiftSDK(sdkManifestPath: sdkManifestPath, triple: triple):
// FIXME: the platform should be determined using the triple
guard let platform = self.core.platformRegistry.lookup(name: "webassembly") else {
self.errors.append("unable to resolve run destination platform: 'webassembly'")
return
}

destinationPlatform = platform

guard let sdk = try? sdkRegistry.synthesizedSDK(platform: platform, sdkManifestPath: sdkManifestPath, triple: triple) else {
self.errors.append("unable to synthesize SDK for Swift SDK build target: '\(runDestination.buildTarget)'")
return
}
destinationSDK = sdk
} catch let error as AmbiguousSDKLookupError {
self.diagnostics.append(error.diagnostic)
return
} catch {
self.errors.append("\(error)")
return
default:
guard let platform = self.core.platformRegistry.lookup(name: runDestination.platform) else {
self.errors.append("unable to resolve run destination platform: '\(runDestination.platform)'")
return
}

destinationPlatform = platform

do {
if let sdk = try sdkRegistry.lookup(runDestination.sdk, activeRunDestination: runDestination) {
destinationSDK = sdk
} else {
self.errors.append("unable to resolve run destination SDK: '\(runDestination.sdk)'")
return
}
} catch let error as AmbiguousSDKLookupError {
self.diagnostics.append(error.diagnostic)
return
} catch {
self.errors.append("\(error)")
return
}
}

let destinationPlatformIsMacOS = destinationPlatform.name == "macosx"
Expand Down Expand Up @@ -3639,9 +3675,23 @@ private class SettingsBuilder: ProjectMatchLookup {

// Destination info: since runDestination.{platform,sdk} were set by the IDE, we expect them to resolve in Swift Build correctly
guard let runDestination = self.parameters.activeRunDestination else { return }
guard let destinationPlatform: Platform = self.core.platformRegistry.lookup(name: runDestination.platform) else {
self.errors.append("unable to resolve run destination platform: '\(runDestination.platform)'")
return

let destinationPlatform: Platform

switch runDestination.buildTarget {
case .swiftSDK:
// TODO use the triple to decide the platform, or use a fallback
guard let platform: Platform = self.core.platformRegistry.lookup(name: "webassembly") else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's fix this hardcoded webassembly reference before landing the initial change. Paraphrasing what I mentioned in another comment, a good way to do this would be to additional method on PlatformInfoExtension like func platformName(triple: LLVMTriple) -> String?.

Then, in each of the implementations:

  • AndroidPlatformExtension: return "android" if triple.os == "linux" and triple.environment has prefix "android"
  • GenericUnixPlatformInfoExtension: return triple.os if (triple.os is "linux" and (triple.environment has prefix "gnu" or == "musl")), "freebsd", or "openbsd"
  • QNXPlatformExtension: return "qnx" if triple.os == "nto"
  • WebAssemblyPlatformExtension: return "webassembly" if triple.os has prefix "wasi"
  • WindowsPlatformExtension: return "windows" if triple.os == "windows

Here in Settings.swift, you enumerate the extension point conformances, add them to a Set, and if you have exactly one match, use it, else emit an error.

If you get no matches at all, fall back to the "none" platform.

--

Also make sure to factor this logic into a dedicated function; I see the hardcoded webassembly lookup is currently in 3 different places.

self.errors.append("unable to resolve run destination platform: '\(runDestination.platform)'")
return
}
destinationPlatform = platform
default:
guard let platform: Platform = self.core.platformRegistry.lookup(name: runDestination.platform) else {
self.errors.append("unable to resolve run destination platform: '\(runDestination.platform)'")
return
}
destinationPlatform = platform
}

// Target info
Expand Down
52 changes: 41 additions & 11 deletions Sources/SWBCore/SwiftSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,53 @@ public struct SwiftSDK: Sendable {
public var sdkRootPath: String
public var swiftResourcesPath: String?
public var swiftStaticResourcesPath: String?
public var clangResourcesPath: String? {
guard let swiftResourcesPath = self.swiftResourcesPath else {
return nil
}

// The clang resource path is conventionally the clang subdirectory of the swift resource path
return Path(swiftResourcesPath).join("clang").str
}
public var clangStaticResourcesPath: String? {
guard let swiftResourcesPath = self.swiftStaticResourcesPath else {
return nil
}

// The clang resource path is conventionally the clang subdirectory of the swift resource path
return Path(swiftResourcesPath).join("clang").str
}
public var includeSearchPaths: [String]?
public var librarySearchPaths: [String]?
public var toolsetPaths: [String]?

public func loadToolsets(sdk: SwiftSDK, fs: any FSProxy) throws -> [Toolset] {
var toolsets: [Toolset] = []

for toolsetPath in self.toolsetPaths ?? [] {
let metadataData = try Data(fs.read(sdk.path.join(toolsetPath)))

let schema = try JSONDecoder().decode(SchemaVersionInfo.self, from: metadataData)
guard schema.schemaVersion == "1.0" else { return [] } // FIXME throw an error

let toolset = try JSONDecoder().decode(Toolset.self, from: metadataData)
toolsets.append(toolset)
}

return toolsets
}
}
struct MetadataV4: Codable {
let targetTriples: [String: TripleProperties]
}

struct Toolset: Codable {
struct ToolProperties: Codable {
var path: String?
var extraCLIOptions: [String]
public struct Toolset: Codable {
public struct Tool: Codable {
public let extraCLIOptions: [String]?
}

var knownTools: [String: ToolProperties] = [:]
var rootPaths: [String] = []
public let rootPath: String
public let swiftCompiler: Tool?
}

/// The identifier of the artifact bundle containing this SDK.
Expand All @@ -55,12 +86,11 @@ public struct SwiftSDK: Sendable {
init?(identifier: String, version: String, path: Path, fs: any FSProxy) throws {
self.identifier = identifier
self.version = version
self.path = path
self.path = path.dirname

let metadataPath = path.join("swift-sdk.json")
guard fs.exists(metadataPath) else { return nil }
guard fs.exists(path) else { return nil }

let metadataData = try Data(fs.read(metadataPath))
let metadataData = try Data(fs.read(path))
let schema = try JSONDecoder().decode(SchemaVersionInfo.self, from: metadataData)
guard schema.schemaVersion == "4.0" else { return nil }

Expand Down Expand Up @@ -118,7 +148,7 @@ public struct SwiftSDK: Sendable {
}

/// Find Swift SDKs in an artifact bundle supporting one of the given targets.
private static func findSDKs(artifactBundle: Path, targetTriples: [String]?, fs: any FSProxy) throws -> [SwiftSDK] {
public static func findSDKs(artifactBundle: Path, targetTriples: [String]?, fs: any FSProxy) throws -> [SwiftSDK] {
// Load info.json from the artifact bundle
let infoPath = artifactBundle.join("info.json")
guard try fs.isFile(infoPath) else { return [] }
Expand Down
8 changes: 8 additions & 0 deletions Sources/SWBCore/WorkspaceContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ public struct WorkspaceContextSDKRegistry: SDKRegistryLookup, Sendable {
public func lookup(nameOrPath: String, basePath: Path, activeRunDestination: RunDestinationInfo?) throws -> SDK? {
return try lookupInEach { try $0.lookup(nameOrPath: nameOrPath, basePath: basePath, activeRunDestination: activeRunDestination) }
}

func synthesizedSDK(platform: Platform, sdkManifestPath: String, triple: String) throws -> SDK? {
return try lookupInEach { try $0.synthesizedSDK(platform: platform, sdkManifestPath: sdkManifestPath, triple: triple) }
}
}

@_spi(Testing) public init(coreSDKRegistry: SDKRegistry, delegate: any SDKRegistryDelegate, userNamespace: MacroNamespace, overridingSDKsDir: Path?) {
Expand Down Expand Up @@ -246,6 +250,10 @@ public struct WorkspaceContextSDKRegistry: SDKRegistryLookup, Sendable {
public func lookup(nameOrPath: String, basePath: Path, activeRunDestination: RunDestinationInfo?) throws -> SDK? {
return try underlyingLookup.lookup(nameOrPath: nameOrPath, basePath: basePath, activeRunDestination: activeRunDestination)
}

public func synthesizedSDK(platform: Platform, sdkManifestPath: String, triple: String) throws -> SDK? {
return try underlyingLookup.synthesizedSDK(platform: platform, sdkManifestPath: sdkManifestPath, triple: triple)
}
}

/// Wrapper for information needed to use a workspace.
Expand Down
23 changes: 21 additions & 2 deletions Sources/SWBProtocol/MessageSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,16 @@ public struct BuildParametersMessagePayload: SerializableCodable, Equatable, Sen
}

public struct RunDestinationInfo: SerializableCodable, Hashable, Sendable {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is not part of public API and there is no need to maintain backwards compatibility, so this should eliminate the dedicated properties and go straight to having a BuildTarget enum with the two cases (swiftSDK for swiftSdkPath + triple, and appleSDK for platform + sdk + sdkVariant).

public var disableOnlyActiveArch: Bool
public var hostTargetedPlatform: String?

public var buildTarget: BuildTarget?

public var platform: String
public var sdk: String
public var sdkVariant: String?
public var targetArchitecture: String
public var supportedArchitectures: OrderedSet<String>
public var disableOnlyActiveArch: Bool
public var hostTargetedPlatform: String?

public init(platform: String, sdk: String, sdkVariant: String?, targetArchitecture: String, supportedArchitectures: OrderedSet<String>, disableOnlyActiveArch: Bool, hostTargetedPlatform: String?) {
self.platform = platform
Expand All @@ -334,7 +337,23 @@ public struct RunDestinationInfo: SerializableCodable, Hashable, Sendable {
self.supportedArchitectures = supportedArchitectures
self.disableOnlyActiveArch = disableOnlyActiveArch
self.hostTargetedPlatform = hostTargetedPlatform
self.buildTarget = nil
}

public init(buildTarget: BuildTarget, disableOnlyActiveArch: Bool, hostTargetedPlatform: String? = nil) {
self.platform = ""
self.sdk = ""
self.sdkVariant = nil
self.targetArchitecture = ""
self.supportedArchitectures = []
self.buildTarget = buildTarget
self.disableOnlyActiveArch = disableOnlyActiveArch
self.hostTargetedPlatform = hostTargetedPlatform
}
}

public enum BuildTarget: SerializableCodable, Hashable, Sendable {
case swiftSDK(sdkManifestPath: String, triple: String)
}

/// The arena info being sent in a Message.
Expand Down
21 changes: 21 additions & 0 deletions Sources/SWBUniversalPlatform/Specs/Ld.xcspec
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,27 @@
CommandLineFlag = "-sdk";
IsInputDependency = Yes;
},
{
Name = CLANG_RESOURCE_DIR;
Type = Path;
Condition = "$(LINKER_DRIVER) == clang";
CommandLineFlag = "-resource-dir";
IsInputDependency = Yes;
},
{
Name = CLANG_RESOURCE_DIR;
Type = Path;
Condition = "$(LINKER_DRIVER) == swiftc";
CommandLineArgs = ("-Xclang-linker", "-resource-dir", "-Xclang-linker", "$(CLANG_RESOURCE_DIR)");
IsInputDependency = Yes;
},
{
Name = SWIFTC_RESOURCE_DIR;
Type = Path;
Condition = "$(LINKER_DRIVER) == swiftc";
CommandLineArgs = ("-resource-dir", "$(SWIFTC_RESOURCE_DIR)");
IsInputDependency = Yes;
},
{
Name = "LD_OPTIMIZATION_LEVEL";
Type = String;
Expand Down
Loading
Loading