From 218e72763b04cc8976b1e2669e86e2200bc0d74a Mon Sep 17 00:00:00 2001 From: DLuminary <86183013+DLuminary@users.noreply.github.com> Date: Mon, 24 Aug 2026 13:34:42 +0300 Subject: [PATCH] Escape the path separator when prepending to PYTHONPATH PybindWrap.cmake and MatlabWrap.cmake build the environment for the wrapper generator as "PYTHONPATH=${GTWRAP_PACKAGE_DIR}${GTWRAP_PATH_SEPARATOR}$ENV{PYTHONPATH}" with GTWRAP_PATH_SEPARATOR set to ":" on UNIX and ";" elsewhere. The UNIX branch is fine. On the other branch an unescaped ";" is CMake's list separator, so the quoted argument is split rather than being passed through as a single PYTHONPATH assignment. When PYTHONPATH is unset the string ends in a trailing ";", the empty second element is dropped, and the command still receives one valid assignment -- which is why this has not been noticed. When PYTHONPATH is set, the split lands in the middle of the command line and the build fails outright. Reproduced with a minimal project using add_custom_command, forcing the non-UNIX branch: unescaped ";", PYTHONPATH unset -> ran ok unescaped ";", PYTHONPATH set -> Error 127, build fails escaped "\;", PYTHONPATH set -> ran ok, PYTHONPATH=/pkg/dir:/existing escaped "\;", PYTHONPATH unset -> ran ok escaped "\;", UNIX branch -> ran ok (unchanged) So on Windows the wrapper currently builds only because PYTHONPATH is typically unset; a conda environment or any other setup that defines it would fail before the generator runs. Note this is not tested on a real Windows host -- the non-UNIX branch was forced on Linux to exercise the same CMake parsing. The UNIX path is unaffected either way. The same class of bug exists in gtsam's python/CMakeLists.txt, where the four PYTHONPATH-setting targets join with "/" instead of a path-list separator at all; that is fixed separately in borglab/gtsam. --- cmake/MatlabWrap.cmake | 4 +++- cmake/PybindWrap.cmake | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmake/MatlabWrap.cmake b/cmake/MatlabWrap.cmake index 55b7cdb..f565e8e 100644 --- a/cmake/MatlabWrap.cmake +++ b/cmake/MatlabWrap.cmake @@ -229,7 +229,9 @@ function(wrap_library_internal interfaceHeader moduleName linkLibraries extraInc if(UNIX) set(GTWRAP_PATH_SEPARATOR ":") else() - set(GTWRAP_PATH_SEPARATOR ";") + # Escaped: an unescaped ";" is CMake's list separator, so it would split + # the quoted PYTHONPATH argument below instead of joining two path entries. + set(GTWRAP_PATH_SEPARATOR "\\;") endif() # Set boost serialization flag for the python script call below. diff --git a/cmake/PybindWrap.cmake b/cmake/PybindWrap.cmake index 2c98b69..9721c8d 100644 --- a/cmake/PybindWrap.cmake +++ b/cmake/PybindWrap.cmake @@ -60,7 +60,9 @@ function( if(UNIX) set(GTWRAP_PATH_SEPARATOR ":") else() - set(GTWRAP_PATH_SEPARATOR ";") + # Escaped: an unescaped ";" is CMake's list separator, so it would split + # the quoted PYTHONPATH argument below instead of joining two path entries. + set(GTWRAP_PATH_SEPARATOR "\\;") endif() # Create a copy of interface_headers so we can freely manipulate it