Skip to content

Commit 9ffbb30

Browse files
committed
Merge remote-tracking branch 'source/main' into rconnell/metal-toolchain
2 parents 66e402a + 5547e32 commit 9ffbb30

File tree

5 files changed

+124
-5
lines changed

5 files changed

+124
-5
lines changed

.github/workflows/automerge.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ on:
1313
jobs:
1414
create_merge_pr:
1515
name: Create PR to merge main into release branch
16-
uses: swiftlang/github-workflows/.github/workflows/create_automerge_pr.yml@main
16+
uses: swiftlang/github-workflows/.github/workflows/create_automerge_pr.yml@0.0.1
1717
with:
1818
head_branch: main
1919
base_branch: release/6.3

.github/workflows/pull_request.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ concurrency:
1414
jobs:
1515
tests:
1616
name: Test
17-
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main
17+
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@0.0.1
1818
with:
1919
linux_os_versions: '["amazonlinux2", "bookworm", "noble", "jammy", "rhel-ubi9"]'
2020
linux_swift_versions: '["nightly-main"]'
@@ -30,7 +30,7 @@ jobs:
3030

3131
soundness:
3232
name: Soundness
33-
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
33+
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@0.0.1
3434
with:
3535
license_header_check_project_name: "Swift"
3636
license_header_check_enabled: false

Sources/CoreCommands/SwiftCommandState.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,7 @@ public final class SwiftCommandState {
10591059
swiftSDK: swiftSDK,
10601060
environment: self.environment,
10611061
customTargetInfo: targetInfo,
1062+
observabilityScope: self.observabilityScope,
10621063
fileSystem: self.fileSystem)
10631064
})
10641065
}()
@@ -1075,6 +1076,7 @@ public final class SwiftCommandState {
10751076
swiftSDK: hostSwiftSDK,
10761077
environment: self.environment,
10771078
customTargetInfo: targetInfo,
1079+
observabilityScope: self.observabilityScope,
10781080
fileSystem: self.fileSystem
10791081
)
10801082
})

