Skip to content

Commit e39a8cc

Browse files
committed
Copy conda DLLs to build dir at configure time instead of manipulating DLL search paths
1 parent 5bd8240 commit e39a8cc

2 files changed

Lines changed: 20 additions & 25 deletions

File tree

Libs/Application/Job/PythonWorker.cpp

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -237,34 +237,14 @@ bool PythonWorker::init() {
237237

238238
#ifdef _WIN32
239239
if (using_bundled) {
240-
// Register DLL directories via AddDllDirectory BEFORE Python init.
241-
// We do NOT call SetDefaultDllDirectories here — Python 3.8+ calls
242-
// SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) internally
243-
// during Py_Initialize(), which enables LOAD_LIBRARY_SEARCH_USER_DIRS.
244-
// Directories registered via AddDllDirectory before that point will then
245-
// be searched for implicit DLL dependencies of .pyd extensions.
240+
// Conda's .pyd extensions need DLLs from Library/bin/ (e.g. libexpat.dll).
241+
// In the build tree, these are copied next to the executables by CMake.
242+
// In the installed app, critical DLLs are in bin/ from InstallBundledPython.
243+
// Prepend key directories to PATH as a safety net.
246244
QString dlls_dir = python_home + "/DLLs";
247245
QString app_dir = QCoreApplication::applicationDirPath();
248-
AddDllDirectory(dlls_dir.toStdWString().c_str());
249-
AddDllDirectory(python_home.toStdWString().c_str());
250-
AddDllDirectory(app_dir.toStdWString().c_str());
251-
252-
// In the build tree, PYTHONHOME points to conda's prefix. Conda's .pyd
253-
// files need DLLs from conda's Library/bin/ (e.g. libexpat.dll for pyexpat).
254-
QString conda_library_bin = python_home + "/Library/bin";
255-
if (QDir(conda_library_bin).exists()) {
256-
AddDllDirectory(conda_library_bin.toStdWString().c_str());
257-
}
258-
259-
// Also prepend to PATH for any DLL loading that happens before Python's
260-
// SetDefaultDllDirectories takes effect.
261246
QString current_path = qgetenv("PATH");
262-
QString new_path = dlls_dir + ";" + python_home + ";" + app_dir;
263-
if (QDir(conda_library_bin).exists()) {
264-
new_path += ";" + conda_library_bin;
265-
}
266-
new_path += ";" + current_path;
267-
qputenv("PATH", new_path.toUtf8());
247+
qputenv("PATH", (dlls_dir + ";" + python_home + ";" + app_dir + ";" + current_path).toUtf8());
268248

269249
SW_LOG("Registered bundled Python DLL directories");
270250
} else {

cmake/BundledPython.cmake

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,21 @@ if(WIN32)
106106
# On Windows, Python3_EXECUTABLE is directly in the conda prefix (no bin/ subdir)
107107
set(_conda_prefix "${_conda_prefix}")
108108
message(STATUS "Windows: skipping conda extension copy (bundled Python has its own DLLs)")
109+
110+
# Copy conda's Library/bin DLLs to the build output directory so the embedded
111+
# interpreter can find them. Conda's .pyd extensions (pyexpat, _ssl, etc.) have
112+
# implicit DLL dependencies (libexpat, libssl, etc.) in Library/bin/. In the
113+
# build tree, the application directory is always searched for DLLs, so placing
114+
# them next to the executables is the most reliable approach.
115+
set(_conda_lib_bin "${_conda_prefix}/Library/bin")
116+
if(EXISTS "${_conda_lib_bin}")
117+
file(GLOB _conda_dlls "${_conda_lib_bin}/*.dll")
118+
list(LENGTH _conda_dlls _num_dlls)
119+
message(STATUS "Copying ${_num_dlls} DLLs from conda Library/bin to build output")
120+
file(COPY ${_conda_dlls} DESTINATION "${CMAKE_BINARY_DIR}/bin/Release")
121+
# Also copy to bin/ for NMake builds
122+
file(COPY ${_conda_dlls} DESTINATION "${CMAKE_BINARY_DIR}/bin")
123+
endif()
109124
else()
110125
get_filename_component(_conda_prefix "${_conda_prefix}" DIRECTORY) # .../
111126
set(_conda_lib_dynload "${_conda_prefix}/lib/python3.12/lib-dynload")

0 commit comments

Comments
 (0)