Skip to content

Commit 14c4329

Browse files
committed
Start reducing first stage bootstrap build
Currently, the initial bootstrap build using CMake is essentially building the entirety of SwiftPM and that is (mostly) because the `Commands` module pulls in everything that SwiftPM possibly offers. This introduces a new `swift-bootstrap` executable which is a minimal version of `swift-build` and extracts the necessary parts of the `Commands` module into a new `CoreCommands` module. This is a bit of a proof-of-concept at the moment, but demonstrates the idea and allows building the first stage without needing to build the swift-crypto dependency. Remaining issues: - currently breaks some functionality as shown by not running the full test suite successfully - the Windows build currently relies entirely on CMake, so making that a minimal build is not really viable at this time. I am not entirely sure how to factor things so that both build styles stay available, so right now there's only the minimal build. - exposes the awkwardness of the `--buid-system` option. There are several commands that just create a native `BuildOperation` object no matter what. This change improves the situation somewhat by throwing when any of these commands are called even though XCBuild was selected as the build system, but there's some more refactoring to do to make this better. - `SwiftToolTests` currently still depend on `Commands` but should be shifted over to a new `CoreCommandsTests` test target so that is no longer necessary
1 parent d61b1ea commit 14c4329

27 files changed

+756
-443
lines changed

CMakeLists.txt

-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ if(FIND_PM_DEPS)
5252
endif()
5353

5454
find_package(ArgumentParser CONFIG REQUIRED)
55-
find_package(SwiftCrypto CONFIG REQUIRED)
5655
find_package(SwiftDriver CONFIG REQUIRED)
5756
find_package(SwiftCollections CONFIG REQUIRED)
5857
endif()

Package.swift

