Skip to content

Commit 051460e

Browse files
authored
[5.5] [SR-14896] swift build --build-system xcode doesn't respect SWIFT_EXEC (#3596) (#3600)
When using the `xcode` build system, SwiftPM isn't passing down the result of overriding SWIFT_EXEC. The same is true for `-toolchain` and `-sdk` but this will be fixed in a different PR. Besides being incorrect and thus a generally something that should be fixed, this also affects SwiftPM CI, where the newly built compiler isn't being used when cross-compilation is active (since SwiftPM currently implements multi-architecture builds by switching build system to use xcbuild). This is the Swift 5.5 nomination of this fix. rdar://80194565 (cherry picked from commit 9aab44d)
1 parent 1a361f7 commit 051460e

File tree

2 files changed

+49
-10
lines changed

2 files changed

+49
-10
lines changed

Sources/XCBuildSupport/XcodeBuildSystem.swift

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,7 @@ public final class XcodeBuildSystem: SPMBuildCore.BuildSystem {
122122
}
123123

124124
func createBuildParametersFile() throws -> AbsolutePath? {
125-
// We only generate the build parameters file if it's required.
126-
guard !buildParameters.archs.isEmpty else { return nil }
127-
125+
// Generate the run destination parameters.
128126
let runDestination = XCBBuildParameters.RunDestination(
129127
platform: "macosx",
130128
sdk: "macosx",
@@ -133,20 +131,28 @@ public final class XcodeBuildSystem: SPMBuildCore.BuildSystem {
133131
supportedArchitectures: [],
134132
disableOnlyActiveArch: true
135133
)
134+
135+
// Generate a table of any overriding build settings.
136+
var settings: [String: String] = [:]
137+
// Always specify the path of the effective Swift compiler, which was determined in the same way as for the native build system.
138+
settings["SWIFT_EXEC"] = buildParameters.toolchain.swiftCompiler.pathString
139+
// Optionally also set the list of architectures to build for.
140+
if !buildParameters.archs.isEmpty {
141+
settings["ARCHS"] = buildParameters.archs.joined(separator: " ")
142+
}
143+
144+
// Generate the build parameters.
136145
let params = XCBBuildParameters(
137146
configurationName: buildParameters.configuration.xcbuildName,
138-
overrides: .init(commandLine: .init(table: [
139-
"ARCHS": "\(buildParameters.archs.joined(separator: " "))",
140-
])),
147+
overrides: .init(commandLine: .init(table: settings)),
141148
activeRunDestination: runDestination
142149
)
143150

144-
let encoder = JSONEncoder.makeWithDefaults()
145-
151+
// Write out the parameters as a JSON file, and return the path.
152+
let encoder = JSONEncoder.makeWithDefaults()
146153
let data = try encoder.encode(params)
147154
let file = try withTemporaryFile(deleteOnClose: false) { $0.path }
148155
try localFileSystem.writeFileContents(file, bytes: ByteString(data))
149-
150156
return file
151157
}
152158

Tests/CommandsTests/BuildToolTests.swift

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ final class BuildToolTests: XCTestCase {
2626
@discardableResult
2727
private func execute(
2828
_ args: [String],
29+
environment: [String : String]? = nil,
2930
packagePath: AbsolutePath? = nil
3031
) throws -> (stdout: String, stderr: String) {
31-
return try SwiftPMProduct.SwiftBuild.execute(args, packagePath: packagePath)
32+
return try SwiftPMProduct.SwiftBuild.execute(args, packagePath: packagePath, env: environment)
3233
}
3334

3435
func build(_ args: [String], packagePath: AbsolutePath? = nil) throws -> BuildResult {
@@ -280,4 +281,36 @@ final class BuildToolTests: XCTestCase {
280281
}
281282
}
282283
}
284+
285+
func testXcodeBuildSystemOverrides() throws {
286+
#if !os(macOS)
287+
try XCTSkipIf(true, "test requires `xcbuild` and is therefore only supported on macOS")
288+
#endif
289+
fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { path in
290+
// Try building using XCBuild without specifying overrides. This should succeed, and should use the default compiler path.
291+
let defaultOutput = try execute(["-c", "debug", "-v"], packagePath: path).stdout
292+
XCTAssert(defaultOutput.contains(Resources.default.swiftCompiler.pathString), defaultOutput)
293+
294+
// Now try building using XCBuild while specifying a faulty compiler override. This should fail. Note that we need to set the executable to use for the manifest itself to the default one, since it defaults to SWIFT_EXEC if not provided.
295+
var overriddenOutput = ""
296+
do {
297+
overriddenOutput = try execute(["-c", "debug", "-v"], environment: ["SWIFT_EXEC": "/usr/bin/false", "SWIFT_EXEC_MANIFEST": Resources.default.swiftCompiler.pathString], packagePath: path).stdout
298+
XCTFail("unexpected success (was SWIFT_EXEC not overridden properly?)")
299+
}
300+
catch SwiftPMProductError.executionFailure(let error, let stdout, _) {
301+
switch error {
302+
case ProcessResult.Error.nonZeroExit(let result) where result.exitStatus != .terminated(code: 0):
303+
overriddenOutput = stdout
304+
break
305+
default:
306+
XCTFail("`swift build' failed in an unexpected manner")
307+
}
308+
}
309+
catch {
310+
XCTFail("`swift build' failed in an unexpected manner")
311+
}
312+
XCTAssert(overriddenOutput.contains("/usr/bin/false"), overriddenOutput)
313+
}
314+
}
315+
283316
}

0 commit comments

Comments
 (0)