From c5bb41dc012bd1516d00205769b216c83d101514 Mon Sep 17 00:00:00 2001 From: Sandro Mani Date: Tue, 28 Sep 2021 21:01:47 +0200 Subject: [PATCH] PyQt5 cmake improvements --- CMakeLists.txt | 2 +- cmake/FindPyQt5.cmake | 68 ++++++++++++++++++++++++++---------------- cmake/FindPyQt5.py | 61 +++++++++---------------------------- cmake/FindQsci.cmake | 44 +++++++++++++++++++-------- cmake/PyQtMacros.cmake | 12 ++++++-- python/CMakeLists.txt | 4 ++- 6 files changed, 102 insertions(+), 89 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 897b0fcfa9c..e6fe9376d6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -929,12 +929,12 @@ if (WITH_CORE AND WITH_BINDINGS AND NOT WITH_QT6) set (QGIS_PYTHON_OUTPUT_DIRECTORY ${PYTHON_OUTPUT_DIRECTORY}/qgis) # python support: check for interpreter, sip, pyqt5 + find_package(SIP REQUIRED) find_package(PyQt5 REQUIRED) set(PYQT_SIP_FLAGS ${PYQT5_SIP_FLAGS}) set(PYQT_SIP_DIR ${PYQT5_SIP_DIR}) separate_arguments(PYQT_SIP_FLAGS) # convert space separated values to a list - find_package(SIP REQUIRED) find_package(Qsci REQUIRED) include(PythonMacros) include(PyQtMacros) diff --git a/cmake/FindPyQt5.cmake b/cmake/FindPyQt5.cmake index 88990331d13..33733925116 100644 --- a/cmake/FindPyQt5.cmake +++ b/cmake/FindPyQt5.cmake @@ -11,40 +11,58 @@ # # This file defines the following variables: # -# PYQT5_VERSION - The version of PyQt5 found expressed as a 6 digit hex number -# suitable for comparison as a string -# # PYQT5_VERSION_STR - The version of PyQt5 as a human readable string. # -# PYQT5_VERSION_TAG - The PyQt version tag using by PyQt's sip files. -# # PYQT5_SIP_DIR - The directory holding the PyQt5 .sip files. # # PYQT5_SIP_FLAGS - The SIP flags used to build PyQt. -IF(EXISTS PYQT5_VERSION) +IF(EXISTS PYQT5_VERSION_STR) # Already in cache, be silent SET(PYQT5_FOUND TRUE) -ELSE(EXISTS PYQT5_VERSION) +ELSE(EXISTS PYQT5_VERSION_STR) - FIND_FILE(_find_pyqt5_py FindPyQt5.py PATHS ${CMAKE_MODULE_PATH} NO_CMAKE_FIND_ROOT_PATH) + IF(SIP_BUILD_EXECUTABLE) + # SIP >= 5.0 path - EXECUTE_PROCESS(COMMAND ${Python_EXECUTABLE} ${_find_pyqt5_py} OUTPUT_VARIABLE pyqt_config) - IF(pyqt_config) - STRING(REGEX REPLACE "^pyqt_version:([^\n]+).*$" "\\1" PYQT5_VERSION ${pyqt_config}) - STRING(REGEX REPLACE ".*\npyqt_version_str:([^\n]+).*$" "\\1" PYQT5_VERSION_STR ${pyqt_config}) - STRING(REGEX REPLACE ".*\npyqt_version_tag:([^\n]+).*$" "\\1" PYQT5_VERSION_TAG ${pyqt_config}) - STRING(REGEX REPLACE ".*\npyqt_version_num:([^\n]+).*$" "\\1" PYQT5_VERSION_NUM ${pyqt_config}) - STRING(REGEX REPLACE ".*\npyqt_mod_dir:([^\n]+).*$" "\\1" PYQT5_MOD_DIR ${pyqt_config}) - STRING(REGEX REPLACE ".*\npyqt_sip_dir:([^\n]+).*$" "\\1" PYQT5_SIP_DIR ${pyqt_config}) - IF(EXISTS ${PYQT5_SIP_DIR}/Qt5) - SET(PYQT5_SIP_DIR ${PYQT5_SIP_DIR}/Qt5) - ENDIF(EXISTS ${PYQT5_SIP_DIR}/Qt5) - STRING(REGEX REPLACE ".*\npyqt_sip_flags:([^\n]+).*$" "\\1" PYQT5_SIP_FLAGS ${pyqt_config}) - STRING(REGEX REPLACE ".*\npyqt_bin_dir:([^\n]+).*$" "\\1" PYQT5_BIN_DIR ${pyqt_config}) - STRING(REGEX REPLACE ".*\npyqt_sip_module:([^\n]+).*$" "\\1" PYQT5_SIP_IMPORT ${pyqt_config}) - SET(PYQT5_FOUND TRUE) - ENDIF(pyqt_config) + + FILE(GLOB _pyqt5_metadata "${Python_SITEARCH}/PyQt5*.dist-info/METADATA") + IF(_pyqt5_metadata) + FILE(READ ${_pyqt5_metadata} _pyqt5_metadata_contents) + STRING(REGEX REPLACE ".*\nVersion: ([^\n]+).*$" "\\1" PYQT5_VERSION_STR ${_pyqt5_metadata_contents}) + ELSE(_pyqt5_metadata) + EXECUTE_PROCESS(COMMAND ${Python_EXECUTABLE} -c "from PyQt5.QtCore import PYQT_VERSION_STR; print(PYQT_VERSION_STR)" OUTPUT_VARIABLE PYQT5_VERSION_STR) + ENDIF(_pyqt5_metadata) + + IF(PYQT5_VERSION_STR) + SET(PYQT5_MOD_DIR "${Python_SITEARCH}/PyQt5") + SET(PYQT5_SIP_DIR "${Python_SITEARCH}/PyQt5/bindings") + FIND_PROGRAM(__pyuic5 "pyuic5") + GET_FILENAME_COMPONENT(PYQT5_BIN_DIR ${__pyuic5} DIRECTORY) + + SET(PYQT5_FOUND TRUE) + ENDIF(PYQT5_VERSION_STR) + + ELSE(SIP_BUILD_EXECUTABLE) + # SIP 4.x path + + FIND_FILE(_find_pyqt5_py FindPyQt5.py PATHS ${CMAKE_MODULE_PATH} NO_CMAKE_FIND_ROOT_PATH) + + EXECUTE_PROCESS(COMMAND ${Python_EXECUTABLE} ${_find_pyqt5_py} OUTPUT_VARIABLE pyqt_config) + IF(pyqt_config) + STRING(REGEX REPLACE "^pyqt_version_str:([^\n]+).*$" "\\1" PYQT5_VERSION_STR ${pyqt_config}) + STRING(REGEX REPLACE ".*\npyqt_mod_dir:([^\n]+).*$" "\\1" PYQT5_MOD_DIR ${pyqt_config}) + STRING(REGEX REPLACE ".*\npyqt_sip_dir:([^\n]+).*$" "\\1" PYQT5_SIP_DIR ${pyqt_config}) + IF(EXISTS ${PYQT5_SIP_DIR}/Qt5) + SET(PYQT5_SIP_DIR ${PYQT5_SIP_DIR}/Qt5) + ENDIF(EXISTS ${PYQT5_SIP_DIR}/Qt5) + STRING(REGEX REPLACE ".*\npyqt_sip_flags:([^\n]+).*$" "\\1" PYQT5_SIP_FLAGS ${pyqt_config}) + STRING(REGEX REPLACE ".*\npyqt_bin_dir:([^\n]+).*$" "\\1" PYQT5_BIN_DIR ${pyqt_config}) + STRING(REGEX REPLACE ".*\npyqt_sip_module:([^\n]+).*$" "\\1" PYQT5_SIP_IMPORT ${pyqt_config}) + SET(PYQT5_FOUND TRUE) + ENDIF(pyqt_config) + + ENDIF(SIP_BUILD_EXECUTABLE) IF(PYQT5_FOUND) IF(NOT PyQt5_FIND_QUIETLY) @@ -56,4 +74,4 @@ ELSE(EXISTS PYQT5_VERSION) ENDIF(PyQt5_FIND_REQUIRED) ENDIF(PYQT5_FOUND) -ENDIF(EXISTS PYQT5_VERSION) +ENDIF(EXISTS PYQT5_VERSION_STR) diff --git a/cmake/FindPyQt5.py b/cmake/FindPyQt5.py index cc34682b80f..fe029af9fac 100644 --- a/cmake/FindPyQt5.py +++ b/cmake/FindPyQt5.py @@ -32,57 +32,24 @@ import os.path import PyQt5.QtCore +import sipconfig +import sys -try: - __import__('sipbuild') -except ImportError: - import sipconfig # won't work for SIP v5 - import sys +cfg = sipconfig.Configuration() +sip_dir = cfg.default_sip_dir +for p in (os.path.join(sip_dir, "PyQt5"), + os.path.join(sip_dir, "PyQt5-3"), + sip_dir, + os.path.join(cfg.default_mod_dir, "PyQt5", "bindings")): + if os.path.exists(os.path.join(p, "QtCore", "QtCoremod.sip")): + sip_dir = p + break - cfg = sipconfig.Configuration() - sip_dir = cfg.default_sip_dir - for p in (os.path.join(sip_dir, "PyQt5"), - os.path.join(sip_dir, "PyQt5-3"), - sip_dir, - os.path.join(cfg.default_mod_dir, "PyQt5", "bindings")): - if os.path.exists(os.path.join(p, "QtCore", "QtCoremod.sip")): - sip_dir = p - break - cfg = { - 'pyqt_mod_dir': os.path.join(cfg.default_mod_dir, "PyQt5"), - 'pyqt_sip_dir': sip_dir, - 'pyqt_bin_dir': cfg.default_bin_dir, - } -else: # Code for SIP v5 - from distutils.sysconfig import get_python_lib - import shutil - cfg = { - 'pyqt_mod_dir': os.path.dirname(PyQt5.__file__), - 'pyqt_sip_dir': os.path.join(get_python_lib(plat_specific=1), "PyQt5", "bindings"), - 'pyqt_bin_dir': os.path.dirname(shutil.which("pyuic5")), - } - -print("pyqt_version:%06.0x" % PyQt5.QtCore.PYQT_VERSION) -print("pyqt_version_num:%d" % PyQt5.QtCore.PYQT_VERSION) print("pyqt_version_str:%s" % PyQt5.QtCore.PYQT_VERSION_STR) - -pyqt_version_tag = "" -in_t = False -pyqt_config_list = PyQt5.QtCore.PYQT_CONFIGURATION["sip_flags"].split(' ') -for item in pyqt_config_list: - if item == "-t": - in_t = True - elif in_t: - if item.startswith("Qt_5"): - pyqt_version_tag = item - else: - in_t = False -print("pyqt_version_tag:%s" % pyqt_version_tag) - -print("pyqt_mod_dir:%s" % cfg['pyqt_mod_dir']) -print("pyqt_sip_dir:%s" % cfg['pyqt_sip_dir']) +print("pyqt_mod_dir:%s" % os.path.join(cfg.default_mod_dir, "PyQt5")) +print("pyqt_sip_dir:%s" % sip_dir) print("pyqt_sip_flags:%s" % PyQt5.QtCore.PYQT_CONFIGURATION['sip_flags']) -print("pyqt_bin_dir:%s" % cfg['pyqt_bin_dir']) +print("pyqt_bin_dir:%s" % cfg.default_bin_dir) try: import PyQt5.sip diff --git a/cmake/FindQsci.cmake b/cmake/FindQsci.cmake index ecde37f62f0..69e41c1fe98 100644 --- a/cmake/FindQsci.cmake +++ b/cmake/FindQsci.cmake @@ -21,23 +21,43 @@ IF(QSCI_MOD_VERSION_STR) SET(QSCI_FOUND TRUE) ELSE(QSCI_MOD_VERSION_STR) - FIND_FILE(_find_qsci_py FindQsci.py PATHS ${CMAKE_MODULE_PATH} NO_CMAKE_FIND_ROOT_PATH) + IF(SIP_BUILD_EXECUTABLE) + # SIP >= 5.0 path - SET(QSCI_VER 5) + FILE(GLOB _qsci_metadata "${Python_SITEARCH}/QScintilla*.dist-info/METADATA") + IF(_qsci_metadata) + FILE(READ ${_qsci_metadata} _qsci_metadata_contents) + STRING(REGEX REPLACE ".*\nVersion: ([^\n]+).*$" "\\1" QSCI_MOD_VERSION_STR ${_qsci_metadata_contents}) + ELSE(_qsci_metadata) + EXECUTE_PROCESS(COMMAND ${Python_EXECUTABLE} -c "from PyQt5.Qsci import QSCINTILLA_VERSION_STR; print(QSCINTILLA_VERSION_STR)" OUTPUT_VARIABLE QSCI_MOD_VERSION_STR) + ENDIF(_qsci_metadata) - EXECUTE_PROCESS(COMMAND ${Python_EXECUTABLE} ${_find_qsci_py} ${QSCI_VER} OUTPUT_VARIABLE qsci_ver) + IF(QSCI_MOD_VERSION_STR) + SET(QSCI_SIP_DIR "${PYQT5_SIP_DIR}") + SET(QSCI_FOUND TRUE) + ENDIF(QSCI_MOD_VERSION_STR) - IF(qsci_ver) - STRING(REGEX REPLACE "^qsci_version_str:([^\n]+).*$" "\\1" QSCI_MOD_VERSION_STR ${qsci_ver}) - SET(QSCI_FOUND TRUE) - ENDIF(qsci_ver) + ELSE(SIP_BUILD_EXECUTABLE) + # SIP 4.x path + + FIND_FILE(_find_qsci_py FindQsci.py PATHS ${CMAKE_MODULE_PATH} NO_CMAKE_FIND_ROOT_PATH) + + SET(QSCI_VER 5) + + EXECUTE_PROCESS(COMMAND ${Python_EXECUTABLE} ${_find_qsci_py} ${QSCI_VER} OUTPUT_VARIABLE qsci_ver) + + IF(qsci_ver) + STRING(REGEX REPLACE "^qsci_version_str:([^\n]+).*$" "\\1" QSCI_MOD_VERSION_STR ${qsci_ver}) + FIND_PATH(QSCI_SIP_DIR + NAMES Qsci/qscimod5.sip + PATHS ${PYQT5_SIP_DIR} ${SIP_DEFAULT_SIP_DIR} + ) + SET(QSCI_FOUND TRUE) + ENDIF(qsci_ver) + + ENDIF(SIP_BUILD_EXECUTABLE) IF(QSCI_FOUND) - FIND_PATH(QSCI_SIP_DIR - NAMES Qsci/qscimod5.sip - PATHS ${PYQT5_SIP_DIR} ${SIP_DEFAULT_SIP_DIR} - ) - IF(NOT QSCI_FIND_QUIETLY) MESSAGE(STATUS "Found QScintilla2 PyQt module: ${QSCI_MOD_VERSION_STR}") ENDIF(NOT QSCI_FIND_QUIETLY) diff --git a/cmake/PyQtMacros.cmake b/cmake/PyQtMacros.cmake index b6c4e4194e9..e441906ffff 100644 --- a/cmake/PyQtMacros.cmake +++ b/cmake/PyQtMacros.cmake @@ -17,6 +17,8 @@ ENDIF(NOT PYUIC_PROGRAM) # Adapted from QT4_WRAP_UI MACRO(PYQT_WRAP_UI outfiles ) + SET(PYUIC_WRAPPER_OUTPUT_DIRECTORY "${PYTHON_OUTPUT_DIRECTORY}") + SET(PYUIC_WRAPPER_PYTHON_EXECUTABLE "${Python_EXECUTABLE}") IF(CMAKE_HOST_WIN32) IF(USING_NINJA OR USING_NMAKE) SET(PYUIC_WRAPPER "${CMAKE_SOURCE_DIR}/scripts/pyuic_wrapper.bat") @@ -25,18 +27,22 @@ MACRO(PYQT_WRAP_UI outfiles ) SET(PYUIC_WRAPPER "${CMAKE_SOURCE_DIR}/scripts/pyuic_wrapper.bat") SET(PYUIC_WRAPPER_PATH "${QGIS_OUTPUT_DIRECTORY}/bin/${CMAKE_BUILD_TYPE}") ENDIF(USING_NINJA OR USING_NMAKE) - ELSE(CMAKE_HOST_WIN32) + ELSEIF(MINGW) + # Clear all variables to invoke PYUIC_PROGRAM directly + SET(PYUIC_WRAPPER_OUTPUT_DIRECTORY "") + SET(PYUIC_WRAPPER_PYTHON_EXECUTABLE "") + ELSE() # TODO osx SET(PYUIC_WRAPPER "${CMAKE_SOURCE_DIR}/scripts/pyuic_wrapper.sh") SET(PYUIC_WRAPPER_PATH "${QGIS_OUTPUT_DIRECTORY}/lib") - ENDIF(CMAKE_HOST_WIN32) + ENDIF() FOREACH(it ${ARGN}) GET_FILENAME_COMPONENT(outfile ${it} NAME_WE) GET_FILENAME_COMPONENT(infile ${it} ABSOLUTE) SET(outfile ${CMAKE_CURRENT_BINARY_DIR}/ui_${outfile}.py) ADD_CUSTOM_COMMAND(OUTPUT ${outfile} - COMMAND ${PYUIC_WRAPPER} "${PYUIC_PROGRAM}" "${PYUIC_WRAPPER_PATH}" "${PYTHON_OUTPUT_DIRECTORY}" "${Python_EXECUTABLE}" ${infile} -o ${outfile} + COMMAND ${PYUIC_WRAPPER} "${PYUIC_PROGRAM}" "${PYUIC_WRAPPER_PATH}" "${PYUIC_WRAPPER_OUTPUT_DIRECTORY}" "${PYUIC_WRAPPER_PYTHON_EXECUTABLE}" ${infile} -o ${outfile} MAIN_DEPENDENCY ${infile} DEPENDS pygui pycore pyqtcompat ) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 73e066cf8ee..ec0693addcb 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -167,9 +167,11 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_definitions(-std=c++14) endif() -if(UNIX AND NOT SIP_VERSION_NUM LESS 265984) +if((UNIX OR MINGW) AND SIP_VERSION_STR VERSION_GREATER_EQUAL 4.10 AND SIP_VERSION_STR VERSION_LESS 5.0) set(SIP_EXTRA_OPTIONS -P ${SIP_EXTRA_OPTIONS}) add_definitions(-Dprotected=public) +elseif((UNIX OR MINGW) AND SIP_VERSION_STR VERSION_GREATER_EQUAL 5.0) + add_definitions(-Dprotected=public) endif() set (PY_MODULES core)