From a21d67c117414bec2fa9963d98048ef97ce5e269 Mon Sep 17 00:00:00 2001
From: Christoph Reiter <reiter.christoph@gmail.com>
Date: Mon, 23 Dec 2024 06:03:11 +0100
Subject: [PATCH 117/N] venvlauncher: try looking for the versioned .exe
 first and then fall back

By default venvlauncher only looks for python.exe in the python "home",
which fails in the MSYS2 case where there could be multiple python versions
in the same prefix i.e. python.exe could be 3.12 while the venv was created
with python3.13.exe.

Upstream CPython doesn't have this problem since they never have multiple
Python versions in the same prefix.

On the other hand the Python test suite (test_sysconfig specifically) creates
a venv for the uninstalled Python build dir, where only python.exe exists.

To cover both cases we first try to look for the python3.XY.exe and then
fall back to python.exe.
---
 Makefile.pre.in |  4 ++--
 PC/launcher.c   | 16 +++++++++++++---
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/Makefile.pre.in b/Makefile.pre.in
index 28a3a1c..5882484 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -918,10 +918,10 @@ $(BUILDPYTHON):	Programs/python.o $(LINK_PYTHON_DEPS) python_exe.o
 	$(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -municode -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) python_exe.o
 
 $(BUILDVENVLAUNCHER): $(BUILDPYTHON) venvlauncher.o  $(srcdir)/PC/launcher.c
-	$(LINKCC) -D_CONSOLE -DVENV_REDIRECT $(PY_STDMODULE_CFLAGS) -municode -static -static-libgcc -static-libstdc++ venvlauncher.o $(srcdir)/PC/launcher.c -o $@ -lversion
+	$(LINKCC) -D_CONSOLE -DVENV_REDIRECT -DPYTHON_EXECUTABLE_WITH_VERSION="L\"python$(LDVERSION)$(EXE)\"" $(PY_STDMODULE_CFLAGS) -municode -static -static-libgcc -static-libstdc++ venvlauncher.o $(srcdir)/PC/launcher.c -o $@ -lversion
 
 $(BUILDVENVWLAUNCHER): $(BUILDPYTHONW) venvwlauncher.o  $(srcdir)/PC/launcher.c
-	$(LINKCC) -D_WINDOWS -DVENV_REDIRECT $(PY_STDMODULE_CFLAGS) -mwindows -municode -static -static-libgcc -static-libstdc++ venvwlauncher.o $(srcdir)/PC/launcher.c -o $@ -lversion
+	$(LINKCC) -D_WINDOWS -DVENV_REDIRECT -DPYTHON_EXECUTABLE_WITH_VERSION="L\"pythonw$(LDVERSION)$(EXE)\"" $(PY_STDMODULE_CFLAGS) -mwindows -municode -static -static-libgcc -static-libstdc++ venvwlauncher.o $(srcdir)/PC/launcher.c -o $@ -lversion
 
 platform: $(PYTHON_FOR_BUILD_DEPS) pybuilddir.txt
 	$(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform
diff --git a/PC/launcher.c b/PC/launcher.c
index 796bf73..6c8cd76 100644
--- a/PC/launcher.c
+++ b/PC/launcher.c
@@ -1931,7 +1931,8 @@ process(int argc, wchar_t ** argv)
         if (!cch) {
             error(0, L"Cannot determine memory for home path");
         }
-        cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 4; /* include sep, null and quotes */
+        cch += (DWORD)max(wcslen(PYTHON_EXECUTABLE_WITH_VERSION),
+                          wcslen(PYTHON_EXECUTABLE)) + 4; /* include sep, null and quotes */
         executable = (wchar_t *)malloc(cch * sizeof(wchar_t));
         if (executable == NULL) {
             error(RC_NO_MEMORY, L"A memory allocation failed");
@@ -1949,13 +1950,22 @@ process(int argc, wchar_t ** argv)
             executable[cch_actual++] = L'\\';
             executable[cch_actual] = L'\0';
         }
-        if (wcscat_s(&executable[1], cch - 1, PYTHON_EXECUTABLE)) {
+        if (wcscat_s(&executable[1], cch - 1, PYTHON_EXECUTABLE_WITH_VERSION)) {
             error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'",
                   venv_cfg_path);
         }
         /* there's no trailing quote, so we only have to skip one character for the test */
+        // Check if the versioned executable (PYTHON_EXECUTABLE_WITH_VERSION) exists first
         if (GetFileAttributesW(&executable[1]) == INVALID_FILE_ATTRIBUTES) {
-            error(RC_NO_PYTHON, L"No Python at '%ls'", executable);
+            // If not found, try PYTHON_EXECUTABLE
+            executable[cch_actual] = L'\0';  // Reset the path
+            if (wcscat_s(&executable[1], cch - 1, PYTHON_EXECUTABLE)) {
+                error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'",
+                      venv_cfg_path);
+            }
+            if (GetFileAttributesW(&executable[1]) == INVALID_FILE_ATTRIBUTES) {
+                error(RC_NO_PYTHON, L"No Python at '%ls'", executable);
+            }
         }
         /* now append the final quote */
         wcscat_s(executable, cch, L"\"");
