From d541544bc7b68cb3419e3ef160b3f8f10f12760e Mon Sep 17 00:00:00 2001 From: Nicholas FitzRoy-Dale Date: Mon, 5 Aug 2024 20:49:03 +1000 Subject: [PATCH] Don't hard-code Python dir. --- Windows/EmbedPython.cpp | 33 +++++++--------------- Windows/EmbedPython.h | 4 +-- Windows/Rime.vcxproj | 11 ++++---- Windows/RimePython.cpp | 61 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 74 insertions(+), 35 deletions(-) diff --git a/Windows/EmbedPython.cpp b/Windows/EmbedPython.cpp index dda57d1..59de563 100644 --- a/Windows/EmbedPython.cpp +++ b/Windows/EmbedPython.cpp @@ -1,4 +1,5 @@ #include +#include #define PY_SSIZE_T_CLEAN @@ -18,9 +19,11 @@ FILE* logFile = nullptr; struct NewPythonThreadStartupInfo { - const wchar_t* pythonInstallDir; + const std::wstring pythonInstallDir; void(*onStart)(void *); void *param; + + NewPythonThreadStartupInfo(const std::wstring pythonInstallDir, void(*onStart)(void *), void *param) : pythonInstallDir(pythonInstallDir), onStart(onStart), param(param) {} }; static PyObject* PythonWriteImpl(PyObject* self, PyObject* args) @@ -68,25 +71,13 @@ static DWORD WINAPI PythonThread(LPVOID lpParam) PyConfig config; PyConfig_InitIsolatedConfig(&config); - config.home = (wchar_t *)info->pythonInstallDir; + // TODO: Do we need this copy? + config.home = new wchar_t[info->pythonInstallDir.size() + 1]; + wcscpy_s(config.home, info->pythonInstallDir.size() + 1, info->pythonInstallDir.c_str()); + OutputDebugString(L"Initializing Python from config...\n"); OutputDebugString(config.home); -#if 0 - size_t maxLen = wcslen(info->pythonInstallDir) + wcslen(pythonZipSuffix) + 2; - auto pythonZip = new wchar_t[maxLen]; - - swprintf_s(pythonZip, maxLen, L"%s\\%s", info->pythonInstallDir, pythonZipSuffix); - PyConfig_SetString(&config, &config.pythonpath_env, pythonZip); - - delete[] pythonZip; - - auto pythonExePath = new wchar_t[wcslen(info->pythonInstallDir) + wcslen(pythonExeSuffix) + 2]; - swprintf_s(pythonExePath, wcslen(info->pythonInstallDir) + wcslen(pythonExeSuffix) + 2, L"%s\\%s", info->pythonInstallDir, pythonExeSuffix); - PyConfig_SetString(&config, &config.executable, pythonExePath); -#endif - - Py_InitializeFromConfig(&config); // Ensure that we have sys.stdout and sys.stderr objects. These may not exist if running from a GUI. @@ -154,13 +145,9 @@ static DWORD WINAPI PythonThread(LPVOID lpParam) } -void InitEmbedPython(const wchar_t *pythonInstallDir, void(*onStart)(void *), wchar_t *logFilename, void *param) +void InitEmbedPython(const std::wstring pythonInstallDir, void(*onStart)(void *), wchar_t *logFilename, void *param) { - NewPythonThreadStartupInfo* info = new NewPythonThreadStartupInfo(); - - info->pythonInstallDir = pythonInstallDir; - info->onStart = onStart; - info->param = param; + NewPythonThreadStartupInfo* info = new NewPythonThreadStartupInfo(pythonInstallDir, onStart, param); if (logFilename) { diff --git a/Windows/EmbedPython.h b/Windows/EmbedPython.h index 96e973d..308c650 100644 --- a/Windows/EmbedPython.h +++ b/Windows/EmbedPython.h @@ -1,5 +1,5 @@ #pragma once +#include - -void InitEmbedPython(const wchar_t* pythonInstallDir, void(*onStart)(void*), wchar_t* logFilename, void* param); +void InitEmbedPython(const std::wstring pythonInstallDir, void(*onStart)(void*), wchar_t* logFilename, void* param); void StopEmbedPythonThread(); \ No newline at end of file diff --git a/Windows/Rime.vcxproj b/Windows/Rime.vcxproj index d6e1d03..7b0ea49 100644 --- a/Windows/Rime.vcxproj +++ b/Windows/Rime.vcxproj @@ -106,16 +106,15 @@ _DEBUG;_WINDOWS;%(PreprocessorDefinitions) true stdcpp17 + MultiThreadedDebugDLL Windows - true - $(PythonLibs);%(AdditionalLibraryDirectories) python3.dll;python312.dll + $(PythonLibs);%(AdditionalLibraryDirectories) + $(CoreLibraryDependencies);%(AdditionalDependencies) - - true - + @@ -125,6 +124,8 @@ true NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true + stdcpp17 + MultiThreadedDLL Windows diff --git a/Windows/RimePython.cpp b/Windows/RimePython.cpp index fd79d99..69e269a 100644 --- a/Windows/RimePython.cpp +++ b/Windows/RimePython.cpp @@ -1,4 +1,10 @@ #include +#include +#include +#include +#include "PathCch.h" + + #define PY_SSIZE_T_CLEAN @@ -80,19 +86,64 @@ static void OnPythonThreadStarted(void *data) return; } - PyRun_SimpleFileEx(fp, "launch.py", 1); + // We read launch.py and run it as a string so as to avoid passing FILE pointers from the launcher to the Python library. + // This is because the Nuget version of the Python library we are using includes only a release build. Building + // a debug build of the rest of the project results in FILE structures that are incompatible with the release build + // and cause weird crashes deep inside msvcrt. + fseek(fp, 0, SEEK_END); + size_t size = ftell(fp); + fseek(fp, 0, SEEK_SET); + char *buffer = new char[size + 1]; + fread(buffer, 1, size, fp); + buffer[size] = 0; + + fclose(fp); + + PyRun_SimpleString(buffer); + + delete[] buffer; +} + +static std::optional getPythonDir() +{ + // Try to find the Python install dir as either below the executable or below the cwd. + // 1. Try below the executable. + wchar_t pythonInstallDir[MAX_PATH]; + + GetModuleFileNameW(NULL, pythonInstallDir, MAX_PATH); + PathCchRemoveFileSpec(pythonInstallDir, MAX_PATH); + wcscat_s(pythonInstallDir, MAX_PATH, L"\\python"); + + if (GetFileAttributesW(pythonInstallDir) == INVALID_FILE_ATTRIBUTES) + { + // 2. Try below the cwd. + GetCurrentDirectoryW(MAX_PATH, pythonInstallDir); + wcscat_s(pythonInstallDir, MAX_PATH, L"\\python"); + + if (GetFileAttributesW(pythonInstallDir) == INVALID_FILE_ATTRIBUTES) + { + OutputDebugString(L"Python not found\n"); + return nullptr; + } + } + + return std::wstring(pythonInstallDir); } const wchar_t *launchPySuffix = L"launch.py"; void *StartRimeServer(void(*OnServerStartedFn)(void*), void* data) { - auto pythonInstallDir = L"C:\\Users\\wzddm\\source\\rime-release-test\\rime-windows\\python"; + auto pythonInstallDir = getPythonDir(); + if (!pythonInstallDir.has_value()) + { + return nullptr; + } - size_t maxLen = wcslen(pythonInstallDir) + wcslen(launchPySuffix) + 2; + size_t maxLen = pythonInstallDir->size() + wcslen(launchPySuffix) + 2; auto launchPy = new wchar_t[maxLen]; - swprintf_s(launchPy, maxLen, L"%s\\%s", pythonInstallDir, launchPySuffix); + swprintf_s(launchPy, maxLen, L"%s\\%s", pythonInstallDir->c_str(), launchPySuffix); OnRimeServerStartedStruct* onRimeServerStarted = new OnRimeServerStartedStruct(); onRimeServerStarted->OnServerStarted = OnServerStartedFn; @@ -106,7 +157,7 @@ void *StartRimeServer(void(*OnServerStartedFn)(void*), void* data) CreateDirectoryW(LogPath, NULL); wcscat_s(LogPath, MAX_PATH, L"\\rime.log"); - InitEmbedPython(pythonInstallDir, OnPythonThreadStarted, LogPath, onRimeServerStarted); + InitEmbedPython(pythonInstallDir.value(), OnPythonThreadStarted, LogPath, onRimeServerStarted); return onRimeServerStarted; }