Skip to content

Commit 8a8a86a

Browse files
committed
plugins: Global compiler flags should not be used when building plugin dependencies
Build tools and end products are built with separate toolchains: * Plugins and any targets they depend on, such as helper tools, are built with the host toolchain. * End products are built with the target toolchain. Compiler flags set with -Xswiftc are intended to be applied only to end product builds, but they are incorrectly being applied to plugin tool builds as well. This can cause problems because target toolchain flags might not be suitable for a tools build. In the worst case, the build might fail completely if the host toolchain rejects the target toolchain flag. For example, Linux toochains accept the `-static-executable` flag, but macOS toolchains reject it. Fixes: rdar://127813618
1 parent 9183b7c commit 8a8a86a

File tree

3 files changed

+74
-8
lines changed

3 files changed

+74
-8
lines changed

Sources/CoreCommands/Options.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,28 +316,28 @@ package struct BuildOptions: ParsableArguments {
316316
@Option(
317317
name: .customLong("Xcc", withSingleDash: true),
318318
parsing: .unconditionalSingleValue,
319-
help: "Pass flag through to all C compiler invocations"
319+
help: "Pass flag through to C compiler invocations for the target triple"
320320
)
321321
var cCompilerFlags: [String] = []
322322

323323
@Option(
324324
name: .customLong("Xswiftc", withSingleDash: true),
325325
parsing: .unconditionalSingleValue,
326-
help: "Pass flag through to all Swift compiler invocations"
326+
help: "Pass flag through to Swift compiler invocations for the target triple"
327327
)
328328
var swiftCompilerFlags: [String] = []
329329

330330
@Option(
331331
name: .customLong("Xlinker", withSingleDash: true),
332332
parsing: .unconditionalSingleValue,
333-
help: "Pass flag through to all linker invocations"
333+
help: "Pass flag through to linker invocations for the target triple"
334334
)
335335
var linkerFlags: [String] = []
336336

337337
@Option(
338338
name: .customLong("Xcxx", withSingleDash: true),
339339
parsing: .unconditionalSingleValue,
340-
help: "Pass flag through to all C++ compiler invocations"
340+
help: "Pass flag through to C++ compiler invocations for the target triple"
341341
)
342342
var cxxCompilerFlags: [String] = []
343343

Sources/CoreCommands/SwiftCommandState.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ package final class SwiftCommandState {
725725
when building on macOS.
726726
"""
727727

728-
private func _buildParams(toolchain: UserToolchain) throws -> BuildParameters {
728+
private func _buildParams(toolchain: UserToolchain, buildFlags: PackageModel.BuildFlags) throws -> BuildParameters {
729729
let triple = toolchain.targetTriple
730730

731731
let dataPath = self.scratchDirectory.appending(
@@ -741,7 +741,7 @@ package final class SwiftCommandState {
741741
configuration: options.build.configuration,
742742
toolchain: toolchain,
743743
triple: triple,
744-
flags: options.build.buildFlags,
744+
flags: buildFlags,
745745
pkgConfigDirectories: options.locations.pkgConfigDirectories,
746746
architectures: options.build.architectures,
747747
workers: options.build.jobs ?? UInt32(ProcessInfo.processInfo.activeProcessorCount),
@@ -796,7 +796,7 @@ package final class SwiftCommandState {
796796

797797
private lazy var _toolsBuildParameters: Result<BuildParameters, Swift.Error> = {
798798
Result(catching: {
799-
try _buildParams(toolchain: self.getHostToolchain())
799+
try _buildParams(toolchain: self.getHostToolchain(), buildFlags: .init())
800800
})
801801
}()
802802

@@ -808,7 +808,7 @@ package final class SwiftCommandState {
808808

809809
private lazy var _productsBuildParameters: Result<BuildParameters, Swift.Error> = {
810810
Result(catching: {
811-
try _buildParams(toolchain: self.getTargetToolchain())
811+
try _buildParams(toolchain: self.getTargetToolchain(), buildFlags: options.build.buildFlags)
812812
})
813813
}()
814814

Tests/CommandsTests/SwiftCommandStateTests.swift

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,72 @@ final class SwiftCommandStateTests: CommandsTestCase {
322322
try XCTAssertMatch(plan.buildProducts.compactMap { $0 as? Build.ProductBuildDescription }.first?.linkArguments() ?? [],
323323
[.anySequence, "-gnone", .anySequence])
324324
}
325+
326+
func testSeparateProductAndToolBuildParameters() throws {
327+
let fs = InMemoryFileSystem(emptyFiles: [
328+
"/Pkg/Sources/exe/main.swift",
329+
"/Pkg/Sources/helper/main.swift",
330+
"/Pkg/Plugins/plugin/main.swift",
331+
])
332+
333+
let observer = ObservabilitySystem.makeForTesting()
334+
let graph = try loadModulesGraph(
335+
fileSystem: fs,
336+
manifests: [
337+
Manifest.createRootManifest(
338+
displayName: "Pkg",
339+
path: "/Pkg",
340+
targets: [
341+
TargetDescription(name: "exe", dependencies: []),
342+
TargetDescription(name: "helper", dependencies: []),
343+
TargetDescription(
344+
name: "plugin",
345+
dependencies: ["helper"],
346+
type: .plugin,
347+
pluginCapability: .command(
348+
intent: .sourceCodeFormatting,
349+
permissions: []
350+
)
351+
)
352+
]
353+
)
354+
],
355+
observabilityScope: observer.topScope
356+
)
357+
358+
var plan: BuildPlan
359+
360+
/* Set up flags which should only be used for product builds */
361+
let staticExecutableOptions = try GlobalOptions.parse(["-Xswiftc", "-static-executable"])
362+
let staticExecutable = try SwiftCommandState.makeMockState(options: staticExecutableOptions)
363+
364+
XCTAssert(try staticExecutable.productsBuildParameters.flags.swiftCompilerFlags.contains("-static-executable"))
365+
XCTAssertFalse(try staticExecutable.toolsBuildParameters.flags.swiftCompilerFlags.contains("-static-executable"))
366+
367+
// Verify that the flags are used appropriately in the build plan
368+
plan = try BuildPlan(
369+
destinationBuildParameters: staticExecutable.productsBuildParameters,
370+
toolsBuildParameters: staticExecutable.toolsBuildParameters,
371+
graph: graph,
372+
fileSystem: fs,
373+
observabilityScope: observer.topScope
374+
)
375+
let result = try BuildPlanResult(plan: plan)
376+
377+
// exe should be built with -static-executable
378+
let exe = try result.target(for: "exe")
379+
XCTAssert(try exe.swiftTarget().compileArguments().contains("-static-executable"))
380+
381+
// helper should be built without -static-executable
382+
// There are 2 helper targets in the plan:
383+
// - one will yield a normal target,
384+
// - the other is for the use of the command plugin
385+
// We can find the correct one via the plugin's dependency chain.
386+
let plugin = plan.graph.allTargets.first(where: { $0.name == "plugin" })!
387+
let helper = (plugin.dependencies.first(where: { $0.name == "helper"})?.target)!
388+
let helperTarget = plan.targetMap[helper.id]!
389+
XCTAssertFalse(try helperTarget.swiftTarget().compileArguments().contains("-static-executable"))
390+
}
325391
}
326392

327393
extension SwiftCommandState {

0 commit comments

Comments
 (0)