Sources/PackageModel/UserToolchain.swift

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,8 @@ public final class UserToolchain: Toolchain {
359359
useXcrun: Bool,
360360
environment: Environment,
361361
searchPaths: [AbsolutePath],
362-
fileSystem: any FileSystem
362+
fileSystem: any FileSystem,
363+
observabilityScope: ObservabilityScope? = nil
363364
) throws -> SwiftCompilers {
364365
func validateCompiler(at path: AbsolutePath?) throws {
365366
guard let path else { return }
@@ -371,10 +372,37 @@ public final class UserToolchain: Toolchain {
371372
}
372373

373374
let lookup = { UserToolchain.lookup(variable: $0, searchPaths: searchPaths, environment: environment) }
375+
376+
// Warn if SWIFT_EXEC or SWIFT_EXEC_MANIFEST is set but points to a non-existent or non-executable path
377+
func warnIfInvalid(envVar: String, value: String, resolved: AbsolutePath?) {
378+
guard resolved == nil else { return }
379+
380+
let message: String
381+
if let absolutePath = try? AbsolutePath(validating: value) {
382+
if fileSystem.exists(absolutePath) {
383+
message = "\(envVar) is set to '\(value)' which exists but is not executable; ignoring"
384+
} else {
385+
message = "\(envVar) is set to '\(value)' but the file does not exist; ignoring"
386+
}
387+
} else {
388+
message = "\(envVar) is set to '\(value)' but no executable was found in search paths; ignoring"
389+
}
390+
391+
observabilityScope?.emit(warning: message)
392+
}
393+
374394
// Get overrides.
375395
let SWIFT_EXEC_MANIFEST = lookup("SWIFT_EXEC_MANIFEST")
376396
let SWIFT_EXEC = lookup("SWIFT_EXEC")
377397

398+
// Emit warnings if environment variables are set but lookup failed
399+
if let swiftExecValue = environment["SWIFT_EXEC"], !swiftExecValue.isEmpty {
400+
warnIfInvalid(envVar: "SWIFT_EXEC", value: swiftExecValue, resolved: SWIFT_EXEC)
401+
}
402+
if let swiftExecManifestValue = environment["SWIFT_EXEC_MANIFEST"], !swiftExecManifestValue.isEmpty {
403+
warnIfInvalid(envVar: "SWIFT_EXEC_MANIFEST", value: swiftExecManifestValue, resolved: SWIFT_EXEC_MANIFEST)
404+
}
405+
378406
// Validate the overrides.
379407
try validateCompiler(at: SWIFT_EXEC)
380408
try validateCompiler(at: SWIFT_EXEC_MANIFEST)
@@ -713,6 +741,7 @@ public final class UserToolchain: Toolchain {
713741
customTargetInfo: JSON? = nil,
714742
customLibrariesLocation: ToolchainConfiguration.SwiftPMLibrariesLocation? = nil,
715743
customInstalledSwiftPMConfiguration: InstalledSwiftPMConfiguration? = nil,
744+
observabilityScope: ObservabilityScope? = nil,
716745
fileSystem: any FileSystem = localFileSystem
717746
) throws {
718747
self.swiftSDK = swiftSDK
@@ -736,7 +765,8 @@ public final class UserToolchain: Toolchain {
736765
useXcrun: self.useXcrun,
737766
environment: environment,
738767
searchPaths: self.envSearchPaths,
739-
fileSystem: fileSystem
768+
fileSystem: fileSystem,
769+
observabilityScope: observabilityScope
740770
)
741771
self.swiftCompilerPath = swiftCompilers.compile
742772
self.architectures = swiftSDK.architectures

Tests/PackageModelTests/PackageModelTests.swift

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Basics
1515
@_spi(SwiftPMInternal)
1616
@testable import PackageModel
1717

18+
import _InternalTestSupport
1819
import func TSCBasic.withTemporaryFile
1920
import XCTest
2021

@@ -199,4 +200,90 @@ final class PackageModelTests: XCTestCase {
199200
XCTAssertEqual(compilers.compile, binDirs.first?.appending(expectedExecuable))
200201
}
201202
}
203+
204+
func testDetermineSwiftCompilersWarnsOnInvalidSWIFT_EXEC() throws {
205+
let fs = localFileSystem
206+
try withTemporaryDirectory(removeTreeOnDeinit: true) { tmp in
207+
let toolchainPath = tmp.appending("swift.xctoolchain")
208+
let toolchainBinDir = toolchainPath.appending(components: "usr", "bin")
209+
try fs.createDirectory(toolchainBinDir, recursive: true)
210+
211+
#if os(Windows)
212+
let exeSuffix = ".exe"
213+
#else
214+
let exeSuffix = ""
215+
#endif
216+
217+
// Create a valid swiftc in the toolchain
218+
let validSwiftc = toolchainBinDir.appending("swiftc\(exeSuffix)")
219+
try fs.writeFileContents(validSwiftc, bytes: ByteString(Self.tinyPEBytes))
220+
#if !os(Windows)
221+
try fs.chmod(.executable, path: validSwiftc, options: [])
222+
#endif
223+
224+
// Test 1: SWIFT_EXEC points to non-existent file
225+
do {
226+
let observability = ObservabilitySystem.makeForTesting()
227+
let nonExistentPath = tmp.appending(components: "nonexistent", "path", "to", "swiftc")
228+
let environment: Environment = ["SWIFT_EXEC": nonExistentPath.pathString]
229+
230+
_ = try UserToolchain.determineSwiftCompilers(
231+
binDirectories: [toolchainBinDir],
232+
useXcrun: false,
233+
environment: environment,
234+
searchPaths: [],
235+
fileSystem: fs,
236+
observabilityScope: observability.topScope
237+
)
238+
239+
testDiagnostics(observability.diagnostics) { result in
240+
result.check(diagnostic: .contains("SWIFT_EXEC is set to '\(nonExistentPath.pathString)' but the file does not exist; ignoring"), severity: .warning)
241+
}
242+
}
243+
244+
// Test 2: SWIFT_EXEC points to file that exists but is not executable
245+
do {
246+
let observability = ObservabilitySystem.makeForTesting()
247+
let notExecutablePath = tmp.appending("not-executable")
248+
try fs.writeFileContents(notExecutablePath, bytes: "")
249+
#if !os(Windows)
250+
try fs.chmod(.userUnWritable, path: notExecutablePath, options: [])
251+
#endif
252+
253+
let environment: Environment = ["SWIFT_EXEC": notExecutablePath.pathString]
254+
255+
_ = try UserToolchain.determineSwiftCompilers(
256+
binDirectories: [toolchainBinDir],
257+
useXcrun: false,
258+
environment: environment,
259+
searchPaths: [],
260+
fileSystem: fs,
261+
observabilityScope: observability.topScope
262+
)
263+
264+
testDiagnostics(observability.diagnostics) { result in
265+
result.check(diagnostic: .contains("SWIFT_EXEC is set to '\(notExecutablePath.pathString)' which exists but is not executable; ignoring"), severity: .warning)
266+
}
267+
}
268+
269+
// Test 3: SWIFT_EXEC is not an absolute path and not found in search paths
270+
do {
271+
let observability = ObservabilitySystem.makeForTesting()
272+
let environment: Environment = ["SWIFT_EXEC": "nonexistent-compiler"]
273+
274+
_ = try UserToolchain.determineSwiftCompilers(
275+
binDirectories: [toolchainBinDir],
276+
useXcrun: false,
277+
environment: environment,
278+
searchPaths: [],
279+
fileSystem: fs,
280+
observabilityScope: observability.topScope
281+
)
282+
283+
testDiagnostics(observability.diagnostics) { result in
284+
result.check(diagnostic: .contains("SWIFT_EXEC is set to 'nonexistent-compiler' but no executable was found in search paths; ignoring"), severity: .warning)
285+
}
286+
}
287+
}
288+
}
202289
}

0 commit comments

Comments
 (0)