|
| 1 | + |
| 2 | +cmake_minimum_required(VERSION 3.20.6) |
| 3 | + |
| 4 | +project(PythonQt) |
| 5 | + |
| 6 | +#---------------------------------------------------------------------------- |
| 7 | +# Qt version |
| 8 | + |
| 9 | +set(_default_qt_major_version 5) |
| 10 | +if(DEFINED Qt6_DIR) |
| 11 | + set(_default_qt_major_version 6) |
| 12 | +endif() |
| 13 | + |
| 14 | +# Set PythonQt_QT_VERSION |
| 15 | +set(PythonQt_QT_VERSION ${_default_qt_major_version} CACHE STRING "Qt major version (5 or 6)") |
| 16 | +set_property(CACHE PythonQt_QT_VERSION PROPERTY STRINGS "5" "6") |
| 17 | +if(NOT "${PythonQt_QT_VERSION}" MATCHES "^(5|6)$") |
| 18 | + message(FATAL_ERROR "error: PythonQt_QT_VERSION must be 5 or 6.") |
| 19 | +endif() |
| 20 | + |
| 21 | +# Requirements |
| 22 | +set(minimum_required_qt5_version "5.15.0") |
| 23 | +set(minimum_required_qt6_version "6.4.0") |
| 24 | +set(minimum_required_qt_version ${minimum_required_qt${PythonQt_QT_VERSION}_version}) |
| 25 | + |
| 26 | +find_package(Qt${PythonQt_QT_VERSION} ${minimum_required_qt_version} QUIET) |
| 27 | + |
| 28 | +#---------------------------------------------------------------------------- |
| 29 | +# Qt components |
| 30 | +set(qtlibs |
| 31 | + Core |
| 32 | + Widgets |
| 33 | + Network |
| 34 | + OpenGL |
| 35 | + Qml |
| 36 | + Quick |
| 37 | + Sql |
| 38 | + Svg |
| 39 | + Multimedia |
| 40 | + WebEngineWidgets |
| 41 | + UiTools |
| 42 | + Xml |
| 43 | + ) |
| 44 | +# XmlPatterns and WebKitWidgets are removed from Qt 6 |
| 45 | +if(PythonQt_QT_VERSION VERSION_EQUAL "5") |
| 46 | + list(APPEND qtlibs |
| 47 | + XmlPatterns |
| 48 | + WebKitWidgets |
| 49 | + ) |
| 50 | +endif() |
| 51 | + |
| 52 | +#----------------------------------------------------------------------------- |
| 53 | +# Python libraries |
| 54 | + |
| 55 | +find_package(Python3 COMPONENTS Development REQUIRED) |
| 56 | + |
| 57 | +#----------------------------------------------------------------------------- |
| 58 | +# Build options |
| 59 | + |
| 60 | +option(PythonQt_SUPPORT_NAME_PROPERTY "Enable PythonQt name-property support" ON) |
| 61 | +option(PythonQt_USE_RELEASE_PYTHON_FALLBACK "Fallback to Release python when Debug missing" ON) |
| 62 | +option(PythonQt_DEBUG "Enable PythonQt debug output" OFF) |
| 63 | +option(PythonQt_BUILD_QTALL "Build PythonQt_QtAll bindings as a separate library" ON) |
| 64 | + |
| 65 | +if(NOT DEFINED PythonQt_INSTALL_RUNTIME_DIR) |
| 66 | + set(PythonQt_INSTALL_RUNTIME_DIR bin) |
| 67 | +endif() |
| 68 | + |
| 69 | +if(NOT DEFINED PythonQt_INSTALL_LIBRARY_DIR) |
| 70 | + set(PythonQt_INSTALL_LIBRARY_DIR lib${LIB_SUFFIX}) |
| 71 | +endif() |
| 72 | + |
| 73 | +if(NOT DEFINED PythonQt_INSTALL_ARCHIVE_DIR) |
| 74 | + set(PythonQt_INSTALL_ARCHIVE_DIR lib${LIB_SUFFIX}) |
| 75 | +endif() |
| 76 | + |
| 77 | +if(NOT DEFINED PythonQt_INSTALL_INCLUDE_DIR) |
| 78 | + set(PythonQt_INSTALL_INCLUDE_DIR include/PythonQt) |
| 79 | +endif() |
| 80 | + |
| 81 | +#----------------------------------------------------------------------------- |
| 82 | +# Set qtlib_to_wraplib_* variables |
| 83 | + |
| 84 | +set(qtlib_to_wraplib_Widgets gui) |
| 85 | +set(qtlib_to_wraplib_WebKitWidgets webkit) |
| 86 | + |
| 87 | +set(qt_wrapped_lib_depends_gui Multimedia PrintSupport) |
| 88 | +if(PythonQt_QT_VERSION VERSION_EQUAL "6") |
| 89 | + list(APPEND qt_wrapped_lib_depends_gui Widgets OpenGL) |
| 90 | +endif() |
| 91 | +set(qt_wrapped_lib_depends_multimedia MultimediaWidgets) |
| 92 | +set(qt_wrapped_lib_depends_quick QuickWidgets) |
| 93 | +set(qt_wrapped_lib_depends_svg ) |
| 94 | +if(PythonQt_QT_VERSION VERSION_EQUAL "6") |
| 95 | + list(APPEND qt_wrapped_lib_depends_svg SvgWidgets) |
| 96 | +endif() |
| 97 | +set(qt_wrapped_lib_depends_webenginewidgets ) |
| 98 | +set(qt_wrapped_lib_depends_webkit ) |
| 99 | + |
| 100 | +foreach(qtlib ${qtlibs}) |
| 101 | + string(TOLOWER ${qtlib} qtlib_lowercase) |
| 102 | + if(DEFINED qtlib_to_wraplib_${qtlib}) |
| 103 | + set(qtlib_lowercase ${qtlib_to_wraplib_${qtlib}}) |
| 104 | + endif() |
| 105 | + set(qtlib_to_wraplib_${qtlib} ${qtlib_lowercase}) |
| 106 | +endforeach() |
| 107 | + |
| 108 | +#----------------------------------------------------------------------------- |
| 109 | +# Define PythonQt_Wrap_Qt* options |
| 110 | +option(PythonQt_Wrap_QtAll "Make all Qt components available in python" OFF) |
| 111 | +foreach(qtlib ${qtlibs}) |
| 112 | + OPTION(PythonQt_Wrap_Qt${qtlib_to_wraplib_${qtlib}} "Make all of Qt${qtlib} available in python" OFF) |
| 113 | +endforeach() |
| 114 | + |
| 115 | +#----------------------------------------------------------------------------- |
| 116 | +# Force option if it applies |
| 117 | +if(PythonQt_Wrap_QtAll) |
| 118 | + foreach(qtlib ${qtlibs}) |
| 119 | + # XXX xmlpatterns wrapper does *NOT* build at all :( |
| 120 | + if(${qtlib} STREQUAL "XmlPatterns") |
| 121 | + continue() |
| 122 | + endif() |
| 123 | + set(qt_wrapped_lib ${qtlib_to_wraplib_${qtlib}}) |
| 124 | + if(NOT ${PythonQt_Wrap_Qt${qt_wrapped_lib}}) |
| 125 | + set(PythonQt_Wrap_Qt${qt_wrapped_lib} ON CACHE BOOL "Make all of Qt${qt_wrapped_lib} available in python" FORCE) |
| 126 | + message(STATUS "Enabling [PythonQt_Wrap_Qt${qt_wrapped_lib}] because of [PythonQt_Wrap_QtAll] evaluates to True") |
| 127 | + endif() |
| 128 | + endforeach() |
| 129 | +endif() |
| 130 | + |
| 131 | +#----------------------------------------------------------------------------- |
| 132 | +# Setup Qt |
| 133 | + |
| 134 | +# Required components |
| 135 | +set(qt_required_components Core Widgets) |
| 136 | +foreach(qtlib ${qtlibs}) |
| 137 | + set(qt_wrapped_lib ${qtlib_to_wraplib_${qtlib}}) |
| 138 | + if(${PythonQt_Wrap_Qt${qt_wrapped_lib}}) |
| 139 | + list(APPEND qt_required_components ${qtlib} ${qt_wrapped_lib_depends_${qt_wrapped_lib}}) |
| 140 | + endif() |
| 141 | +endforeach() |
| 142 | +if(BUILD_TESTING) |
| 143 | + list(APPEND qt_required_components Test) |
| 144 | +endif() |
| 145 | +if("${Qt${PythonQt_QT_VERSION}_VERSION}" VERSION_GREATER_EQUAL "6.10.0") |
| 146 | + list(APPEND qt_required_components CorePrivate) |
| 147 | +endif() |
| 148 | +list(REMOVE_DUPLICATES qt_required_components) |
| 149 | + |
| 150 | +message(STATUS "${PROJECT_NAME}: Required Qt components [${qt_required_components}]") |
| 151 | +find_package(Qt${PythonQt_QT_VERSION} ${minimum_required_qt_version} COMPONENTS ${qt_required_components} REQUIRED) |
| 152 | + |
| 153 | +set(QT_VERSION_MAJOR ${Qt${PythonQt_QT_VERSION}_VERSION_MAJOR}) |
| 154 | +set(QT_VERSION_MINOR ${Qt${PythonQt_QT_VERSION}_VERSION_MINOR}) |
| 155 | + |
| 156 | +set(QT_LIBRARIES ) |
| 157 | +foreach(qt_component ${qt_required_components}) |
| 158 | + list(APPEND QT_LIBRARIES Qt${PythonQt_QT_VERSION}::${qt_component}) |
| 159 | +endforeach() |
| 160 | + |
| 161 | +if(UNIX) |
| 162 | + find_package(OpenGL) |
| 163 | + if(OPENGL_FOUND) |
| 164 | + list(APPEND QT_LIBRARIES ${OPENGL_LIBRARIES}) |
| 165 | + endif() |
| 166 | +endif() |
| 167 | + |
| 168 | +#----------------------------------------------------------------------------- |
| 169 | +# Pre-generated wrappers default to Qt 5.15 set (generated_cpp_515). Users may override. |
| 170 | +if(PythonQt_QT_VERSION VERSION_EQUAL "5") |
| 171 | + set(generated_cpp_suffix "_515") |
| 172 | + set(_default_generated_path "${CMAKE_CURRENT_SOURCE_DIR}/generated_cpp${generated_cpp_suffix}") |
| 173 | +else() |
| 174 | + set(_default_generated_path "PythonQt_GENERATED_PATH-NOTFOUND") |
| 175 | +endif() |
| 176 | + |
| 177 | +set(PythonQt_GENERATED_PATH "${_default_generated_path}" |
| 178 | + CACHE PATH "Directory containing pre-generated PythonQt Qt wrappers for Qt ${QT_VERSION_MAJOR}.${QT_VERSION_MINOR} (e.g., generated_cpp_515)") |
| 179 | + |
| 180 | +if(NOT IS_DIRECTORY "${PythonQt_GENERATED_PATH}") |
| 181 | + message(FATAL_ERROR |
| 182 | + "PythonQt: missing generated wrapper sources for Qt ${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.\n" |
| 183 | + "Expected: ${PythonQt_GENERATED_PATH}\n" |
| 184 | + "Hint: pass -DPythonQt_GENERATED_PATH:PATH=/path/to/generated_cpp") |
| 185 | +endif() |
| 186 | + |
| 187 | +#----------------------------------------------------------------------------- |
| 188 | +# Build targets |
| 189 | +add_subdirectory(src) |
| 190 | +if(PythonQt_BUILD_QTALL) |
| 191 | + add_subdirectory(extensions/PythonQt_QtAll) |
| 192 | +endif() |
| 193 | + |
| 194 | +#----------------------------------------------------------------------------- |
| 195 | +# Testing |
| 196 | + |
| 197 | +option(BUILD_TESTING "Build the testing tree." OFF) |
| 198 | +include(CTest) |
| 199 | + |
| 200 | +if(BUILD_TESTING) |
| 201 | + create_test_sourcelist(test_sources PythonQtCppTests.cpp |
| 202 | + tests/PythonQtTestMain.cpp |
| 203 | + ) |
| 204 | + |
| 205 | + set_property(SOURCE tests/PythonQtTestMain.cpp PROPERTY COMPILE_DEFINITIONS "main=tests_PythonQtTestMain") |
| 206 | + |
| 207 | + list(APPEND test_sources |
| 208 | + tests/PythonQtTests.cpp |
| 209 | + tests/PythonQtTests.h |
| 210 | + ) |
| 211 | + |
| 212 | + if(PythonQt_Wrap_QtCore AND PythonQt_BUILD_QTALL) |
| 213 | + list(APPEND test_sources |
| 214 | + tests/PythonQtTestCleanup.cpp |
| 215 | + tests/PythonQtTestCleanup.h |
| 216 | + ) |
| 217 | + |
| 218 | + set_property(SOURCE tests/PythonQtTestMain.cpp APPEND PROPERTY COMPILE_DEFINITIONS "PythonQt_Wrap_QtCore") |
| 219 | + endif() |
| 220 | + |
| 221 | + add_executable(PythonQtCppTests ${test_sources}) |
| 222 | + |
| 223 | + target_include_directories(PythonQtCppTests |
| 224 | + PRIVATE |
| 225 | + $<$<BOOL:${PythonQt_Wrap_QtCore}>:${PythonQt_GENERATED_PATH}> |
| 226 | + $<$<AND:$<BOOL:${PythonQt_Wrap_QtCore}>,$<BOOL:${PythonQt_BUILD_QTALL}>>:${CMAKE_CURRENT_SOURCE_DIR}/extensions/PythonQt_QtAll> |
| 227 | + ) |
| 228 | + |
| 229 | + target_link_libraries(PythonQtCppTests PythonQt) |
| 230 | + if(PythonQt_Wrap_QtCore AND PythonQt_BUILD_QTALL) |
| 231 | + target_link_libraries(PythonQtCppTests PythonQt_QtAll) |
| 232 | + endif() |
| 233 | + |
| 234 | + set_target_properties(PythonQtCppTests |
| 235 | + PROPERTIES |
| 236 | + AUTOMOC TRUE |
| 237 | + ) |
| 238 | + |
| 239 | + target_compile_definitions(PythonQtCppTests |
| 240 | + PRIVATE |
| 241 | + $<$<BOOL:${PythonQt_SUPPORT_NAME_PROPERTY}>:PYTHONQT_SUPPORT_NAME_PROPERTY> |
| 242 | + ) |
| 243 | + |
| 244 | + if(WIN32) |
| 245 | + # Python3::Python may resolve to the import library directory (e.g. .../libs), |
| 246 | + # while pythonXY.dll typically lives one level above. Add both. |
| 247 | + get_filename_component(_python3_lib_dir "${Python3_LIBRARY}" DIRECTORY) |
| 248 | + get_filename_component(_python3_root_dir "${_python3_lib_dir}" DIRECTORY) |
| 249 | + |
| 250 | + set(_pythonqt_test_path_entries |
| 251 | + "$<TARGET_FILE_DIR:PythonQtCppTests>" |
| 252 | + "$<TARGET_FILE_DIR:PythonQt>" |
| 253 | + "$<TARGET_FILE_DIR:Qt${PythonQt_QT_VERSION}::Core>" |
| 254 | + "$<TARGET_FILE_DIR:Python3::Python>" |
| 255 | + "${_python3_lib_dir}" |
| 256 | + "${_python3_root_dir}" |
| 257 | + ) |
| 258 | + if(PythonQt_BUILD_QTALL) |
| 259 | + list(APPEND _pythonqt_test_path_entries "$<TARGET_FILE_DIR:PythonQt_QtAll>") |
| 260 | + endif() |
| 261 | + list(JOIN _pythonqt_test_path_entries ";" _pythonqt_test_path_prefix) |
| 262 | + set(_pythonqt_test_env "PATH=${_pythonqt_test_path_prefix};$ENV{PATH}") |
| 263 | + |
| 264 | + add_test( |
| 265 | + NAME tests_PythonQtTestMain |
| 266 | + COMMAND ${CMAKE_COMMAND} -E env "${_pythonqt_test_env}" $<TARGET_FILE:PythonQtCppTests> tests/PythonQtTestMain |
| 267 | + ) |
| 268 | + else() |
| 269 | + add_test( |
| 270 | + NAME tests_PythonQtTestMain |
| 271 | + COMMAND $<TARGET_FILE:PythonQtCppTests> tests/PythonQtTestMain |
| 272 | + ) |
| 273 | + endif() |
| 274 | +endif() |
| 275 | + |
0 commit comments