Skip to content

Create an alternate SwiftPM build system called Swift Build #8271

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

Merged
merged 3 commits into from
Mar 3, 2025
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
16 changes: 16 additions & 0 deletions IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ final class SwiftPMTests: XCTestCase {
}
}

func testSwiftBuild() throws {
#if os(Linux)
if FileManager.default.contents(atPath: "/etc/system-release").map { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ?? false {
throw XCTSkip("Skipping SwiftBuild testing on Amazon Linux because of platform issues.")
}
#endif

// Test SwiftBuildSystem
try withTemporaryDirectory { tmpDir in
let packagePath = tmpDir.appending(component: "foo")
try localFileSystem.createDirectory(packagePath)
try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "executable")
try sh(swiftBuild, "--package-path", packagePath, "--build-system", "swiftbuild")
}
}

func testArchCustomization() throws {
#if !os(macOS)
try XCTSkip("Test requires macOS")
Expand Down
35 changes: 35 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,13 @@ let package = Package(
.unsafeFlags(["-static"]),
]
),
.target(
name: "SwiftBuildSupport",
dependencies: [
"SPMBuildCore",
"PackageGraph",
]
),
.target(
/** High level functionality */
name: "Workspace",
Expand Down Expand Up @@ -500,6 +507,7 @@ let package = Package(
"PackageGraph",
"Workspace",
"XCBuildSupport",
"SwiftBuildSupport",
],
exclude: ["CMakeLists.txt"],
swiftSettings: [
Expand All @@ -521,6 +529,7 @@ let package = Package(
"SourceControl",
"Workspace",
"XCBuildSupport",
"SwiftBuildSupport",
] + swiftSyntaxDependencies(["SwiftIDEUtils"]),
exclude: ["CMakeLists.txt", "README.md"],
swiftSettings: [
Expand Down Expand Up @@ -619,6 +628,7 @@ let package = Package(
"PackageLoading",
"PackageModel",
"XCBuildSupport",
"SwiftBuildSupport",
],
exclude: ["CMakeLists.txt"]
),
Expand Down Expand Up @@ -698,6 +708,7 @@ let package = Package(
dependencies: [
"Build",
"XCBuildSupport",
"SwiftBuildSupport",
"_InternalTestSupport"
],
swiftSettings: [
Expand Down Expand Up @@ -1010,3 +1021,27 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
.package(path: "../swift-toolchain-sqlite"),
]
}

if ProcessInfo.processInfo.environment["SWIFTPM_SWBUILD_FRAMEWORK"] == nil &&
ProcessInfo.processInfo.environment["SWIFTPM_NO_SWBUILD_DEPENDENCY"] == nil {

let swiftbuildsupport: Target = package.targets.first(where: { $0.name == "SwiftBuildSupport" } )!
swiftbuildsupport.dependencies += [
.product(name: "SwiftBuild", package: "swift-build"),
]

swiftbuildsupport.dependencies += [
// This is here to statically link the build service in the same executable as SwiftPM
.product(name: "SWBBuildService", package: "swift-build"),
]

if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
package.dependencies += [
.package(url: "https://github.com/swiftlang/swift-build.git", branch: relatedDependenciesBranch),
]
} else {
package.dependencies += [
.package(path: "../swift-build"),
]
}
}
1 change: 1 addition & 0 deletions Sources/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ add_subdirectory(swift-test)
add_subdirectory(SwiftSDKCommand)
add_subdirectory(Workspace)
add_subdirectory(XCBuildSupport)
add_subdirectory(SwiftBuildSupport)
31 changes: 31 additions & 0 deletions Sources/CoreCommands/BuildSystemSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Basics
import Build
import SPMBuildCore
import XCBuildSupport
import SwiftBuildSupport
import PackageGraph

import class Basics.ObservabilityScope
Expand Down Expand Up @@ -93,10 +94,40 @@ private struct XcodeBuildSystemFactory: BuildSystemFactory {
}
}

private struct SwiftBuildSystemFactory: BuildSystemFactory {
let swiftCommandState: SwiftCommandState

func makeBuildSystem(
explicitProduct: String?,
traitConfiguration: TraitConfiguration,
cacheBuildManifest: Bool,
productsBuildParameters: BuildParameters?,
toolsBuildParameters: BuildParameters?,
packageGraphLoader: (() async throws -> ModulesGraph)?,
outputStream: OutputByteStream?,
logLevel: Diagnostic.Severity?,
observabilityScope: ObservabilityScope?
) throws -> any BuildSystem {
return try SwiftBuildSystem(
buildParameters: productsBuildParameters ?? self.swiftCommandState.productsBuildParameters,
packageGraphLoader: packageGraphLoader ?? {
try await self.swiftCommandState.loadPackageGraph(
explicitProduct: explicitProduct
)
},
outputStream: outputStream ?? self.swiftCommandState.outputStream,
logLevel: logLevel ?? self.swiftCommandState.logLevel,
fileSystem: self.swiftCommandState.fileSystem,
observabilityScope: observabilityScope ?? self.swiftCommandState.observabilityScope
)
}
}

