Skip to content

Add support for iOS triples in swift build #6572

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

Closed
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
13 changes: 9 additions & 4 deletions Sources/Basics/Triple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public struct Triple: Encodable, Equatable, Sendable {
public enum OS: String, Encodable, CaseIterable, Sendable {
case darwin
case macOS = "macosx"
case iOS = "ios"
case linux
case windows
case wasi
Expand All @@ -83,11 +84,14 @@ public struct Triple: Encodable, Equatable, Sendable {
public enum ABI: Encodable, Equatable, RawRepresentable, Sendable {
case unknown
case android
case simulator
case other(name: String)

public init?(rawValue: String) {
if rawValue.hasPrefix(ABI.android.rawValue) {
self = .android
} else if rawValue.hasPrefix(ABI.simulator.rawValue) {
self = .simulator
} else if let version = rawValue.firstIndex(where: { $0.isNumber }) {
self = .other(name: String(rawValue[..<version]))
} else {
Expand All @@ -98,6 +102,7 @@ public struct Triple: Encodable, Equatable, Sendable {
public var rawValue: String {
switch self {
case .android: return "android"
case .simulator: return "simulator"
case .other(let name): return name
case .unknown: return "unknown"
}
Expand Down Expand Up @@ -176,7 +181,7 @@ public struct Triple: Encodable, Equatable, Sendable {
switch (vendor, os) {
case (.apple, .noneOS):
return false
case (.apple, _), (_, .macOS), (_, .darwin):
case (.apple, _), (_, .macOS), (_, .iOS), (_, .darwin):
return true
default:
return false
Expand Down Expand Up @@ -273,7 +278,7 @@ extension Triple {
/// The file extension for dynamic libraries (eg. `.dll`, `.so`, or `.dylib`)
public var dynamicLibraryExtension: String {
switch os {
case .darwin, .macOS:
case .darwin, .macOS, .iOS:
return ".dylib"
case .linux, .openbsd:
return ".so"
Expand All @@ -288,7 +293,7 @@ extension Triple {

public var executableExtension: String {
switch os {
case .darwin, .macOS:
case .darwin, .macOS, .iOS:
return ""
case .linux, .openbsd:
return ""
Expand All @@ -309,7 +314,7 @@ extension Triple {
/// The file extension for Foundation-style bundle.
public var nsbundleExtension: String {
switch os {
case .darwin, .macOS:
case .darwin, .macOS, .iOS:
return ".bundle"
default:
// See: https://github.com/apple/swift-corelibs-foundation/blob/master/Docs/FHS%20Bundles.md
Expand Down
6 changes: 3 additions & 3 deletions Sources/Commands/Utilities/TestingSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ enum TestingSupport {
)

// Add the sdk platform path if we have it. If this is not present, we might always end up failing.
let sdkPlatformFrameworksPath = try Destination.sdkPlatformFrameworkPaths()
let sdkPlatformFrameworksPath = try Destination.sdkPlatformPaths(os: .macOS)
// appending since we prefer the user setting (if set) to the one we inject
env.appendPath("DYLD_FRAMEWORK_PATH", value: sdkPlatformFrameworksPath.fwk.pathString)
env.appendPath("DYLD_LIBRARY_PATH", value: sdkPlatformFrameworksPath.lib.pathString)
env.appendPath("DYLD_FRAMEWORK_PATH", value: sdkPlatformFrameworksPath.frameworkPath.pathString)
env.appendPath("DYLD_LIBRARY_PATH", value: sdkPlatformFrameworksPath.libraryPath.pathString)

try TSCBasic.Process.checkNonZeroExit(arguments: args, environment: env)
// Read the temporary file's content.
Expand Down
41 changes: 21 additions & 20 deletions Sources/CoreCommands/SwiftTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -678,31 +678,34 @@ public final class SwiftTool {
component: destinationTriple.platformBuildPathComponent(buildSystem: options.build.buildSystem)
)

var buildFlags = self.options.build.buildFlags
buildFlags.append(destinationToolchain.extraFlags)

return try BuildParameters(
dataPath: dataPath,
configuration: options.build.configuration,
configuration: self.options.build.configuration,
toolchain: destinationToolchain,
destinationTriple: destinationTriple,
flags: options.build.buildFlags,
pkgConfigDirectories: options.locations.pkgConfigDirectories,
architectures: options.build.architectures,
workers: options.build.jobs ?? UInt32(ProcessInfo.processInfo.activeProcessorCount),
shouldLinkStaticSwiftStdlib: options.linker.shouldLinkStaticSwiftStdlib,
flags: buildFlags,
pkgConfigDirectories: self.options.locations.pkgConfigDirectories,
architectures: self.options.build.architectures,
workers: self.options.build.jobs ?? UInt32(ProcessInfo.processInfo.activeProcessorCount),
shouldLinkStaticSwiftStdlib: self.options.linker.shouldLinkStaticSwiftStdlib,
canRenameEntrypointFunctionName: driverSupport.checkSupportedFrontendFlags(
flags: ["entry-point-function-name"], toolchain: destinationToolchain, fileSystem: self.fileSystem
),
sanitizers: options.build.enabledSanitizers,
enableCodeCoverage: false, // set by test commands when appropriate
indexStoreMode: options.build.indexStoreMode.buildParameter,
enableParseableModuleInterfaces: options.build.shouldEnableParseableModuleInterfaces,
emitSwiftModuleSeparately: options.build.emitSwiftModuleSeparately,
useIntegratedSwiftDriver: options.build.useIntegratedSwiftDriver,
useExplicitModuleBuild: options.build.useExplicitModuleBuild,
isXcodeBuildSystemEnabled: options.build.buildSystem == .xcode,
forceTestDiscovery: options.build.enableTestDiscovery, // backwards compatibility, remove with --enable-test-discovery
testEntryPointPath: options.build.testEntryPointPath,
explicitTargetDependencyImportCheckingMode: options.build.explicitTargetDependencyImportCheck.modeParameter,
linkerDeadStrip: options.linker.linkerDeadStrip,
indexStoreMode: self.options.build.indexStoreMode.buildParameter,
enableParseableModuleInterfaces: self.options.build.shouldEnableParseableModuleInterfaces,
emitSwiftModuleSeparately: self.options.build.emitSwiftModuleSeparately,
useIntegratedSwiftDriver: self.options.build.useIntegratedSwiftDriver,
useExplicitModuleBuild: self.options.build.useExplicitModuleBuild,
isXcodeBuildSystemEnabled: self.options.build.buildSystem == .xcode,
forceTestDiscovery: self.options.build.enableTestDiscovery, // backwards compatibility, remove with --enable-test-discovery
testEntryPointPath: self.options.build.testEntryPointPath,
explicitTargetDependencyImportCheckingMode: self.options.build.explicitTargetDependencyImportCheck.modeParameter,
linkerDeadStrip: self.options.linker.linkerDeadStrip,
verboseOutput: self.logLevel <= .info
)
})
Expand Down Expand Up @@ -734,10 +737,8 @@ public final class SwiftTool {
} else {
return .failure(DestinationError.noDestinationsDecoded(customDestination))
}
} else if let triple = options.build.customCompileTriple,
let targetDestination = Destination.defaultDestination(for: triple, host: hostDestination)
{
destination = targetDestination
} else if let triple = options.build.customCompileTriple {
destination = try Destination.defaultDestination(for: triple, host: hostDestination)
} else if let swiftSDKSelector = options.build.swiftSDKSelector {
destination = try SwiftSDKBundle.selectBundle(
fromBundlesAt: sharedSwiftSDKsDirectory,
Expand Down
16 changes: 16 additions & 0 deletions Sources/PackageModel/BuildFlags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,20 @@ public struct BuildFlags: Equatable, Encodable {
self.linkerFlags = linkerFlags
self.xcbuildFlags = xcbuildFlags
}

/// Appends corresponding properties of a different `BuildFlags` value into `self`.
/// - Parameter buildFlags: a `BuildFlags` value to merge flags from.
public mutating func append(_ buildFlags: BuildFlags) {
cCompilerFlags += buildFlags.cCompilerFlags
cxxCompilerFlags += buildFlags.cxxCompilerFlags
swiftCompilerFlags += buildFlags.swiftCompilerFlags
linkerFlags += buildFlags.linkerFlags

if var xcbuildFlags, let newXcbuildFlags = buildFlags.xcbuildFlags {
xcbuildFlags += newXcbuildFlags
self.xcbuildFlags = xcbuildFlags
} else if let xcbuildFlags = buildFlags.xcbuildFlags {
self.xcbuildFlags = xcbuildFlags
}
}
}
141 changes: 111 additions & 30 deletions Sources/PackageModel/Destination.swift
Original file line number Diff line number Diff line change
Expand Up @@ -405,14 +405,7 @@ public struct Destination: Equatable {
sdkPath = value
} else {
// No value in env, so search for it.
let sdkPathStr = try TSCBasic.Process.checkNonZeroExit(
arguments: ["/usr/bin/xcrun", "--sdk", "macosx", "--show-sdk-path"],
environment: environment
).spm_chomp()
guard !sdkPathStr.isEmpty else {
throw DestinationError.invalidInstallation("default SDK not found")
}
sdkPath = try AbsolutePath(validating: sdkPathStr)
sdkPath = try Destination.sdkPlatformPaths(os: .macOS, environment: environment).sdkPath
}
#else
sdkPath = nil
Expand All @@ -422,11 +415,11 @@ public struct Destination: Equatable {
var extraCCFlags: [String] = []
var extraSwiftCFlags: [String] = []
#if os(macOS)
let sdkPaths = try Destination.sdkPlatformFrameworkPaths(environment: environment)
extraCCFlags += ["-F", sdkPaths.fwk.pathString]
extraSwiftCFlags += ["-F", sdkPaths.fwk.pathString]
extraSwiftCFlags += ["-I", sdkPaths.lib.pathString]
extraSwiftCFlags += ["-L", sdkPaths.lib.pathString]
let sdkPaths = try Destination.sdkPlatformPaths(os: .macOS, environment: environment)
extraCCFlags += ["-F", sdkPaths.frameworkPath.pathString]
extraSwiftCFlags += ["-F", sdkPaths.frameworkPath.pathString]
extraSwiftCFlags += ["-I", sdkPaths.libraryPath.pathString]
extraSwiftCFlags += ["-L", sdkPaths.libraryPath.pathString]
#endif

#if !os(Windows)
Expand All @@ -445,43 +438,75 @@ public struct Destination: Equatable {
)
}

/// Returns `macosx` sdk platform framework path.
public static func sdkPlatformFrameworkPaths(
/// Returns sdk platform framework path. Works with Apple platforms
public static func sdkPlatformPaths(
os: Triple.OS,
abi: Triple.ABI? = nil,
environment: EnvironmentVariables = .process()
) throws -> (fwk: AbsolutePath, lib: AbsolutePath) {
if let path = _sdkPlatformFrameworkPath {
return path
) throws -> PlatformSDK {
/// Naive caching will replace old cache each time the paths request for new OS/ABI pair
if let sdkPlatformPaths = _sdkPlatformPaths,
sdkPlatformPaths.os == os,
sdkPlatformPaths.abi == abi {
return sdkPlatformPaths
}

guard let sdkName = sdkName(os: os, abi: abi) else {
throw StringError("Could not provide SDK name for the OS")
}

let platformPath = try TSCBasic.Process.checkNonZeroExit(
arguments: ["/usr/bin/xcrun", "--sdk", "macosx", "--show-sdk-platform-path"],
arguments: ["/usr/bin/xcrun", "--sdk", sdkName, "--show-sdk-platform-path"],
environment: environment
).spm_chomp()

let platformSDKPath = try TSCBasic.Process.checkNonZeroExit(
arguments: ["/usr/bin/xcrun", "--sdk", sdkName, "--show-sdk-path"],
environment: environment
).spm_chomp()

guard !platformPath.isEmpty else {
throw StringError("could not determine SDK platform path")
throw StringError("Could not determine SDK platform path")
}

// For XCTest framework.
let fwk = try AbsolutePath(validating: platformPath).appending(
let framework = try AbsolutePath(validating: platformPath).appending(
components: "Developer", "Library", "Frameworks"
)

// For XCTest Swift library.
let lib = try AbsolutePath(validating: platformPath).appending(
let library = try AbsolutePath(validating: platformPath).appending(
components: "Developer", "usr", "lib"
)

let sdkPlatformFrameworkPath = (fwk, lib)
_sdkPlatformFrameworkPath = sdkPlatformFrameworkPath
return sdkPlatformFrameworkPath
// For building on Apple platforms other than macOS
let sdk = try AbsolutePath(validating: platformSDKPath)

let sdkPlatformPaths = PlatformSDK(
os: os,
abi: abi,
libraryPath: library,
frameworkPath: framework,
sdkPath: sdk
)

_sdkPlatformPaths = sdkPlatformPaths
return sdkPlatformPaths
}

// FIXME: convert this from a tuple to a proper struct with documented properties
/// Cache storage for sdk platform path.
private static var _sdkPlatformFrameworkPath: (fwk: AbsolutePath, lib: AbsolutePath)? = nil
private static var _sdkPlatformPaths: PlatformSDK? = nil

private static func sdkName(os: Triple.OS, abi: Triple.ABI?) -> String? {
switch (os, abi) {
case (.macOS, _): return "macosx"
case (.iOS, .simulator): return "iphonesimulator"
case (.iOS, _): return "iphoneos"
default: return nil
}
}

/// Returns a default destination of a given target environment
public static func defaultDestination(for triple: Triple, host: Destination) -> Destination? {
public static func defaultDestination(for triple: Triple, host: Destination) throws -> Destination {
if triple.isWASI() {
let wasiSysroot = host.toolset.rootPaths.first?
.parentDirectory // usr
Expand All @@ -491,10 +516,57 @@ public struct Destination: Equatable {
toolset: host.toolset,
pathsConfiguration: .init(sdkRootPath: wasiSysroot)
)
} else if triple.isApple() {
return try applePlatformDestination(triple: triple, host: host)
}

throw StringError("Unsupported triple")
}

private static func applePlatformDestination(triple: Triple, host: Destination) throws -> Destination {
guard triple.arch == .arm64 || triple.arch == .x86_64 else {
throw StringError("Unsupported arch for Apple platforms. Supported arch: \([Triple.Arch.arm64.rawValue, Triple.Arch.x86_64.rawValue])")
}

guard triple.osVersion != nil else {
throw StringError("Explicit OS version required when specifying triples for Apple platforms")
}
return nil

if triple.arch == .x86_64 {
switch (triple.os, triple.abi) {
case (.iOS, .simulator): break
case (.macOS, _): break
default: throw StringError("Provided OS and ABI don't support x86_64 arch. x86_64 arch supproted by ios-simulator and macosx")
}
}


let sdkPaths = try Destination.sdkPlatformPaths(os: triple.os, abi: triple.abi)
var extraCCFlags = [String]()
var extraSwiftCFlags = [String]()

extraCCFlags += ["-F", sdkPaths.frameworkPath.pathString]
extraCCFlags += ["-target", triple.tripleString]

extraSwiftCFlags += ["-F", sdkPaths.frameworkPath.pathString]
extraSwiftCFlags += ["-I", sdkPaths.libraryPath.pathString]
extraSwiftCFlags += ["-L", sdkPaths.libraryPath.pathString]
extraSwiftCFlags += ["-target", triple.tripleString]

return Destination(
toolset: .init(
knownTools: [
.cCompiler: .init(extraCLIOptions: extraCCFlags),
.swiftCompiler: .init(extraCLIOptions: extraSwiftCFlags),
],
rootPaths: host.toolset.rootPaths
),
pathsConfiguration: .init(sdkRootPath: sdkPaths.sdkPath)
)
}



/// Propagates toolchain and SDK paths known to the destination to `swiftc` CLI options.
public mutating func applyPathCLIOptions() {
var properties = self.toolset.knownTools[.swiftCompiler] ?? .init(extraCLIOptions: [])
Expand Down Expand Up @@ -748,3 +820,12 @@ struct SerializedDestinationV3: Decodable {
/// Mapping of triple strings to corresponding properties of such run-time triple.
let runTimeTriples: [String: TripleProperties]
}

/// Represetns platform sdk metadata including paths to libraries, sdk and specifies the platform details
public struct PlatformSDK {
public let os: Triple.OS
public let abi: Triple.ABI?
public let libraryPath: AbsolutePath
public let frameworkPath: AbsolutePath
public let sdkPath: AbsolutePath
}
17 changes: 16 additions & 1 deletion Sources/SPMBuildCore/BinaryTarget+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ extension BinaryTarget {
// FIXME: this filter needs to become more sophisticated
guard let library = metadata.libraries.first(where: {
$0.platform == triple.os.asXCFrameworkPlatformString &&
$0.architectures.contains(triple.arch.rawValue)
$0.platformVariant == triple.abi.asXCFrameworkPlatformVariantString &&
$0.architectures.contains(triple.arch.rawValue)
}) else {
return []
}
Expand Down Expand Up @@ -98,8 +99,22 @@ extension Triple.OS {
switch self {
case .darwin, .linux, .wasi, .windows, .openbsd, .noneOS:
return nil // XCFrameworks do not support any of these platforms today.
case .iOS:
return "ios"
case .macOS:
return "macos"
}
}
}

extension Triple.ABI {
/// Returns a representation of the receiver that can be compared with platform variant strings declared in an XCFramework.
fileprivate var asXCFrameworkPlatformVariantString: String? {
switch self {
case .unknown, .other, .android:
return nil // XCFrameworks do not support any of these variants today.
case .simulator:
return "simulator"
}
}
}
Loading