Skip to content

[windows] add a check for a matching python architecture #1942

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

charles-zablit
Copy link

Fixes swiftlang/swift#79981.

On Windows, if there is a mismatch between the architecture of the installed Python and the architecture of the toolchain, swift repl will crash immediately with 0xC000007B. This patch adds a check to warn the user of the architecture mismatch right before invoking the REPL, which will crash.

We should also look into adding a warning at the end of the Swift GUI installer.

@charles-zablit
Copy link
Author

@swift-ci please test

@charles-zablit charles-zablit self-assigned this Jun 27, 2025
@charles-zablit charles-zablit force-pushed the charles-zablit/windows/warn-architecture-mismatch-python branch from a1ef87c to def835d Compare June 27, 2025 18:22
@charles-zablit
Copy link
Author

@swift-ci please test

@adrian-prantl adrian-prantl requested a review from artemcm June 27, 2025 18:25
@@ -1434,6 +1438,42 @@ extension Driver {

return (.subcommand(subcommand), updatedArgs)
}

private static func checkIfMatchingPythonArch() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment here explaining that this exists because users who install the x86 toolchain on arm64 windows will get an extremely cryptic error message otherwise?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completely forgot to add it after our discussion. Fixed 👍

@adrian-prantl
Copy link
Contributor

This is going to be huge usability improvement. I'd like @artemcm to sign off though due the unusual nature of the patch.

@charles-zablit charles-zablit force-pushed the charles-zablit/windows/warn-architecture-mismatch-python branch from def835d to f163a58 Compare June 27, 2025 22:50
@charles-zablit
Copy link
Author

@swift-ci please test

@artemcm
Copy link
Contributor

artemcm commented Jun 27, 2025

@swift-ci please test Windows platform

Copy link
Contributor

@artemcm artemcm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to find a way to do this without executing Python code? e.g. can we locate the python.exe executable and do something equivalent to what file does on UNIX?

/// of the Python installation.
///
/// When installing the x86 toolchain on ARM64 Windows, if the user does not
/// install an x86 version of Python, they will get acryptic error message
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// install an x86 version of Python, they will get acryptic error message
/// install an x86 version of Python, they will get a cryptic error message

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is quite non-cryptic IMO. 0xC000007B is a NTSTATUS; 0xC => Kernel; 0x7B = 123 = ERROR_INVALID_NAME. This is the same behaviour as Linux when the loader is not found.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant cryptic from the point of view of the user. They only get this error message with no indication of what is failing and how to fix it (see the screenshot below). I will change the wording in the comment 👍

lldb_crash

