|
| 1 | +/* |
| 2 | + * Copyright 2025 The Emscripten Authors. All rights reserved. |
| 3 | + * Emscripten is available under two separate licenses, the MIT license and the |
| 4 | + * University of Illinois/NCSA Open Source License. Both these licenses can be |
| 5 | + * found in the LICENSE file. |
| 6 | + */ |
| 7 | + |
| 8 | +#include <process.h> // For _spawnvp |
| 9 | +#include <stdio.h> |
| 10 | +#include <stdlib.h> // For _splitpath_s, _makepath_s, GetEnvironmentVariable |
| 11 | +#include <string.h> |
| 12 | +#include <windows.h> |
| 13 | + |
| 14 | +#if 1 |
| 15 | +#define dbg printf |
| 16 | +#else |
| 17 | +#define dbg(...) |
| 18 | +#endif |
| 19 | + |
| 20 | +void findPythonExePath(char* pythonExePath, size_t bufferSize) { |
| 21 | + char envPythonPath[MAX_PATH]; // Temporary buffer for environment variable |
| 22 | + |
| 23 | + // Attempt to retrieve the value of the "PYTHON" environment variable. |
| 24 | + DWORD result = GetEnvironmentVariable("PYTHON", envPythonPath, MAX_PATH); |
| 25 | + |
| 26 | + if (result > 0 && result < MAX_PATH) { |
| 27 | + // If the "PYTHON" environment variable is set and its value fits within the |
| 28 | + // buffer, use this explicit path. strncpy_s is used for safe string |
| 29 | + // copying. |
| 30 | + strncpy_s(pythonExePath, bufferSize, envPythonPath, _TRUNCATE); |
| 31 | + dbg("Launcher: Using PYTHON environment variable: %s\n", pythonExePath); |
| 32 | + return; |
| 33 | + } |
| 34 | + |
| 35 | + // If "PYTHON" environment variable is not set or its value is too long, |
| 36 | + // default to "python.exe". The _spawnvp function in main() will then |
| 37 | + // search for "python.exe" in the system's PATH. |
| 38 | + strcpy(pythonExePath, "python.exe"); |
| 39 | + dbg("Launcher: PYTHON environment variable not found or invalid, defaulting " |
| 40 | + "to 'python.exe' (will use PATH search).\n"); |
| 41 | +} |
| 42 | + |
| 43 | +int main() { |
| 44 | + char exePath[MAX_PATH]; |
| 45 | + char drive[_MAX_DRIVE]; |
| 46 | + char dir[_MAX_DIR]; |
| 47 | + char fname[_MAX_FNAME]; |
| 48 | + char ext[_MAX_EXT]; |
| 49 | + char pythonScriptPath[MAX_PATH]; |
| 50 | + char pythonExeToLaunch[MAX_PATH]; // Buffer to store the determined Python |
| 51 | + // executable path |
| 52 | + |
| 53 | + // Call the subroutine to determine the Python executable path and its |
| 54 | + // discovery method. The return value (discoveryMethod) is no longer used for |
| 55 | + // adding -Xutf8. |
| 56 | + findPythonExePath(pythonExeToLaunch, MAX_PATH); |
| 57 | + |
| 58 | + // 1. Get the full path of the current executable. |
| 59 | + if (GetModuleFileName(NULL, exePath, MAX_PATH) == 0) { |
| 60 | + fprintf(stderr, "Error: Could not get executable path (Error code: %lu)\n", GetLastError()); |
| 61 | + return 1; |
| 62 | + } |
| 63 | + |
| 64 | + // 2. Split the executable path into its components (drive, directory, file |
| 65 | + // name, extension). |
| 66 | + _splitpath_s(exePath, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT); |
| 67 | + |
| 68 | + // 3. Construct the Python script path. It uses the same base name as the |
| 69 | + // launcher but with a ".py" extension, residing in the same directory. |
| 70 | + _makepath_s(pythonScriptPath, MAX_PATH, drive, dir, fname, ".py"); |
| 71 | + |
| 72 | + dbg("Launcher: Executable path: %s\n", exePath); |
| 73 | + dbg("Launcher: Python script to run: %s\n", pythonScriptPath); |
| 74 | + |
| 75 | + // 4. Prepare arguments for the Python executable. |
| 76 | + // The arguments are now always 3: python_exe, script_path, and NULL |
| 77 | + // terminator. |
| 78 | + char* argv[] = { |
| 79 | + pythonExeToLaunch, // Use the path determined by findPythonExePath |
| 80 | + pythonScriptPath, |
| 81 | + NULL}; |
| 82 | + |
| 83 | + // Check for EM_WORKAROUND_PYTHON_BUG_34780 environment variable |
| 84 | + if (GetEnvironmentVariable("EM_WORKAROUND_PYTHON_BUG_34780", NULL, 0) > 0) { |
| 85 | + dbg("Launcher: EM_WORKAROUND_PYTHON_BUG_34780 is set so closing stdin\n"); |
| 86 | + fclose(stdin); |
| 87 | + } |
| 88 | + |
| 89 | + // 5. Launch the Python executable and wait for it to finish. |
| 90 | + // _spawnvp searches for the program specified in argv[0] in the system's PATH |
| 91 | + // (if argv[0] is not a full path) and executes it. _P_WAIT ensures the |
| 92 | + // launcher waits for the Python process to complete before continuing. |
| 93 | + intptr_t result = _spawnvp(_P_WAIT, argv[0], argv); |
| 94 | + |
| 95 | + if (result == -1) { |
| 96 | + // If _spawnvp returns -1, an error occurred during the launch of the child |
| 97 | + // process. |
| 98 | + perror("Launcher Error: Failed to launch python.exe"); |
| 99 | + return 1; // Set a specific exit code to indicate launch failure. |
| 100 | + } |
| 101 | + |
| 102 | + // If successful, the return value of _spawnvp is the exit code of the spawned |
| 103 | + // process. |
| 104 | + int exitCode = (int)result; |
| 105 | + dbg("Launcher: python.exe exited with code: %d\n", exitCode); |
| 106 | + return exitCode; |
| 107 | +} |
0 commit comments