@@ -17,6 +17,7 @@ import class TSCBasic.DiagnosticsEngine
17
17
import class TSCBasic. UnknownLocation
18
18
import enum TSCBasic. ProcessEnv
19
19
import func TSCBasic. withTemporaryDirectory
20
+ import func TSCBasic. lookupExecutablePath
20
21
import protocol TSCBasic. DiagnosticData
21
22
import protocol TSCBasic. FileSystem
22
23
import protocol TSCBasic. OutputByteStream
@@ -869,7 +870,7 @@ public struct Driver {
869
870
#if os(Windows)
870
871
if args [ 1 ] == " -repl " { // a `-` gets automatically added to `swift repl`.
871
872
try Driver . checkIfMatchingPythonArch (
872
- executor : executor , env: env, diagnosticsEngine: diagnosticsEngine)
873
+ cwd : ProcessEnv . cwd , env: env, diagnosticsEngine: diagnosticsEngine)
873
874
}
874
875
#endif
875
876
@@ -1452,24 +1453,20 @@ extension Driver {
1452
1453
/// when running lldb (`0xC000007B`). Calling this function before invoking
1453
1454
/// lldb gives them a warning to help troublshoot the issue.
1454
1455
public static func checkIfMatchingPythonArch(
1455
- executor : DriverExecutor , env: [ String : String ] , diagnosticsEngine: DiagnosticsEngine
1456
+ cwd : AbsolutePath ? , env: [ String : String ] , diagnosticsEngine: DiagnosticsEngine
1456
1457
) throws {
1457
1458
#if arch(arm64)
1458
- let toolchainArchitecture = " arm64 "
1459
+ let toolchainArchitecture = ExecutableArchitecture . arm64
1459
1460
#elseif arch(x86_64)
1460
- let toolchainArchitecture = " amd64 "
1461
+ let toolchainArchitecture = ExecutableArchitecture . x64
1461
1462
#elseif arch(x86)
1462
- let toolchainArchitecture = " x86 "
1463
+ let toolchainArchitecture = ExecutableArchitecture . x86
1463
1464
#else
1464
1465
return ;
1465
1466
#endif
1466
- let pythonArchitecture = try executor. checkNonZeroExit (
1467
- args: " python.exe " , " -c " , " import platform; print(platform.machine()) " ,
1468
- environment: env
1469
- ) . trimmingCharacters ( in: . whitespacesAndNewlines)
1470
- . lowercased ( )
1467
+ let pythonArchitecture = readWindowsExecutableArchitecture ( cwd: cwd, env: env)
1471
1468
1472
- if ! pythonArchitecture. contains ( toolchainArchitecture ) {
1469
+ if toolchainArchitecture != pythonArchitecture {
1473
1470
diagnosticsEngine. emit (
1474
1471
. warning(
1475
1472
"""
@@ -1479,6 +1476,68 @@ extension Driver {
1479
1476
""" ) )
1480
1477
}
1481
1478
}
1479
+
1480
+ enum ExecutableArchitecture : String {
1481
+ case x86 = " X86 "
1482
+ case x64 = " X64 "
1483
+ case arm64 = " ARM64 "
1484
+ case unknown = " Unknown "
1485
+
1486
+ static func fromPEMachineByte( machine: UInt16 ) -> Self {
1487
+ // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
1488
+ switch machine {
1489
+ case 0x014c : return . x86
1490
+ case 0x8664 : return . x64
1491
+ case 0xAA64 : return . arm64
1492
+ default : return . unknown
1493
+ }
1494
+ }
1495
+ }
1496
+
1497
+ static func readWindowsExecutableArchitecture( cwd: AbsolutePath ? , env: [ String : String ] )
1498
+ -> ExecutableArchitecture
1499
+ {
1500
+ guard let pathEnv = env [ " Path " ] ? . split ( separator: " ; " ) else {
1501
+ return . unknown
1502
+ }
1503
+ let searchPaths = pathEnv. compactMap { path in
1504
+ try ? AbsolutePath ( validating: String ( path) )
1505
+ }
1506
+ guard let filePath = lookupExecutablePath (
1507
+ filename: " python.exe " , currentWorkingDirectory: cwd, searchPaths: searchPaths)
1508
+ else {
1509
+ return . unknown
1510
+ }
1511
+ guard let fileHandle = FileHandle ( forReadingAtPath: filePath. pathString) else {
1512
+ return . unknown
1513
+ }
1514
+
1515
+ defer { fileHandle. closeFile ( ) }
1516
+
1517
+ // https://upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg
1518
+ fileHandle. seek ( toFileOffset: 0x3C )
1519
+ guard let offsetPointer = try ? fileHandle. read ( upToCount: 4 ) ,
1520
+ offsetPointer. count == 4
1521
+ else {
1522
+ return . unknown
1523
+ }
1524
+
1525
+ let peHeaderOffset = offsetPointer. withUnsafeBytes { $0. load ( as: UInt32 . self) }
1526
+
1527
+ fileHandle. seek ( toFileOffset: UInt64 ( peHeaderOffset) )
1528
+ guard let coffHeader = try ? fileHandle. read ( upToCount: 6 ) , coffHeader. count == 6 else {
1529
+ return . unknown
1530
+ }
1531
+
1532
+ let signature = coffHeader. prefix ( 4 )
1533
+ let machineBytes = coffHeader. suffix ( 2 )
1534
+
1535
+ guard signature == Data ( [ 0x50 , 0x45 , 0x00 , 0x00 ] ) else {
1536
+ return . unknown
1537
+ }
1538
+
1539
+ return . fromPEMachineByte( machine: machineBytes. withUnsafeBytes { $0. load ( as: UInt16 . self) } )
1540
+ }
1482
1541
}
1483
1542
1484
1543
extension Driver {
0 commit comments