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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// swift-tools-version:6.0

import PackageDescription

let package = Package(
name: "SwiftFramework",
products: [
.library(name: "SwiftFramework", type: .dynamic, targets: ["SwiftFramework"]),
],
targets: [
.target(
name: "SwiftFramework",
swiftSettings: [.unsafeFlags(["-enable-library-evolution"])]
),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
public enum SwiftFrameworkWithEvolution
{
case v1
case v2

public
static var latest:Self { .v2 }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// swift-tools-version:6.0

import PackageDescription

let package = Package(name: "TestBinary",
products: [
.executable(name: "TestBinary", targets: ["TestBinary"]),
],
targets: [
.binaryTarget(name: "SwiftFramework", path: "SwiftFramework.xcframework"),
.executableTarget(name: "TestBinary",
dependencies: [
.target(name: "SwiftFramework"),
]
),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import SwiftFramework

print("Latest Framework with LibraryEvolution version: \(SwiftFrameworkWithEvolution.latest)")
6 changes: 5 additions & 1 deletion Sources/Build/BuildPlan/BuildPlan+Clang.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ extension BuildPlan {
clangTarget.libraryBinaryPaths.insert(library.libraryPath)
}
case .xcframework:
let libraries = try self.parseXCFramework(for: target, triple: clangTarget.buildParameters.triple)
let libraries = try self.parseXCFramework(
for: target,
triple: clangTarget.buildParameters.triple,
enableXCFrameworksOnLinux: clangTarget.buildParameters.enableXCFrameworksOnLinux
)
for library in libraries {
library.headersPaths.forEach {
clangTarget.additionalFlags += ["-I", $0.pathString]
Expand Down
3 changes: 2 additions & 1 deletion Sources/Build/BuildPlan/BuildPlan+Product.swift
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,8 @@ extension BuildPlan {
case .xcframework:
let libraries = try self.parseXCFramework(
for: binaryTarget,
triple: productDescription.buildParameters.triple
triple: productDescription.buildParameters.triple,
enableXCFrameworksOnLinux: productDescription.buildParameters.enableXCFrameworksOnLinux
)
for library in libraries {
libraryBinaryPaths.insert(library.libraryPath)
Expand Down
6 changes: 5 additions & 1 deletion Sources/Build/BuildPlan/BuildPlan+Swift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ extension BuildPlan {
swiftTarget.libraryBinaryPaths.insert(library.libraryPath)
}
case .xcframework:
let libraries = try self.parseXCFramework(for: target, triple: swiftTarget.buildParameters.triple)
let libraries = try self.parseXCFramework(
for: target,
triple: swiftTarget.buildParameters.triple,
enableXCFrameworksOnLinux: swiftTarget.buildParameters.enableXCFrameworksOnLinux
)
for library in libraries {
library.headersPaths.forEach {
swiftTarget.additionalFlags += ["-I", $0.pathString, "-Xcc", "-I", "-Xcc", $0.pathString]
Expand Down
7 changes: 5 additions & 2 deletions Sources/Build/BuildPlan/BuildPlan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -653,9 +653,12 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
}

/// Extracts the library information from an XCFramework.
func parseXCFramework(for binaryTarget: BinaryModule, triple: Basics.Triple) throws -> [LibraryInfo] {
func parseXCFramework(for binaryTarget: BinaryModule, triple: Basics.Triple, enableXCFrameworksOnLinux: Bool) throws -> [LibraryInfo] {
try self.externalLibrariesCache.memoize(key: binaryTarget) {
try binaryTarget.parseXCFrameworks(for: triple, fileSystem: self.fileSystem)
if !enableXCFrameworksOnLinux && triple.os == .linux {
return []
}
return try binaryTarget.parseXCFrameworks(for: triple, fileSystem: self.fileSystem)
}
}

Expand Down
7 changes: 7 additions & 0 deletions Sources/CoreCommands/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,13 @@ public struct BuildOptions: ParsableArguments {
@Flag(name: .customLong("experimental-prepare-for-indexing-no-lazy"), help: .hidden)
var prepareForIndexingNoLazy: Bool = false

/// Hidden option to allow XCFrameworks on Linux
@Flag(
name: .customLong("experimental-xcframeworks-on-linux"),
help: .hidden
)
public var enableXCFrameworksOnLinux: Bool = false

/// Whether to enable generation of `.swiftinterface`s alongside `.swiftmodule`s.
@Flag(name: .customLong("enable-parseable-module-interfaces"))
public var shouldEnableParseableModuleInterfaces: Bool = false
Expand Down
1 change: 1 addition & 0 deletions Sources/CoreCommands/SwiftCommandState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,7 @@ public final class SwiftCommandState {
sanitizers: options.build.enabledSanitizers,
indexStoreMode: options.build.indexStoreMode.buildParameter,
prepareForIndexing: prepareForIndexingMode,
enableXCFrameworksOnLinux: options.build.enableXCFrameworksOnLinux,
debuggingParameters: .init(
debugInfoFormat: self.options.build.debugInfoFormat.buildParameter,
triple: triple,
Expand Down
13 changes: 8 additions & 5 deletions Sources/SPMBuildCore/BinaryTarget+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extension BinaryModule {
let metadata = try XCFrameworkMetadata.parse(fileSystem: fileSystem, rootPath: self.artifactPath)
// Filter the libraries that are relevant to the triple.
guard let library = metadata.libraries.first(where: {
$0.platform == triple.os?.asXCFrameworkPlatformString &&
$0.platform == triple.asXCFrameworkPlatformString &&
$0.variant == triple.environment?.asXCFrameworkPlatformVariantString &&
$0.architectures.contains(triple.archName)
}) else {
Expand Down Expand Up @@ -117,13 +117,11 @@ extension Triple {
return self
}
}
}

extension Triple.OS {
/// Returns a representation of the receiver that can be compared with platform strings declared in an XCFramework.
fileprivate var asXCFrameworkPlatformString: String? {
switch self {
case .darwin, .linux, .wasi, .win32, .openbsd, .freebsd, .noneOS:
switch self.os {
case .darwin, .wasi, .win32, .openbsd, .freebsd, .noneOS:
return nil // XCFrameworks do not support any of these platforms today.
case .macosx:
return "macos"
Expand All @@ -133,6 +131,11 @@ extension Triple.OS {
return "tvos"
case .watchos:
return "watchos"
case .linux:
if environment == .android {
return nil
}
return "linux" // Only if --experimental-xcframeworks-on-linux has been passed
default:
return nil // XCFrameworks do not support any of these platforms today.
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/SPMBuildCore/BuildParameters/BuildParameters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ public struct BuildParameters: Encodable {
/// Do minimal build to prepare for indexing
public var prepareForIndexing: PrepareForIndexingMode

/// Support Experimental XCF on Linux
public var enableXCFrameworksOnLinux: Bool

/// Build parameters related to debugging.
public var debuggingParameters: Debugging

Expand Down Expand Up @@ -166,6 +169,7 @@ public struct BuildParameters: Encodable {
indexStoreMode: IndexStoreMode = .auto,
shouldSkipBuilding: Bool = false,
prepareForIndexing: PrepareForIndexingMode = .off,
enableXCFrameworksOnLinux: Bool = false,
debuggingParameters: Debugging? = nil,
driverParameters: Driver = .init(),
linkingParameters: Linking = .init(),
Expand Down Expand Up @@ -229,6 +233,7 @@ public struct BuildParameters: Encodable {
self.indexStoreMode = indexStoreMode
self.shouldSkipBuilding = shouldSkipBuilding
self.prepareForIndexing = prepareForIndexing
self.enableXCFrameworksOnLinux = enableXCFrameworksOnLinux
self.driverParameters = driverParameters
self.linkingParameters = linkingParameters
self.outputParameters = outputParameters
Expand Down
7 changes: 5 additions & 2 deletions Sources/_InternalBuildTestSupport/MockBuildTestHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public func mockBuildPlan(
commonFlags: PackageModel.BuildFlags = .init(),
indexStoreMode: BuildParameters.IndexStoreMode = .off,
omitFramePointers: Bool? = nil,
enableXCFrameworksOnLinux: Bool = false,
driverParameters: BuildParameters.Driver = .init(),
linkingParameters: BuildParameters.Linking = .init(),
targetSanitizers: EnabledSanitizers = .init(),
Expand Down Expand Up @@ -105,7 +106,8 @@ public func mockBuildPlan(
toolchain: toolchain,
flags: commonFlags,
triple: inferredTriple,
indexStoreMode: indexStoreMode
indexStoreMode: indexStoreMode,
enableXCFrameworksOnLinux: enableXCFrameworksOnLinux
)
destinationParameters.debuggingParameters = commonDebuggingParameters
destinationParameters.driverParameters = driverParameters
Expand All @@ -119,7 +121,8 @@ public func mockBuildPlan(
toolchain: toolchain,
flags: commonFlags,
triple: inferredTriple,
indexStoreMode: indexStoreMode
indexStoreMode: indexStoreMode,
enableXCFrameworksOnLinux: enableXCFrameworksOnLinux
)
hostParameters.debuggingParameters = commonDebuggingParameters
hostParameters.driverParameters = driverParameters
Expand Down
2 changes: 2 additions & 0 deletions Sources/_InternalTestSupport/MockBuildTestHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public func mockBuildParameters(
linkerDeadStrip: Bool = true,
linkTimeOptimizationMode: BuildParameters.LinkTimeOptimizationMode? = nil,
omitFramePointers: Bool? = nil,
enableXCFrameworksOnLinux: Bool = false,
prepareForIndexing: BuildParameters.PrepareForIndexingMode = .off
) -> BuildParameters {
try! BuildParameters(
Expand All @@ -105,6 +106,7 @@ public func mockBuildParameters(
workers: 3,
indexStoreMode: indexStoreMode,
prepareForIndexing: prepareForIndexing,
enableXCFrameworksOnLinux: enableXCFrameworksOnLinux,
debuggingParameters: .init(
triple: triple,
shouldEnableDebuggingEntitlement: config == .debug,
Expand Down
114 changes: 114 additions & 0 deletions Tests/BuildTests/BuildPlanTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6807,6 +6807,114 @@ class BuildPlanTestCase: BuildSystemProviderTestCase {
XCTAssertMatch(dynamicLibraryPathExtension, "dylib")
}

func testXCFrameworkBinaryTargetsLinux(platform: String = "linux", arch: String, targetTriple: Basics.Triple) async throws {
let Pkg: AbsolutePath = "/Pkg"

let fs = InMemoryFileSystem(
emptyFiles:
Pkg.appending(components: "Sources", "exe", "main.swift").pathString,
Pkg.appending(components: "Sources", "Library", "Library.swift").pathString
)

try! fs.createDirectory("/Pkg/Framework.xcframework", recursive: true)
try! fs.writeFileContents(
"/Pkg/Framework.xcframework/Info.plist",
string: """
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>\(platform)-\(arch)</string>
<key>LibraryPath</key>
<string>Framework.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>\(arch)</string>
</array>
<key>SupportedPlatform</key>
<string>\(platform)</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>
"""
)

let observability = ObservabilitySystem.makeForTesting()

let graph = try loadModulesGraph(
fileSystem: fs,
manifests: [
Manifest.createRootManifest(
displayName: "Pkg",
path: .init(validating: Pkg.pathString),
products: [
ProductDescription(name: "exe", type: .executable, targets: ["exe"]),
ProductDescription(name: "Library", type: .library(.dynamic), targets: ["Library"]),
],
targets: [
TargetDescription(name: "exe", dependencies: ["Library"]),
TargetDescription(name: "Library", dependencies: ["Framework"]),
TargetDescription(name: "Framework", path: "Framework.xcframework", type: .binary),
]
),
],
binaryArtifacts: [
.plain("pkg"): [
"Framework": .init(kind: .xcframework, originURL: nil, path: "/Pkg/Framework.xcframework"),
],
],
observabilityScope: observability.topScope
)
XCTAssertNoDiagnostics(observability.diagnostics)

let result = try await BuildPlanResult(plan: mockBuildPlan(
triple: targetTriple,
graph: graph,
enableXCFrameworksOnLinux: true,
fileSystem: fs,
observabilityScope: observability.topScope
))
XCTAssertNoDiagnostics(observability.diagnostics)

result.checkProductsCount(2)
result.checkTargetsCount(2)

let buildPath = result.plan.productsBuildPath

let libraryBasicArguments = try result.moduleBuildDescription(for: "Library").swift().compileArguments()
XCTAssertMatch(
libraryBasicArguments,
[.anySequence, "-I", "\(Pkg.appending(components: "Framework.xcframework", "\(platform)-\(arch)"))", .anySequence]
)

let libraryLinkArguments = try result.buildProduct(for: "Library").linkArguments()
XCTAssertMatch(libraryLinkArguments, [.anySequence, "-L", "\(buildPath)", .anySequence])

let exeCompileArguments = try result.moduleBuildDescription(for: "exe").swift().compileArguments()
XCTAssertMatch(
exeCompileArguments,
[.anySequence, "-I", "\(Pkg.appending(components: "Framework.xcframework", "\(platform)-\(arch)"))", .anySequence]
)

let exeLinkArguments = try result.buildProduct(for: "exe").linkArguments()
XCTAssertMatch(exeLinkArguments, [.anySequence, "-L", "\(buildPath)", .anySequence])

let executablePathExtension = try result.buildProduct(for: "exe").binaryPath.extension ?? ""
XCTAssertMatch(executablePathExtension, "")

let dynamicLibraryPathExtension = try result.buildProduct(for: "Library").binaryPath.extension
XCTAssertMatch(dynamicLibraryPathExtension, "so")
}

func testXCFrameworkBinaryTargets() async throws {
try await self.testXCFrameworkBinaryTargets(platform: "macos", arch: "x86_64", targetTriple: .x86_64MacOS)

Expand All @@ -6815,6 +6923,12 @@ class BuildPlanTestCase: BuildSystemProviderTestCase {

let arm64eTriple = try Basics.Triple("arm64e-apple-macosx")
try await self.testXCFrameworkBinaryTargets(platform: "macos", arch: "arm64e", targetTriple: arm64eTriple)

let x86_64Linux = try Basics.Triple("x86_64-unknown-linux-gnu")
try await self.testXCFrameworkBinaryTargetsLinux(arch: "x86_64", targetTriple: x86_64Linux)

let aarch64Linux = try Basics.Triple("aarch64-unknown-linux-gnu")
try await self.testXCFrameworkBinaryTargetsLinux(arch: "aarch64", targetTriple: aarch64Linux)
}

func testArtifactsArchiveBinaryTargets(
Expand Down
Loading