-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
Copy pathSwiftBuildCommand.swift
205 lines (174 loc) · 7.78 KB
/
SwiftBuildCommand.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2017 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 the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import ArgumentParser
import Basics
import Build
import CoreCommands
import PackageGraph
import SPMBuildCore
import XCBuildSupport
import class TSCBasic.Process
import var TSCBasic.stdoutStream
import enum TSCUtility.Diagnostics
import func TSCUtility.getClangVersion
import struct TSCUtility.Version
extension BuildSubset {
var argumentName: String {
switch self {
case .allExcludingTests:
fatalError("no corresponding argument")
case .allIncludingTests:
return "--build-tests"
case .product:
return "--product"
case .target:
return "--target"
}
}
}
struct BuildCommandOptions: ParsableArguments {
/// Returns the build subset specified with the options.
func buildSubset(observabilityScope: ObservabilityScope) -> BuildSubset? {
var allSubsets: [BuildSubset] = []
if let product {
allSubsets.append(.product(product))
}
if let target {
allSubsets.append(.target(target))
}
if buildTests {
allSubsets.append(.allIncludingTests)
}
guard allSubsets.count < 2 else {
observabilityScope.emit(.mutuallyExclusiveArgumentsError(arguments: allSubsets.map{ $0.argumentName }))
return nil
}
return allSubsets.first ?? .allExcludingTests
}
/// If the test should be built.
@Flag(help: "Build both source and test targets")
var buildTests: Bool = false
/// If the binary output path should be printed.
@Flag(name: .customLong("show-bin-path"), help: "Print the binary output path")
var shouldPrintBinPath: Bool = false
/// Whether to output a graphviz file visualization of the combined job graph for all targets
@Flag(name: .customLong("print-manifest-job-graph"),
help: "Write the command graph for the build manifest as a graphviz file")
var printManifestGraphviz: Bool = false
/// Specific target to build.
@Option(help: "Build the specified target")
var target: String?
/// Specific product to build.
@Option(help: "Build the specified product")
var product: String?
/// If should link the Swift stdlib statically.
@Flag(name: .customLong("static-swift-stdlib"), inversion: .prefixedNo, help: "Link Swift stdlib statically")
package var shouldLinkStaticSwiftStdlib: Bool = false
/// Which testing libraries to use (and any related options.)
@OptionGroup()
var testLibraryOptions: TestLibraryOptions
func validate() throws {
// If --build-tests was not specified, it does not make sense to enable
// or disable either testing library.
if !buildTests {
if testLibraryOptions.explicitlyEnableXCTestSupport != nil
|| testLibraryOptions.explicitlyEnableSwiftTestingLibrarySupport != nil {
throw StringError("pass --build-tests to build test targets")
}
}
}
}
/// swift-build command namespace
package struct SwiftBuildCommand: AsyncSwiftCommand {
package static var configuration = CommandConfiguration(
commandName: "build",
_superCommandName: "swift",
abstract: "Build sources into binary products",
discussion: "SEE ALSO: swift run, swift package, swift test",
version: SwiftVersion.current.completeDisplayString,
helpNames: [.short, .long, .customLong("help", withSingleDash: true)])
@OptionGroup()
package var globalOptions: GlobalOptions
@OptionGroup()
var options: BuildCommandOptions
package func run(_ swiftCommandState: SwiftCommandState) async throws {
if options.shouldPrintBinPath {
return try print(swiftCommandState.productsBuildParameters.buildPath.description)
}
if options.printManifestGraphviz {
// FIXME: Doesn't seem ideal that we need an explicit build operation, but this concretely uses the `LLBuildManifest`.
guard let buildOperation = try swiftCommandState.createBuildSystem(explicitBuildSystem: .native) as? BuildOperation else {
throw StringError("asked for native build system but did not get it")
}
let buildManifest = try buildOperation.getBuildManifest()
var serializer = DOTManifestSerializer(manifest: buildManifest)
// print to stdout
let outputStream = stdoutStream
serializer.writeDOT(to: outputStream)
outputStream.flush()
return
}
guard let subset = options.buildSubset(observabilityScope: swiftCommandState.observabilityScope) else {
throw ExitCode.failure
}
if case .allIncludingTests = subset {
func updateTestingParameters(of buildParameters: inout BuildParameters, library: BuildParameters.Testing.Library) {
buildParameters.testingParameters = .init(
configuration: buildParameters.configuration,
targetTriple: buildParameters.triple,
enableCodeCoverage: buildParameters.testingParameters.enableCodeCoverage,
enableTestability: buildParameters.testingParameters.enableTestability,
experimentalTestOutput: buildParameters.testingParameters.experimentalTestOutput,
forceTestDiscovery: globalOptions.build.enableTestDiscovery,
testEntryPointPath: globalOptions.build.testEntryPointPath,
library: library
)
}
var productsBuildParameters = try swiftCommandState.productsBuildParameters
var toolsBuildParameters = try swiftCommandState.toolsBuildParameters
for library in try options.testLibraryOptions.enabledTestingLibraries(swiftCommandState: swiftCommandState) {
updateTestingParameters(of: &productsBuildParameters, library: library)
updateTestingParameters(of: &toolsBuildParameters, library: library)
try build(swiftCommandState, subset: subset, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters)
}
} else {
try build(swiftCommandState, subset: subset, productsBuildParameters: nil, toolsBuildParameters: nil)
}
}
private func build(
_ swiftCommandState: SwiftCommandState,
subset: BuildSubset,
productsBuildParameters: BuildParameters?,
toolsBuildParameters: BuildParameters?
) throws {
let buildSystem = try swiftCommandState.createBuildSystem(
explicitProduct: options.product,
shouldLinkStaticSwiftStdlib: options.shouldLinkStaticSwiftStdlib,
productsBuildParameters: productsBuildParameters,
toolsBuildParameters: toolsBuildParameters,
// command result output goes on stdout
// ie "swift build" should output to stdout
outputStream: TSCBasic.stdoutStream
)
do {
try buildSystem.build(subset: subset)
} catch _ as Diagnostics {
throw ExitCode.failure
}
}
package init() {}
}
package extension _SwiftCommand {
func buildSystemProvider(_ swiftCommandState: SwiftCommandState) throws -> BuildSystemProvider {
swiftCommandState.defaultBuildSystemProvider
}
}