+24-1
Original file line numberDiff line numberDiff line change
@@ -333,19 +333,36 @@ let package = Package(
333333

334334
// MARK: Commands
335335

336+
.target(
337+
/** Minimal set of commands required for bootstrapping a new SwiftPM */
338+
name: "CoreCommands",
339+
dependencies: [
340+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
341+
"Basics",
342+
"Build",
343+
"PackageFingerprint",
344+
"PackageModel",
345+
"Workspace",
346+
347+
// TODO: dependencies to get rid off
348+
"XCBuildSupport",
349+
],
350+
exclude: ["CMakeLists.txt"]
351+
),
352+
336353
.target(
337354
/** High-level commands */
338355
name: "Commands",
339356
dependencies: [
340357
.product(name: "ArgumentParser", package: "swift-argument-parser"),
341358
"Basics",
342359
"Build",
360+
"CoreCommands",
343361
"PackageCollections",
344362
"PackageFingerprint",
345363
"PackageGraph",
346364
"SourceControl",
347365
"Workspace",
348-
"XCBuildSupport",
349366
],
350367
exclude: ["CMakeLists.txt", "README.md"]
351368
),
@@ -361,6 +378,12 @@ let package = Package(
361378
dependencies: ["Commands"],
362379
exclude: ["CMakeLists.txt"]
363380
),
381+
.executableTarget(
382+
/** Builds SwiftPM itself for bootstrapping (minimal version of `swift-build`) */
383+
name: "swift-bootstrap",
384+
dependencies: ["CoreCommands"],
385+
exclude: ["CMakeLists.txt"]
386+
),
364387
.executableTarget(
365388
/** Runs package tests */
366389
name: "swift-test",

Sources/CMakeLists.txt

+2-9
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,8 @@
99
add_subdirectory(SPMSQLite3)
1010
add_subdirectory(Basics)
1111
add_subdirectory(Build)
12-
add_subdirectory(Commands)
12+
add_subdirectory(CoreCommands)
1313
add_subdirectory(LLBuildManifest)
14-
add_subdirectory(PackageCollections)
15-
add_subdirectory(PackageCollectionsModel)
16-
add_subdirectory(PackageCollectionsSigning)
17-
add_subdirectory(PackageCollectionsSigningLibc)
1814
add_subdirectory(PackageDescription)
1915
add_subdirectory(PackageFingerprint)
2016
add_subdirectory(PackageGraph)
@@ -25,9 +21,6 @@ add_subdirectory(PackageRegistry)
2521
add_subdirectory(SPMBuildCore)
2622
add_subdirectory(SPMLLBuild)
2723
add_subdirectory(SourceControl)
28-
add_subdirectory(swift-build)
29-
add_subdirectory(swift-package)
30-
add_subdirectory(swift-run)
31-
add_subdirectory(swift-test)
24+
add_subdirectory(swift-bootstrap)
3225
add_subdirectory(Workspace)
3326
add_subdirectory(XCBuildSupport)

Sources/Commands/Snippets/CardStack.swift

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import Basics
14+
import CoreCommands
1415
import PackageGraph
1516
import PackageModel
1617
import TSCBasic

Sources/Commands/Snippets/Cards/SnippetCard.swift

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import CoreCommands
1314
import PackageModel
1415
import TSCBasic
1516

Sources/Commands/Snippets/Cards/SnippetGroupCard.swift

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import CoreCommands
1314
import PackageModel
1415

1516
/// A card showing the snippets in a ``SnippetGroup``.

Sources/Commands/Snippets/Cards/TopCard.swift

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import CoreCommands
1314
import Foundation
1415
import PackageModel
1516
import PackageGraph

Sources/Commands/SwiftBuildTool.swift

+3-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import ArgumentParser
1414
import Basics
1515
import Build
16+
import CoreCommands
1617
import PackageGraph
1718
import SPMBuildCore
1819
import TSCBasic
@@ -94,7 +95,7 @@ public struct SwiftBuildTool: SwiftCommand {
9495
helpNames: [.short, .long, .customLong("help", withSingleDash: true)])
9596

9697
@OptionGroup()
97-
var globalOptions: GlobalOptions
98+
public var globalOptions: GlobalOptions
9899

99100
@OptionGroup()
100101
var options: BuildToolOptions
@@ -105,7 +106,7 @@ public struct SwiftBuildTool: SwiftCommand {
105106
}
106107

107108
if options.printManifestGraphviz {
108-
let buildOperation = try swiftTool.createBuildOperation()
109+
let buildOperation = try swiftTool.createNativeBuildSystemOnly()
109110
let buildManifest = try buildOperation.getBuildManifest()
110111
var serializer = DOTManifestSerializer(manifest: buildManifest)
111112
// print to stdout
@@ -153,9 +154,3 @@ public struct SwiftBuildTool: SwiftCommand {
153154

154155
public init() {}
155156
}
156-
157-
extension Basics.Diagnostic {
158-
static func mutuallyExclusiveArgumentsError(arguments: [String]) -> Self {
159-
.error(arguments.map{ "'\($0)'" }.spm_localizedJoin(type: .conjunction) + " are mutually exclusive")
160-
}
161-
}

Sources/Commands/SwiftPackageCollectionsTool.swift

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import ArgumentParser
1414
import Basics
15+
import CoreCommands
1516
import Foundation
1617
import PackageCollections
1718
import PackageModel

Sources/Commands/SwiftPackageRegistryTool.swift

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import ArgumentParser
1414
import Basics
15+
import CoreCommands
1516
import TSCBasic
1617
import SPMBuildCore
1718
import Build

Sources/Commands/SwiftPackageTool.swift

+12-11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import ArgumentParser
1414
import Basics
15+
import CoreCommands
1516
import TSCBasic
1617
import SPMBuildCore
1718
import Build
@@ -398,15 +399,15 @@ extension SwiftPackageTool {
398399
var regenerateBaseline: Bool = false
399400

400401
func run(_ swiftTool: SwiftTool) throws {
401-
let apiDigesterPath = try swiftTool.getToolchain().getSwiftAPIDigester()
402+
let apiDigesterPath = try swiftTool.getDestinationToolchain().getSwiftAPIDigester()
402403
let apiDigesterTool = SwiftAPIDigester(fileSystem: swiftTool.fileSystem, tool: apiDigesterPath)
403404

404405
let packageRoot = try globalOptions.locations.packageDirectory ?? swiftTool.getPackageRoot()
405406
let repository = GitRepository(path: packageRoot)
406407
let baselineRevision = try repository.resolveRevision(identifier: treeish)
407408

408409
// We turn build manifest caching off because we need the build plan.
409-
let buildOp = try swiftTool.createBuildOperation(cacheBuildManifest: false)
410+
let buildOp = try swiftTool.createNativeBuildSystemOnly(cacheBuildManifest: false)
410411

411412
let packageGraph = try buildOp.getPackageGraph()
412413
let modulesToDiff = try determineModulesToDiff(
@@ -590,13 +591,13 @@ extension SwiftPackageTool {
590591
// Build the current package.
591592
//
592593
// We turn build manifest caching off because we need the build plan.
593-
let buildOp = try swiftTool.createBuildOperation(cacheBuildManifest: false)
594+
let buildOp = try swiftTool.createNativeBuildSystemOnly(cacheBuildManifest: false)
594595
try buildOp.build()
595596

596597
// Configure the symbol graph extractor.
597598
let symbolGraphExtractor = try SymbolGraphExtract(
598599
fileSystem: swiftTool.fileSystem,
599-
tool: swiftTool.getToolchain().getSymbolGraphExtract(),
600+
tool: swiftTool.getDestinationToolchain().getSymbolGraphExtract(),
600601
skipSynthesizedMembers: skipSynthesizedMembers,
601602
minimumAccessLevel: minimumAccessLevel,
602603
skipInheritedDocs: skipInheritedDocs,
@@ -1067,13 +1068,13 @@ extension SwiftPackageTool {
10671068
let readOnlyDirectories = writableDirectories.contains{ package.path.isDescendantOfOrEqual(to: $0) } ? [] : [package.path]
10681069

10691070
// Use the directory containing the compiler as an additional search directory, and add the $PATH.
1070-
let toolSearchDirs = [try swiftTool.getToolchain().swiftCompilerPath.parentDirectory]
1071+
let toolSearchDirs = [try swiftTool.getDestinationToolchain().swiftCompilerPath.parentDirectory]
10711072
+ getEnvSearchPaths(pathString: ProcessEnv.path, currentWorkingDirectory: .none)
10721073

10731074
// Build or bring up-to-date any executable host-side tools on which this plugin depends. Add them and any binary dependencies to the tool-names-to-path map.
10741075
var toolNamesToPaths: [String: AbsolutePath] = [:]
10751076
for dep in plugin.dependencies {
1076-
let buildOperation = try swiftTool.createBuildOperation(cacheBuildManifest: false)
1077+
let buildOperation = try swiftTool.createNativeBuildSystemOnly(cacheBuildManifest: false)
10771078
switch dep {
10781079
case .product(let productRef, _):
10791080
// Build the product referenced by the tool, and add the executable to the tool map.
@@ -1195,7 +1196,7 @@ final class PluginDelegate: PluginInvocationDelegate {
11951196
buildParameters.flags.linkerFlags.append(contentsOf: parameters.otherLinkerFlags)
11961197

11971198
// Configure the verbosity of the output.
1198-
let logLevel: Diagnostic.Severity
1199+
let logLevel: Basics.Diagnostic.Severity
11991200
switch parameters.logging {
12001201
case .concise:
12011202
logLevel = .warning
@@ -1220,7 +1221,7 @@ final class PluginDelegate: PluginInvocationDelegate {
12201221

12211222
// Create a build operation. We have to disable the cache in order to get a build plan created.
12221223
let outputStream = BufferedOutputByteStream()
1223-
let buildOperation = try self.swiftTool.createBuildOperation(
1224+
let buildOperation = try self.swiftTool.createNativeBuildSystemOnly(
12241225
explicitProduct: explicitProduct,
12251226
cacheBuildManifest: false,
12261227
customBuildParameters: buildParameters,
@@ -1274,7 +1275,7 @@ final class PluginDelegate: PluginInvocationDelegate {
12741275

12751276
func performTestsForPlugin(subset: PluginInvocationTestSubset, parameters: PluginInvocationTestParameters) throws -> PluginInvocationTestResult {
12761277
// Build the tests. Ideally we should only build those that match the subset, but we don't have a way to know which ones they are until we've built them and can examine the binaries.
1277-
let toolchain = try swiftTool.getToolchain()
1278+
let toolchain = try swiftTool.getDestinationToolchain()
12781279
var buildParameters = try swiftTool.buildParameters()
12791280
buildParameters.enableTestability = true
12801281
buildParameters.enableCodeCoverage = parameters.enableCodeCoverage
@@ -1406,7 +1407,7 @@ final class PluginDelegate: PluginInvocationDelegate {
14061407
// Current implementation uses `SymbolGraphExtract()` but in the future we should emit the symbol graph while building.
14071408

14081409
// Create a build operation for building the target., skipping the the cache because we need the build plan.
1409-
let buildOperation = try swiftTool.createBuildOperation(cacheBuildManifest: false)
1410+
let buildOperation = try swiftTool.createNativeBuildSystemOnly(cacheBuildManifest: false)
14101411

14111412
// Find the target in the build operation's package graph; it's an error if we don't find it.
14121413
let packageGraph = try buildOperation.getPackageGraph()
@@ -1420,7 +1421,7 @@ final class PluginDelegate: PluginInvocationDelegate {
14201421
// Configure the symbol graph extractor.
14211422
var symbolGraphExtractor = try SymbolGraphExtract(
14221423
fileSystem: swiftTool.fileSystem,
1423-
tool: swiftTool.getToolchain().getSymbolGraphExtract()
1424+
tool: swiftTool.getDestinationToolchain().getSymbolGraphExtract()
14241425
)
14251426
symbolGraphExtractor.skipSynthesizedMembers = !options.includeSynthesized
14261427
switch options.minimumAccessLevel {

Sources/Commands/SwiftRunTool.swift

+8-7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import ArgumentParser
1414
import Basics
1515
import Build
16+
import CoreCommands
1617
import PackageGraph
1718
import PackageModel
1819
import TSCBasic
@@ -97,7 +98,7 @@ public struct SwiftRunTool: SwiftCommand {
9798
helpNames: [.short, .long, .customLong("help", withSingleDash: true)])
9899

99100
@OptionGroup()
100-
var globalOptions: GlobalOptions
101+
public var globalOptions: GlobalOptions
101102

102103
@OptionGroup()
103104
var options: RunToolOptions
@@ -123,21 +124,21 @@ public struct SwiftRunTool: SwiftCommand {
123124

124125
// Construct the build operation.
125126
// FIXME: We need to implement the build tool invocation closure here so that build tool plugins work with the REPL. rdar://86112934
126-
let buildOp = try swiftTool.createBuildOperation(
127+
let buildSystem = try swiftTool.createNativeBuildSystemOnly(
127128
cacheBuildManifest: false,
128129
customBuildParameters: buildParameters,
129130
customPackageGraphLoader: graphLoader
130131
)
131132

132133
// Perform build.
133-
try buildOp.build()
134+
try buildSystem.build()
134135

135136
// Execute the REPL.
136-
let arguments = buildOp.buildPlan!.createREPLArguments()
137+
let arguments = buildSystem.buildPlan!.createREPLArguments()
137138
print("Launching Swift REPL with arguments: \(arguments.joined(separator: " "))")
138139
try self.run(
139140
fileSystem: swiftTool.fileSystem,
140-
executablePath: swiftTool.getToolchain().swiftInterpreterPath,
141+
executablePath: swiftTool.getDestinationToolchain().swiftInterpreterPath,
141142
originalWorkingDirectory: swiftTool.originalWorkingDirectory,
142143
arguments: arguments
143144
)
@@ -161,7 +162,7 @@ public struct SwiftRunTool: SwiftCommand {
161162
}
162163

163164
let pathRelativeToWorkingDirectory = executablePath.relative(to: swiftTool.originalWorkingDirectory)
164-
let lldbPath = try swiftTool.getToolchain().getLLDB()
165+
let lldbPath = try swiftTool.getDestinationToolchain().getLLDB()
165166
try exec(path: lldbPath.pathString, args: ["--", pathRelativeToWorkingDirectory.pathString] + options.arguments)
166167
} catch let error as RunError {
167168
swiftTool.observabilityScope.emit(error)
@@ -173,7 +174,7 @@ public struct SwiftRunTool: SwiftCommand {
173174
if let executable = options.executable, isValidSwiftFilePath(fileSystem: swiftTool.fileSystem, path: executable) {
174175
swiftTool.observabilityScope.emit(.runFileDeprecation)
175176
// Redirect execution to the toolchain's swift executable.
176-
let swiftInterpreterPath = try swiftTool.getToolchain().swiftInterpreterPath
177+
let swiftInterpreterPath = try swiftTool.getDestinationToolchain().swiftInterpreterPath
177178
// Prepend the script to interpret to the arguments.
178179
let arguments = [executable] + options.arguments
179180
try self.run(

Sources/Commands/SwiftTestTool.swift

+7-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import ArgumentParser
1414
import Basics
1515
import Build
16+
import CoreCommands
1617
import Dispatch
1718
import class Foundation.NSLock
1819
import class Foundation.ProcessInfo
@@ -151,7 +152,7 @@ public struct SwiftTestTool: SwiftCommand {
151152
helpNames: [.short, .long, .customLong("help", withSingleDash: true)])
152153

153154
@OptionGroup()
154-
var globalOptions: GlobalOptions
155+
public var globalOptions: GlobalOptions
155156

156157
@OptionGroup()
157158
var sharedOptions: SharedOptions
@@ -165,7 +166,7 @@ public struct SwiftTestTool: SwiftCommand {
165166
try self.validateArguments(observabilityScope: swiftTool.observabilityScope)
166167

167168
// validate XCTest available on darwin based systems
168-
let toolchain = try swiftTool.getToolchain()
169+
let toolchain = try swiftTool.getDestinationToolchain()
169170
if toolchain.triple.isDarwin() && toolchain.xctestPath == nil {
170171
throw TestError.xctestNotAvailable
171172
}
@@ -183,7 +184,7 @@ public struct SwiftTestTool: SwiftCommand {
183184
let command = try GenerateLinuxMain.parse()
184185
try command.run(swiftTool)
185186
} else if !self.options.shouldRunInParallel {
186-
let toolchain = try swiftTool.getToolchain()
187+
let toolchain = try swiftTool.getDestinationToolchain()
187188
let testProducts = try buildTestsIfNeeded(swiftTool: swiftTool)
188189
let buildParameters = try swiftTool.buildParametersForTest(options: self.options)
189190

@@ -259,7 +260,7 @@ public struct SwiftTestTool: SwiftCommand {
259260
}
260261

261262
} else {
262-
let toolchain = try swiftTool.getToolchain()
263+
let toolchain = try swiftTool.getDestinationToolchain()
263264
let testProducts = try buildTestsIfNeeded(swiftTool: swiftTool)
264265
let testSuites = try TestingSupport.getTestSuites(
265266
in: testProducts,
@@ -351,7 +352,7 @@ public struct SwiftTestTool: SwiftCommand {
351352
/// Merges all profraw profiles in codecoverage directory into default.profdata file.
352353
private func mergeCodeCovRawDataFiles(swiftTool: SwiftTool) throws {
353354
// Get the llvm-prof tool.
354-
let llvmProf = try swiftTool.getToolchain().getLLVMProf()
355+
let llvmProf = try swiftTool.getDestinationToolchain().getLLVMProf()
355356

356357
// Get the profraw files.
357358
let buildParameters = try swiftTool.buildParametersForTest(options: self.options)
@@ -373,7 +374,7 @@ public struct SwiftTestTool: SwiftCommand {
373374
/// Exports profdata as a JSON file.
374375
private func exportCodeCovAsJSON(to path: AbsolutePath, testBinary: AbsolutePath, swiftTool: SwiftTool) throws {
375376
// Export using the llvm-cov tool.
376-
let llvmCov = try swiftTool.getToolchain().getLLVMCov()
377+
let llvmCov = try swiftTool.getDestinationToolchain().getLLVMCov()
377378
let buildParameters = try swiftTool.buildParametersForTest(options: self.options)
378379
let args = [
379380
llvmCov.pathString,

0 commit comments

Comments
 (0)