From 5294b5544cca254a2d6dc1d54730d2d89b3e28de Mon Sep 17 00:00:00 2001 From: Jochen Sprickerhof Date: Mon, 28 Sep 2020 18:26:33 +0200 Subject: [PATCH 1/8] workaround for new path sip dir in pyqt5 >= 5.15.0+dfsg-1+exp1 --- cmake/sip_configure.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/sip_configure.py b/cmake/sip_configure.py index f0a60a8..710d824 100644 --- a/cmake/sip_configure.py +++ b/cmake/sip_configure.py @@ -72,6 +72,11 @@ def get_sip_dir_flags(config): if os.path.exists(default_sip_dir): return default_sip_dir, sip_flags + # workaround for new path sip dir in pyqt5 >= 5.15.0+dfsg-1+exp1 + default_sip_dir = '/usr/lib/python3/dist-packages/PyQt5/bindings' + if os.path.exists(default_sip_dir): + return default_sip_dir, sip_flags + # Homebrew installs sip files here by default default_sip_dir = os.path.join(sipconfig._pkg_config['default_sip_dir'], 'Qt5') if os.path.exists(default_sip_dir): From 023c70b67c1400c9336143ba356fe59c8533145f Mon Sep 17 00:00:00 2001 From: seanyen Date: Fri, 7 Aug 2020 14:30:18 -0700 Subject: [PATCH 2/8] Adding SIP 5 integration. --- CMakeLists.txt | 1 + cmake/pyproject.toml.in | 26 ++++++++++ cmake/sip_helper.cmake | 109 ++++++++++++++++++++++++++++++++-------- 3 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 cmake/pyproject.toml.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 8db3c7b..892d81c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,4 +10,5 @@ install(FILES cmake/shiboken_helper.cmake cmake/sip_configure.py cmake/sip_helper.cmake + cmake/pyproject.toml.in DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/cmake) diff --git a/cmake/pyproject.toml.in b/cmake/pyproject.toml.in new file mode 100644 index 0000000..9353bab --- /dev/null +++ b/cmake/pyproject.toml.in @@ -0,0 +1,26 @@ +# Specify sip v5 as the build system for the package. +[build-system] +requires = ["PyQt-builder >=1, <2"] +build-backend = "sipbuild.api" + +[tool.sip] +project-factory = "pyqtbuild:PyQtProject" + +[tool.sip.builder] +qmake = "@QMAKE_EXECUTABLE@" + +[tool.sip.project] +sip-files-dir = "@SIP_FILES_DIR@" +build-dir = "@SIP_BUILD_DIR@" + +# Specify the PEP 566 metadata for the project. +[tool.sip.metadata] +name = "lib@PROJECT_NAME@" + +[tool.sip.bindings.libqt_gui_cpp_sip] +sip-file = "@SIP_FILE@" +include-dirs = [@SIP_INCLUDE_DIRS@] +libraries = [@SIP_LIBARIES@] +library-dirs = [@SIP_LIBRARY_DIRS@] +qmake-QT = ["widgets"] +define-macros = [@SIP_EXTRA_DEFINES@] diff --git a/cmake/sip_helper.cmake b/cmake/sip_helper.cmake index 253c94f..4030b94 100644 --- a/cmake/sip_helper.cmake +++ b/cmake/sip_helper.cmake @@ -18,7 +18,7 @@ execute_process( if(PYTHON_SIP_EXECUTABLE) string(STRIP ${PYTHON_SIP_EXECUTABLE} SIP_EXECUTABLE) else() - find_program(SIP_EXECUTABLE sip) + find_program(SIP_EXECUTABLE NAMES sip sip-build) endif() if(SIP_EXECUTABLE) @@ -29,6 +29,15 @@ else() set(sip_helper_NOTFOUND TRUE) endif() +if(sip_helper_FOUND) + execute_process( + COMMAND ${SIP_EXECUTABLE} -V + OUTPUT_VARIABLE SIP_VERSION + ERROR_QUIET) + string(STRIP ${SIP_VERSION} SIP_VERSION) + message(STATUS "SIP binding generator version: ${SIP_VERSION}") +endif() + # # Run the SIP generator and compile the generated code into a library. # @@ -96,33 +105,89 @@ function(build_sip_binding PROJECT_NAME SIP_FILE) # SIP configure doesn't handle CMake targets catkin_replace_imported_library_targets(LIBRARIES ${LIBRARIES}) - add_custom_command( - OUTPUT ${SIP_BUILD_DIR}/Makefile - COMMAND ${PYTHON_EXECUTABLE} ${sip_SIP_CONFIGURE} ${SIP_BUILD_DIR} ${SIP_FILE} ${sip_LIBRARY_DIR} \"${INCLUDE_DIRS}\" \"${LIBRARIES}\" \"${LIBRARY_DIRS}\" \"${LDFLAGS_OTHER}\" \"${EXTRA_DEFINES}\" - COMMAND sed -i 's/ -I/ -isystem/g' ${SIP_BUILD_DIR}/Makefile - DEPENDS ${sip_SIP_CONFIGURE} ${SIP_FILE} ${sip_DEPENDS} - WORKING_DIRECTORY ${sip_SOURCE_DIR} - COMMENT "Running SIP generator for ${PROJECT_NAME} Python bindings..." - ) + if(${SIP_VERSION} VERSION_GREATER_EQUAL "5.0.0") + # Since v5, SIP implements the backend per PEP 517, PEP 518 + # Here we synthesize `pyproject.toml` and run `pip install` + + find_program(QMAKE_EXECUTABLE NAMES qmake REQUIRED) - if(NOT EXISTS "${sip_LIBRARY_DIR}") + file(REMOVE_RECURSE ${SIP_BUILD_DIR}) file(MAKE_DIRECTORY ${sip_LIBRARY_DIR}) - endif() - if(WIN32) - set(MAKE_EXECUTABLE NMake.exe) + set(SIP_FILES_DIR ${sip_SOURCE_DIR}) + + set(SIP_INCLUDE_DIRS "") + foreach(_x ${INCLUDE_DIRS}) + set(SIP_INCLUDE_DIRS "${SIP_INCLUDE_DIRS},\"${_x}\"") + endforeach() + string(REGEX REPLACE "^," "" SIP_INCLUDE_DIRS ${SIP_INCLUDE_DIRS}) + + # SIP expects the libraries WITHOUT the file extension. + set(SIP_LIBARIES "") + foreach(_x ${LIBRARIES} ${PYTHON_LIBRARIES}) + get_filename_component(_x_NAME "${_x}" NAME_WLE) + get_filename_component(_x_DIR "${_x}" DIRECTORY) + get_filename_component(_x "${_x_DIR}/${_x_NAME}" ABSOLUTE) + set(SIP_LIBARIES "${SIP_LIBARIES},\"${_x}\"") + endforeach() + string(REGEX REPLACE "^," "" SIP_LIBARIES ${SIP_LIBARIES}) + + set(SIP_LIBRARY_DIRS "") + foreach(_x ${LIBRARY_DIRS}) + set(SIP_LIBRARY_DIRS "${SIP_LIBRARY_DIRS},\"${_x}\"") + endforeach() + string(REGEX REPLACE "^," "" SIP_LIBRARY_DIRS ${SIP_LIBRARY_DIRS}) + + set(SIP_EXTRA_DEFINES "") + foreach(_x ${EXTRA_DEFINES}) + set(SIP_EXTRA_DEFINES "${SIP_EXTRA_DEFINES},\"${_x}\"") + endforeach() + string(REGEX REPLACE "^," "" SIP_EXTRA_DEFINES ${SIP_EXTRA_DEFINES}) + + # TODO: + # I don't know what to do about LDFLAGS_OTHER + # what's the equivalent construct in sip5? + + configure_file( + ${__PYTHON_QT_BINDING_SIP_HELPER_DIR}/pyproject.toml.in + ${sip_BINARY_DIR}/sip/pyproject.toml + ) + add_custom_command( + OUTPUT ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} + COMMAND ${PYTHON_EXECUTABLE} -m pip install . --target ${sip_LIBRARY_DIR} --no-deps + DEPENDS ${sip_SIP_CONFIGURE} ${SIP_FILE} ${sip_DEPENDS} + WORKING_DIRECTORY ${sip_BINARY_DIR}/sip + COMMENT "Running SIP-build generator for ${PROJECT_NAME} Python bindings..." + ) else() - set(MAKE_EXECUTABLE "\$(MAKE)") + add_custom_command( + OUTPUT ${SIP_BUILD_DIR}/Makefile + COMMAND ${PYTHON_EXECUTABLE} ${sip_SIP_CONFIGURE} ${SIP_BUILD_DIR} ${SIP_FILE} ${sip_LIBRARY_DIR} \"${INCLUDE_DIRS}\" \"${LIBRARIES}\" \"${LIBRARY_DIRS}\" \"${LDFLAGS_OTHER}\" \"${EXTRA_DEFINES}\" + COMMAND sed -i 's/ -I/ -isystem/g' ${SIP_BUILD_DIR}/Makefile + DEPENDS ${sip_SIP_CONFIGURE} ${SIP_FILE} ${sip_DEPENDS} + WORKING_DIRECTORY ${sip_SOURCE_DIR} + COMMENT "Running SIP generator for ${PROJECT_NAME} Python bindings..." + ) + + if(NOT EXISTS "${sip_LIBRARY_DIR}") + file(MAKE_DIRECTORY ${sip_LIBRARY_DIR}) + endif() + + if(WIN32) + set(MAKE_EXECUTABLE NMake.exe) + else() + set(MAKE_EXECUTABLE "\$(MAKE)") + endif() + + add_custom_command( + OUTPUT ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} + COMMAND ${MAKE_EXECUTABLE} + DEPENDS ${SIP_BUILD_DIR}/Makefile + WORKING_DIRECTORY ${SIP_BUILD_DIR} + COMMENT "Compiling generated code for ${PROJECT_NAME} Python bindings..." + ) endif() - add_custom_command( - OUTPUT ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} - COMMAND ${MAKE_EXECUTABLE} - DEPENDS ${SIP_BUILD_DIR}/Makefile - WORKING_DIRECTORY ${SIP_BUILD_DIR} - COMMENT "Compiling generated code for ${PROJECT_NAME} Python bindings..." - ) - add_custom_target(lib${PROJECT_NAME} ALL DEPENDS ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} COMMENT "Meta target for ${PROJECT_NAME} Python bindings..." From b8bb032d2cfce2310edf906452a30ac3858edb3a Mon Sep 17 00:00:00 2001 From: v4hn Date: Mon, 14 Feb 2022 15:02:51 +0100 Subject: [PATCH 3/8] allow exceptions this is not the default, but some ROS-specific bindings break without it in header-inline throw statements (e.g. in Poco/Mutex_POSIX.h) --- cmake/pyproject.toml.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/pyproject.toml.in b/cmake/pyproject.toml.in index 9353bab..185c635 100644 --- a/cmake/pyproject.toml.in +++ b/cmake/pyproject.toml.in @@ -24,3 +24,4 @@ libraries = [@SIP_LIBARIES@] library-dirs = [@SIP_LIBRARY_DIRS@] qmake-QT = ["widgets"] define-macros = [@SIP_EXTRA_DEFINES@] +exceptions = true From 10dfac173c1852c75ae0cc1c9c1d185d0cbfeba0 Mon Sep 17 00:00:00 2001 From: v4hn Date: Mon, 14 Feb 2022 16:43:14 +0100 Subject: [PATCH 4/8] use correct EXT_SUFFIX e.g. `.cpython-310-x86_64-linux-gnu.so` instead of just '.so' --- cmake/sip_helper.cmake | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cmake/sip_helper.cmake b/cmake/sip_helper.cmake index 4030b94..056f02c 100644 --- a/cmake/sip_helper.cmake +++ b/cmake/sip_helper.cmake @@ -38,6 +38,11 @@ if(sip_helper_FOUND) message(STATUS "SIP binding generator version: ${SIP_VERSION}") endif() +execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "import distutils.sysconfig as c; print(c.get_config_var('EXT_SUFFIX'), end='')" + OUTPUT_VARIABLE PYTHON_EXTENSION_MODULE_SUFFIX + ERROR_QUIET) + # # Run the SIP generator and compile the generated code into a library. # @@ -153,7 +158,7 @@ function(build_sip_binding PROJECT_NAME SIP_FILE) ${sip_BINARY_DIR}/sip/pyproject.toml ) add_custom_command( - OUTPUT ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} + OUTPUT ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${PYTHON_EXTENSION_MODULE_SUFFIX} COMMAND ${PYTHON_EXECUTABLE} -m pip install . --target ${sip_LIBRARY_DIR} --no-deps DEPENDS ${sip_SIP_CONFIGURE} ${SIP_FILE} ${sip_DEPENDS} WORKING_DIRECTORY ${sip_BINARY_DIR}/sip @@ -180,7 +185,7 @@ function(build_sip_binding PROJECT_NAME SIP_FILE) endif() add_custom_command( - OUTPUT ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} + OUTPUT ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${PYTHON_EXTENSION_MODULE_SUFFIX} COMMAND ${MAKE_EXECUTABLE} DEPENDS ${SIP_BUILD_DIR}/Makefile WORKING_DIRECTORY ${SIP_BUILD_DIR} @@ -189,7 +194,7 @@ function(build_sip_binding PROJECT_NAME SIP_FILE) endif() add_custom_target(lib${PROJECT_NAME} ALL - DEPENDS ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} + DEPENDS ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${PYTHON_EXTENSION_MODULE_SUFFIX} COMMENT "Meta target for ${PROJECT_NAME} Python bindings..." ) add_dependencies(lib${PROJECT_NAME} ${sip_DEPENDENCIES}) From f0855907a53aba5c8903e9b02c7b4c60231962f4 Mon Sep 17 00:00:00 2001 From: v4hn Date: Mon, 14 Feb 2022 16:57:03 +0100 Subject: [PATCH 5/8] split relative and absolute libs for pyproject.toml This is somewhat hacky, but at least passes all libraries to the linker. The previous version of this block was plain broken in the sip5 transition. --- cmake/pyproject.toml.in | 4 +++- cmake/sip_helper.cmake | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cmake/pyproject.toml.in b/cmake/pyproject.toml.in index 185c635..6482671 100644 --- a/cmake/pyproject.toml.in +++ b/cmake/pyproject.toml.in @@ -20,8 +20,10 @@ name = "lib@PROJECT_NAME@" [tool.sip.bindings.libqt_gui_cpp_sip] sip-file = "@SIP_FILE@" include-dirs = [@SIP_INCLUDE_DIRS@] -libraries = [@SIP_LIBARIES@] +libraries = [@SIP_LIBRARIES@] library-dirs = [@SIP_LIBRARY_DIRS@] +# this should be extra-objects, but these break inside pybuild with sip modules +extra-link-args = [@SIP_ABS_LIBRARIES@] qmake-QT = ["widgets"] define-macros = [@SIP_EXTRA_DEFINES@] exceptions = true diff --git a/cmake/sip_helper.cmake b/cmake/sip_helper.cmake index 056f02c..f18faae 100644 --- a/cmake/sip_helper.cmake +++ b/cmake/sip_helper.cmake @@ -127,15 +127,21 @@ function(build_sip_binding PROJECT_NAME SIP_FILE) endforeach() string(REGEX REPLACE "^," "" SIP_INCLUDE_DIRS ${SIP_INCLUDE_DIRS}) - # SIP expects the libraries WITHOUT the file extension. - set(SIP_LIBARIES "") + # pyproject.toml expects libraries listed as such to be added to the linker command + # via `-l`, but this does not work for libraries with absolute paths + # instead we have to pass them to the linker via a different parameter + set(_SIP_REL_LIBRARIES "") + set(_SIP_ABS_LIBRARIES "") foreach(_x ${LIBRARIES} ${PYTHON_LIBRARIES}) - get_filename_component(_x_NAME "${_x}" NAME_WLE) - get_filename_component(_x_DIR "${_x}" DIRECTORY) - get_filename_component(_x "${_x_DIR}/${_x_NAME}" ABSOLUTE) - set(SIP_LIBARIES "${SIP_LIBARIES},\"${_x}\"") + cmake_path(IS_ABSOLUTE _x is_abs) + if(is_abs) + list(APPEND _SIP_ABS_LIBRARIES "\"${_x}\"") + else() + list(APPEND _SIP_REL_LIBRARIES "\"${_x}\"") + endif() endforeach() - string(REGEX REPLACE "^," "" SIP_LIBARIES ${SIP_LIBARIES}) + list(JOIN _SIP_REL_LIBRARIES "," SIP_LIBRARIES) + list(JOIN _SIP_ABS_LIBRARIES "," SIP_ABS_LIBRARIES) set(SIP_LIBRARY_DIRS "") foreach(_x ${LIBRARY_DIRS}) From f4623e2966971e20b8cbe803d876e8da425945fd Mon Sep 17 00:00:00 2001 From: v4hn Date: Wed, 3 Aug 2022 11:52:05 +0200 Subject: [PATCH 6/8] disable build isolation Isolation is not needed as we expect to build in the system environment. Additionally the isolation wrappers can break sip-specific build commands by hiding the path of `sip-distinfo`. --- cmake/sip_helper.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/sip_helper.cmake b/cmake/sip_helper.cmake index f18faae..47058dd 100644 --- a/cmake/sip_helper.cmake +++ b/cmake/sip_helper.cmake @@ -165,7 +165,7 @@ function(build_sip_binding PROJECT_NAME SIP_FILE) ) add_custom_command( OUTPUT ${sip_LIBRARY_DIR}/lib${PROJECT_NAME}${PYTHON_EXTENSION_MODULE_SUFFIX} - COMMAND ${PYTHON_EXECUTABLE} -m pip install . --target ${sip_LIBRARY_DIR} --no-deps + COMMAND ${PYTHON_EXECUTABLE} -m pip install . --target ${sip_LIBRARY_DIR} --no-deps --no-build-isolation DEPENDS ${sip_SIP_CONFIGURE} ${SIP_FILE} ${sip_DEPENDS} WORKING_DIRECTORY ${sip_BINARY_DIR}/sip COMMENT "Running SIP-build generator for ${PROJECT_NAME} Python bindings..." From e1a94496a29f377e94102b84316b7e2e09145078 Mon Sep 17 00:00:00 2001 From: v4hn Date: Wed, 3 Aug 2022 12:22:16 +0200 Subject: [PATCH 7/8] avoid distutils --- cmake/sip_helper.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/sip_helper.cmake b/cmake/sip_helper.cmake index 47058dd..68f79fc 100644 --- a/cmake/sip_helper.cmake +++ b/cmake/sip_helper.cmake @@ -39,7 +39,7 @@ if(sip_helper_FOUND) endif() execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "import distutils.sysconfig as c; print(c.get_config_var('EXT_SUFFIX'), end='')" + COMMAND ${PYTHON_EXECUTABLE} -c "import sysconfig as c; print(c.get_config_var('EXT_SUFFIX'), end='')" OUTPUT_VARIABLE PYTHON_EXTENSION_MODULE_SUFFIX ERROR_QUIET) From ae75b3e539fb1b780e27760437db57b3a18a9e4e Mon Sep 17 00:00:00 2001 From: v4hn Date: Mon, 3 Oct 2022 21:16:06 +0200 Subject: [PATCH 8/8] fix sip4 generator sip4 does not adhere to PEP-3149 https://peps.python.org/pep-3149/ by default, so we force it to do so in the name of consistency with the sip5 generator. --- cmake/sip_configure.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/sip_configure.py b/cmake/sip_configure.py index 710d824..fbc5408 100644 --- a/cmake/sip_configure.py +++ b/cmake/sip_configure.py @@ -4,6 +4,7 @@ import re import subprocess import sys +import sysconfig import sipconfig import PyQt5 @@ -44,6 +45,7 @@ def __init__(self): macros['INCDIR_QT'] = qtconfig['QT_INSTALL_HEADERS'] macros['LIBDIR_QT'] = qtconfig['QT_INSTALL_LIBS'] macros['MOC'] = 'moc-qt5' if which('moc-qt5') else 'moc' + macros['EXTENSION_PLUGIN'] = sysconfig.get_config_var('EXT_SUFFIX')[1:] # skip the initial '.' here self.set_build_macros(macros)