Skip to content
Merged
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
37 changes: 24 additions & 13 deletions Sources/Build/BuildPlan/BuildPlan+Product.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import struct PackageGraph.ResolvedProduct
import struct PackageGraph.ResolvedTarget
import class PackageModel.BinaryTarget
import class PackageModel.ClangTarget

@_spi(SwiftPMInternal)
import class PackageModel.Target

import class PackageModel.SwiftTarget
import class PackageModel.SystemLibraryTarget
import struct SPMBuildCore.BuildParameters
Expand All @@ -28,7 +31,10 @@ extension BuildPlan {
/// Plan a product.
func plan(buildProduct: ProductBuildDescription) throws {
// Compute the product's dependency.
let dependencies = try computeDependencies(of: buildProduct.product, buildParameters: buildProduct.buildParameters)
let dependencies = try computeDependencies(
of: buildProduct.product,
buildParameters: buildProduct.buildParameters
)

// Add flags for system targets.
for systemModule in dependencies.systemModules {
Expand All @@ -50,19 +56,24 @@ extension BuildPlan {
}
}

// Link C++ if needed.
// Note: This will come from build settings in future.
for target in dependencies.staticTargets {
if case let target as ClangTarget = target.underlying, target.isCXX {
let triple = buildProduct.buildParameters.triple
if triple.isDarwin() {
buildProduct.additionalFlags += ["-lc++"]
} else if triple.isWindows() {
// Don't link any C++ library.
} else {
buildProduct.additionalFlags += ["-lstdc++"]
// Don't link libc++ or libstd++ when building for Embedded Swift.
// Users can still link it manually for embedded platforms when needed,
// by providing `-Xlinker -lc++` options via CLI or `Package.swift`.
if !buildProduct.product.targets.contains(where: \.underlying.isEmbeddedSwiftTarget) {
// Link C++ if needed.
// Note: This will come from build settings in future.
for target in dependencies.staticTargets {
if case let target as ClangTarget = target.underlying, target.isCXX {
let triple = buildProduct.buildParameters.triple
if triple.isDarwin() {
buildProduct.additionalFlags += ["-lc++"]
} else if triple.isWindows() {
// Don't link any C++ library.
} else {
buildProduct.additionalFlags += ["-lstdc++"]
}
break
}
break
}
}

Expand Down
16 changes: 16 additions & 0 deletions Sources/PackageModel/Target/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,15 @@ public class Target: PolymorphicCodableProtocol {
self.pluginUsages = []
self.usesUnsafeFlags = try container.decode(Bool.self, forKey: .usesUnsafeFlags)
}

@_spi(SwiftPMInternal)
public var isEmbeddedSwiftTarget: Bool {
for case .enableExperimentalFeature("Embedded") in self.buildSettingsDescription.swiftSettings.map(\.kind) {
return true
}

return false
}
}

extension Target: Hashable {
Expand Down Expand Up @@ -371,3 +380,10 @@ public extension Sequence where Iterator.Element == Target {
}
}
}

extension [TargetBuildSettingDescription.Setting] {
@_spi(SwiftPMInternal)
public var swiftSettings: Self {
self.filter { $0.tool == .swift }
}
}
43 changes: 43 additions & 0 deletions Sources/SPMTestSupport/MockPackageGraphs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,46 @@ public func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackage

return (graph, fs, observability.topScope)
}

@_spi(SwiftPMInternal)
public func embeddedCxxInteropPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph {
let fs = InMemoryFileSystem(
emptyFiles:
"/Pkg/Sources/app/main.swift",
"/Pkg/Sources/lib/lib.cpp",
"/Pkg/Sources/lib/include/lib.h",
"/Pkg/Tests/test/TestCase.swift"
)

let observability = ObservabilitySystem.makeForTesting()
let graph = try loadPackageGraph(
fileSystem: fs,
manifests: [
Manifest.createRootManifest(
displayName: "Pkg",
path: "/Pkg",
targets: [
TargetDescription(
name: "app",
dependencies: ["lib"],
settings: [.init(tool: .swift, kind: .enableExperimentalFeature("Embedded"))]
),
TargetDescription(
name: "lib",
dependencies: [],
settings: [.init(tool: .swift, kind: .interoperabilityMode(.Cxx))]
),
TargetDescription(
name: "test",
dependencies: ["lib"],
type: .test
),
]
),
],
observabilityScope: observability.topScope
)
XCTAssertNoDiagnostics(observability.diagnostics)

return (graph, fs, observability.topScope)
}
38 changes: 34 additions & 4 deletions Tests/BuildTests/CrossCompilationBuildPlanTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import class PackageModel.Manifest
import struct PackageModel.TargetDescription
import func SPMTestSupport.loadPackageGraph

@_spi(SwiftPMInternal)
import func SPMTestSupport.embeddedCxxInteropPackageGraph

@_spi(SwiftPMInternal)
import func SPMTestSupport.macrosPackageGraph

Expand All @@ -37,13 +40,12 @@ import XCTest

final class CrossCompilationBuildPlanTests: XCTestCase {
func testEmbeddedWasmTarget() throws {
let pkgPath = AbsolutePath("/Pkg")
let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath)
var (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: "/Pkg")

let triple = try Triple("wasm32-unknown-none-wasm")
var parameters = mockBuildParameters(targetTriple: triple)
parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true
let result = try BuildPlanResult(plan: BuildPlan(
var result = try BuildPlanResult(plan: BuildPlan(
buildParameters: parameters,
graph: graph,
fileSystem: fs,
Expand All @@ -55,7 +57,34 @@ final class CrossCompilationBuildPlanTests: XCTestCase {
result.checkTargetsCount(5)

let buildPath = result.plan.productsBuildPath
let appBuildDescription = try result.buildProduct(for: "app")
var appBuildDescription = try result.buildProduct(for: "app")
XCTAssertEqual(
try appBuildDescription.linkArguments(),
[
result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString,
"-L", buildPath.pathString,
"-o", buildPath.appending(components: "app.wasm").pathString,
"-module-name", "app", "-static-stdlib", "-emit-executable",
"@\(buildPath.appending(components: "app.product", "Objects.LinkFileList"))",
"-target", triple.tripleString,
"-g",
]
)

(graph, fs, observabilityScope) = try embeddedCxxInteropPackageGraph(pkgRootPath: "/Pkg")

result = try BuildPlanResult(plan: BuildPlan(
buildParameters: parameters,
graph: graph,
fileSystem: fs,
observabilityScope: observabilityScope
))
result.checkProductsCount(2)
// There are two additional targets on non-Apple platforms, for test discovery and
// test entry point
result.checkTargetsCount(5)

appBuildDescription = try result.buildProduct(for: "app")
XCTAssertEqual(
try appBuildDescription.linkArguments(),
[
Expand All @@ -64,6 +93,7 @@ final class CrossCompilationBuildPlanTests: XCTestCase {
"-o", buildPath.appending(components: "app.wasm").pathString,
"-module-name", "app", "-static-stdlib", "-emit-executable",
"@\(buildPath.appending(components: "app.product", "Objects.LinkFileList"))",
"-enable-experimental-feature", "Embedded",
"-target", triple.tripleString,
"-g",
]
Expand Down