extension SwiftCommandState {
public var defaultBuildSystemProvider: BuildSystemProvider {
.init(providers: [
.native: NativeBuildSystemFactory(swiftCommandState: self),
.swiftbuild: SwiftBuildSystemFactory(swiftCommandState: self),
.xcode: XcodeBuildSystemFactory(swiftCommandState: self)
])
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/CoreCommands/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ target_link_libraries(CoreCommands PUBLIC
TSCBasic
TSCUtility
Workspace
XCBuildSupport)
XCBuildSupport
SwiftBuildSupport)
target_link_libraries(CoreCommands PRIVATE
DriverSupport
$<$<NOT:$<PLATFORM_ID:Darwin>>:FoundationXML>)
Expand Down
4 changes: 2 additions & 2 deletions Sources/CoreCommands/SwiftCommandState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ public final class SwiftCommandState {
toolsBuildParameters: BuildParameters? = .none,
packageGraphLoader: (() async throws -> ModulesGraph)? = .none,
outputStream: OutputByteStream? = .none,
logLevel: Basics.Diagnostic.Severity? = .none,
logLevel: Basics.Diagnostic.Severity? = nil,
observabilityScope: ObservabilityScope? = .none
) async throws -> BuildSystem {
guard let buildSystemProvider else {
Expand All @@ -747,7 +747,7 @@ public final class SwiftCommandState {
toolsBuildParameters: toolsBuildParameters,
packageGraphLoader: packageGraphLoader,
outputStream: outputStream,
logLevel: logLevel,
logLevel: logLevel ?? self.logLevel,
observabilityScope: observabilityScope
)

Expand Down
5 changes: 5 additions & 0 deletions Sources/PackageModel/SwiftLanguageVersion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public struct SwiftLanguageVersion: Hashable, Sendable {
v3, v4, v4_2, v5, v6
]

/// The list of supported Swift language versions for this toolchain.
public static let supportedSwiftLanguageVersions = [
v4, v4_2, v5, v6
]

/// The raw value of the language version.
//
// This should be passed as a value to Swift compiler's -swift-version flag.
Expand Down
4 changes: 3 additions & 1 deletion Sources/SPMBuildCore/BuildSystem/BuildSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public enum BuildSubset {
}

/// A protocol that represents a build system used by SwiftPM for all build operations. This allows factoring out the
/// implementation details between SwiftPM's `BuildOperation` and the XCBuild backed `XCBuildSystem`.
/// implementation details between SwiftPM's `BuildOperation` and the Swift Build backed `SwiftBuildSystem`.
public protocol BuildSystem: Cancellable {

/// The delegate used by the build system.
Expand Down Expand Up @@ -130,6 +130,7 @@ public struct BuildSystemProvider {
// TODO: In the future, we may want this to be about specific capabilities of a build system rather than choosing a concrete one.
public enum Kind: String, CaseIterable {
case native
case swiftbuild
case xcode
}

Expand Down Expand Up @@ -172,6 +173,7 @@ extension BuildSystemProvider.Kind {
public var usesXcodeBuildEngine: Bool {
switch self {
case .native: return false
case .swiftbuild: return false
Copy link
Contributor

Choose a reason for hiding this comment

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

Chore (blocking): this should return "true"

Copy link
Member Author

Choose a reason for hiding this comment

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

SwiftBuild isn't using any Xcode or its build engine. It's unclear what this check is for.

Copy link
Contributor

@bkhouri bkhouri Mar 3, 2025

Choose a reason for hiding this comment

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

The variable name may be incorrect, but this is meant to determine if the underlying build engine uses the same "build output directory structure" as Xcode.

Setting this to true will likely resolve #8272

Copy link
Member

Choose a reason for hiding this comment

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

SwiftBuild doesn't use the same directory structure.

case .xcode: return true
}
}
Expand Down
26 changes: 26 additions & 0 deletions Sources/SwiftBuildSupport/BuildSystem.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import SPMBuildCore
import PackageModel

extension BuildConfiguration {
public var swiftbuildName: String {
switch self {
case .debug: "Debug"
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: can we have the build configuration confirm to "ConvertibleString" ?

case .release: "Release"
}
}
}

extension BuildSubset {
var pifTargetName: String {
switch self {
case .product(let name, _):
PackagePIFProjectBuilder.targetName(for: name)
case .target(let name, _):
name
case .allExcludingTests:
PIFBuilder.allExcludingTestsTargetName
case .allIncludingTests:
PIFBuilder.allIncludingTestsTargetName
}
}
}
23 changes: 23 additions & 0 deletions Sources/SwiftBuildSupport/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This source file is part of the Swift open source project
#
# Copyright (c) 2025 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See http://swift.org/LICENSE.txt for license information
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors

add_library(SwiftBuildSupport STATIC
PIF.swift
PIFBuilder.swift
BuildSystem.swift
SwiftBuildSystem.swift)
target_link_libraries(SwiftBuildSupport PUBLIC
Build
DriverSupport
TSCBasic
TSCUtility
PackageGraph
)

set_target_properties(SwiftBuildSupport PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
Loading