return;
}
if !output.lowercased().contains(arch) {
stderrStream.send("There is an architecture mismatch between the installed toolchain and the resolved Python's architecture:\n")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please use the driver's diagnosticEngine and define a new .warning for this message instead of using the stderr stream directly?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to using the diagnosticsEngine.

#else
return;
#endif
let process = Process()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the time we are in invocationRunMode we have an instance of the DriverExecutor, so we should plumb that here and use it to run this subprocess instead of using Process APIs directly. You can see an example of this being done in xcrunFind.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to using DriverExecutor.

@artemcm artemcm requested a review from compnerd June 27, 2025 23:29
@@ -1425,6 +1426,9 @@ extension Driver {
if firstArg == "repl" {
updatedArgs.remove(at: 1)
updatedArgs.append("-repl")
#if os(Windows)
checkIfMatchingPythonArch()
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this problem not exist on Darwin as well? If you run arch -m x86_64 swift repl that should fail similarly.

Copy link
Author

@charles-zablit charles-zablit Jun 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not able to get the problem to happen on Darwin. arch -x86_64 swift repl starts the REPL correctly.

Maybe we should bring this check to other platforms as well. I focused on Windows in this patch because all the reports I received were from Windows users on ARM64 machines.

/// of the Python installation.
///
/// When installing the x86 toolchain on ARM64 Windows, if the user does not
/// install an x86 version of Python, they will get acryptic error message
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is quite non-cryptic IMO. 0xC000007B is a NTSTATUS; 0xC => Kernel; 0x7B = 123 = ERROR_INVALID_NAME. This is the same behaviour as Linux when the loader is not found.

#endif
let process = Process()
process.executableURL = URL(fileURLWithPath: "C:\\Windows\\System32\\cmd.exe")
process.arguments = ["/c", "python.exe", "-c", "import platform; print(platform.machine())"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a pretty bad way to check if the DLL matches. Are we checking if python matches the architecture or if the current runtime as indicated by PYTHONHOME matchess?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are checking that the Python that is resolved from the Path matches the architecture. I have tried some combinations of configurations:

Everything below was run on an ARM64 Windows VM with the x86 toolchain installed.

Python Path pointing to PYTHONHOME pointing to Output
ARM64 Nothing Crash with warning
ARM64 ARM64 Crash with warning
ARM64 AMD64 Crash with warning
AMD64 Nothing Launches fine
AMD64 ARM64 Fatal Python error: init_fs_encoding
AMD64 AMD64 Launches fine

I think that checking if the Python that is in the Path is the correct one is enough. If there is a mismatch between the Python that is in the Path and the one that is in PYTHONHOME, that is another problem and there are enough information printed to the user to help them figure it out.

@charles-zablit
Copy link
Author

Is it possible to find a way to do this without executing Python code? e.g. can we locate the python.exe executable and do something equivalent to what file does on UNIX?

There is no non third party alternative to file on Windows. There is a way to check programmatically that python.exe is the correct architecture according to this SO thread. I'm happy to try to implement this in Swift, but I think it will add some complexity to the code compared to just calling Python?

@artemcm
Copy link
Contributor

artemcm commented Jun 30, 2025

Is it possible to find a way to do this without executing Python code? e.g. can we locate the python.exe executable and do something equivalent to what file does on UNIX?

There is no non third party alternative to file on Windows. There is a way to check programmatically that python.exe is the correct architecture according to this SO thread. I'm happy to try to implement this in Swift, but I think it will add some complexity to the code compared to just calling Python?

If we can reduce the set of dependencies the driver needs in order to perform all the tasks it needs to then we should. Even though swift repl does depend on having Python on the system, it would be really nice to avoid the driver having to - especially by firing up the interpreter and executing code.

Furthermore, as-is, when executing this check in the driver on a system where Python is missing, the user will still get this warning with some surprising pythonArchitecture contents printed.

I know there is Dumpbin.exe that is present on the system when Windows SDK is installed, which can act similarly to file, but it also doesn't seem all that bad to try and read the file machine type from the COFF header - that way we can ensure the binary exists in the first place and emit an appropriate error in that case too.

@artemcm
Copy link
Contributor

artemcm commented Jun 30, 2025

To add to the above comment, the driver today on most code paths plans the build without launching any subprocesses at all, which is crucial in some environments where the driver is used as a library in an environment where it is not allowed to launch processes itself.

While I'm not aware of this being a hard requirement on Windows today, it's still worthwhile to avoid adding new process launch sites when we can.

@charles-zablit
Copy link
Author

@swift-ci please test

@charles-zablit
Copy link
Author

The COFF header approach seems to work fine 👍

I am unsure what's the best way to resolve the path to python. My current approach manually searches for it with lookupExecutablePath to avoid executing a subprocess.
However, we could use where.exe to achieve the same thing and use the actual resolution mechanism from Windows.

@compnerd
Copy link
Member

compnerd commented Jul 1, 2025

The COFF header approach seems to work fine 👍

I am unsure what's the best way to resolve the path to python. My current approach manually searches for it with lookupExecutablePath to avoid executing a subprocess.

However, we could use where.exe to achieve the same thing and use the actual resolution mechanism from Windows.

Looking up in Path is the right thing to do - if it is not in Path, it won't be found anyway.

Copy link
Contributor

@artemcm artemcm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a couple of small nits, but otherwise this looks good, thank you!

@charles-zablit charles-zablit force-pushed the charles-zablit/windows/warn-architecture-mismatch-python branch from 47bc64b to ede165c Compare July 1, 2025 16:59
@charles-zablit
Copy link
Author

I have a couple of small nits, but otherwise this looks good, thank you!

Thanks! Fixed the issues and added documentation to the new methods.

@charles-zablit charles-zablit enabled auto-merge July 1, 2025 17:00
@artemcm
Copy link
Contributor

artemcm commented Jul 1, 2025

@swift-ci test

@artemcm
Copy link
Contributor

artemcm commented Jul 1, 2025

@swift-ci test Windows platform

@artemcm
Copy link
Contributor

artemcm commented Jul 1, 2025

@swift-ci test Windows platform

@charles-zablit
Copy link
Author

charles-zablit commented Jul 1, 2025

Odd, the build error happens only on Windows:

C:\Users\swift-ci\jenkins\workspace\swift-driver-PR-windows\swift-driver\Sources\SwiftDriver\Driver\Driver.swift:870:13: error: cannot find 'checkIfMatchingPythonArch' in scope
 868 |     #if os(Windows)
 869 |       if args[1] == "-repl" {  // a `-` gets automatically added to `swift repl`.
 870 |         try checkIfMatchingPythonArch(
     |             `- error: cannot find 'checkIfMatchingPythonArch' in scope
 871 |           cwd: ProcessEnv.cwd, env: env, diagnosticsEngine: diagnosticsEngine)
 872 |       }

Do I need to manually import the method?

EDIT: Added the file to CMakeList.txt.

@charles-zablit
Copy link
Author

@swift-ci please test windows platform

@charles-zablit charles-zablit force-pushed the charles-zablit/windows/warn-architecture-mismatch-python branch from ede165c to 4c094d5 Compare July 2, 2025 11:06
@charles-zablit
Copy link
Author

@swift-ci please test

@artemcm
Copy link
Contributor

artemcm commented Jul 2, 2025

@swift-ci please test windows platform

static func readWindowsExecutableArchitecture(
cwd: AbsolutePath?, env: [String: String], filename: String
) -> Self {
let searchPaths = getEnvSearchPaths(pathString: env["Path"], currentWorkingDirectory: cwd)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Environment on Windows is case-insensitive and this WILL lead to very subtle bugs in cases where Path happens to be cased as PATH.

Do we have an Environment abstraction available to SwiftDriver like we have in SwiftPM that we can use instead of a raw [String: String] dictionary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have TSCBasic.ProcessEnvironmentBlock which I just transitioned most uses in the driver to: fe4a66c.

Though this is missing a bunch of good stuff that SPM's Environment has, it does use ProcessEnvironmentKey which appears to differentiate case sensitivity for Windows in particular. @charles-zablit we should switch to that here.

/// When installing the x86 toolchain on ARM64 Windows, if the user does not
/// install an x86 version of Python, they will get acryptic error message
/// when running lldb (`0xC000007B`). Calling this function before invoking
/// lldb gives them a warning to help troublshoot the issue.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "troublshoot" mis-spelled

/// of the Python installation.
///
/// When installing the x86 toolchain on ARM64 Windows, if the user does not
/// install an x86 version of Python, they will get acryptic error message
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "acryptic" => "a cryptic"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

swift repl crashes immediately on Windows ARM64
5 participants