1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
cmake_minimum_required(VERSION 3.16)
cmake_policy(VERSION 3.16)
# Enable policy to run automoc on generated files.
if(POLICY CMP0071)
cmake_policy(SET CMP0071 NEW)
endif()
project(scriptableapplication)
# Set CPP standard to C++11 minimum.
set(CMAKE_CXX_STANDARD 11)
# Find required Qt packages.
find_package(Qt5 5.12 REQUIRED COMPONENTS Core Gui Widgets)
# Use provided python interpreter if given.
if(NOT python_interpreter)
find_program(python_interpreter NAMES python3 python)
endif()
message(STATUS "Using python interpreter: ${python_interpreter}")
# Macro to get various pyside / python include / link flags.
macro(pyside2_config option output_var)
if(${ARGC} GREATER 2)
set(is_list ${ARGV2})
else()
set(is_list "")
endif()
execute_process(
COMMAND ${python_interpreter} "${CMAKE_SOURCE_DIR}/../utils/pyside2_config.py"
${option}
OUTPUT_VARIABLE ${output_var}
OUTPUT_STRIP_TRAILING_WHITESPACE)
if ("${${output_var}}" STREQUAL "")
message(FATAL_ERROR "Error: Calling pyside2_config.py ${option} returned no output.")
endif()
if(is_list)
string (REPLACE " " ";" ${output_var} "${${output_var}}")
endif()
endmacro()
# Query for the shiboken2-generator path, PySide2 path, Python path, include paths and linker flags.
pyside2_config(--shiboken2-module-path SHIBOKEN2_MODULE_PATH)
pyside2_config(--shiboken2-generator-path SHIBOKEN2_GENERATOR_PATH)
pyside2_config(--pyside2-path PYSIDE2_PATH)
pyside2_config(--python-include-path PYTHON_INCLUDE_DIR)
pyside2_config(--shiboken2-generator-include-path SHIBOKEN2_GENERATOR_INCLUDE_DIR 1)
pyside2_config(--pyside2-include-path PYSIDE2_INCLUDE_DIR 1)
pyside2_config(--python-link-flags-cmake PYTHON_LINKING_DATA 0)
pyside2_config(--shiboken2-module-shared-libraries-cmake SHIBOKEN2_MODULE_SHARED_LIBRARIES 0)
pyside2_config(--pyside2-shared-libraries-cmake PYSIDE2_SHARED_LIBRARIES 0)
set(SHIBOKEN_PATH "${SHIBOKEN2_GENERATOR_PATH}/shiboken2${CMAKE_EXECUTABLE_SUFFIX}")
if(NOT EXISTS ${SHIBOKEN_PATH})
message(FATAL_ERROR "Shiboken executable not found at path: ${SHIBOKEN_PATH}")
endif()
# Get all relevant Qt include dirs, to pass them on to shiboken.
get_property(QT_CORE_INCLUDE_DIRS TARGET Qt5::Core PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
get_property(QT_GUI_INCLUDE_DIRS TARGET Qt5::Gui PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
get_property(QT_WIDGETS_INCLUDE_DIRS TARGET Qt5::Widgets PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
set(QT_INCLUDE_DIRS ${QT_CORE_INCLUDE_DIRS} ${QT_GUI_INCLUDE_DIRS} ${QT_WIDGETS_INCLUDE_DIRS})
set(INCLUDES "")
foreach(INCLUDE_DIR ${QT_INCLUDE_DIRS})
list(APPEND INCLUDES "-I${INCLUDE_DIR}")
endforeach()
# On macOS, check if Qt is a framework build. This affects how include paths should be handled.
get_target_property(QtCore_is_framework Qt5::Core FRAMEWORK)
if (QtCore_is_framework)
get_target_property(qt_core_library_location Qt5::Core LOCATION)
get_filename_component(qt_core_library_location_dir "${qt_core_library_location}" DIRECTORY)
get_filename_component(lib_dir "${qt_core_library_location_dir}/../" ABSOLUTE)
list(APPEND INCLUDES "--framework-include-paths=${lib_dir}")
endif()
# Set up the options to pass to shiboken.
set(WRAPPED_HEADER ${CMAKE_SOURCE_DIR}/wrappedclasses.h)
set(TYPESYSTEM_FILE ${CMAKE_SOURCE_DIR}/scriptableapplication.xml)
set(SHIBOKEN_OPTIONS --generator-set=shiboken --enable-parent-ctor-heuristic
--enable-pyside-extensions --enable-return-value-heuristic --use-isnull-as-nb_nonzero
--avoid-protected-hack
${INCLUDES}
-I${CMAKE_SOURCE_DIR}
-T${CMAKE_SOURCE_DIR}
-T${PYSIDE2_PATH}/typesystems
--output-directory=${CMAKE_CURRENT_BINARY_DIR}
)
# Specify which sources will be generated by shiboken, and their dependencies.
set(GENERATED_SOURCES
${CMAKE_CURRENT_BINARY_DIR}/AppLib/applib_module_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/AppLib/mainwindow_wrapper.cpp)
set(GENERATED_SOURCES_DEPENDENCIES
${WRAPPED_HEADER}
${TYPESYSTEM_FILE}
)
# Add custom target to run shiboken.
add_custom_command(OUTPUT ${GENERATED_SOURCES}
COMMAND ${SHIBOKEN_PATH}
${SHIBOKEN_OPTIONS} ${WRAPPED_HEADER} ${TYPESYSTEM_FILE}
DEPENDS ${GENERATED_SOURCES_DEPENDENCIES}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Running generator for ${TYPESYSTEM_FILE}.")
# Set the CPP files.
set(SOURCES
mainwindow.cpp
pythonutils.cpp
${GENERATED_SOURCES}
)
# We need to include the headers for the module bindings that we use.
set(PYSIDE2_ADDITIONAL_INCLUDES "")
foreach(INCLUDE_DIR ${PYSIDE2_INCLUDE_DIR})
list(APPEND PYSIDE2_ADDITIONAL_INCLUDES "${INCLUDE_DIR}/QtCore")
list(APPEND PYSIDE2_ADDITIONAL_INCLUDES "${INCLUDE_DIR}/QtGui")
list(APPEND PYSIDE2_ADDITIONAL_INCLUDES "${INCLUDE_DIR}/QtWidgets")
endforeach()
# =============================================================================================
# !!! (The section below is deployment related, so in a real world application you will want to
# take care of this properly with some custom script or tool).
# =============================================================================================
# Enable rpaths so that the example can be executed from the build dir.
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH ${PYSIDE2_PATH} ${SHIBOKEN2_MODULE_PATH})
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# =============================================================================================
# !!! End of dubious section.
# =============================================================================================
# Declare executable so we can enable automoc.
add_executable(${PROJECT_NAME} main.cpp)
# Enable automoc.
set_property(TARGET ${PROJECT_NAME} PROPERTY AUTOMOC 1)
# Add the rest of the sources.
target_sources(${PROJECT_NAME} PUBLIC ${SOURCES})
# Apply relevant include and link flags.
target_include_directories(${PROJECT_NAME} PRIVATE ${PYTHON_INCLUDE_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${SHIBOKEN2_GENERATOR_INCLUDE_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${PYSIDE2_INCLUDE_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${PYSIDE2_ADDITIONAL_INCLUDES})
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Widgets)
target_link_libraries(${PROJECT_NAME} PRIVATE ${SHIBOKEN2_MODULE_SHARED_LIBRARIES})
target_link_libraries(${PROJECT_NAME} PRIVATE ${PYSIDE2_SHARED_LIBRARIES})
# Find and link to the python library.
list(GET PYTHON_LINKING_DATA 0 PYTHON_LIBDIR)
list(GET PYTHON_LINKING_DATA 1 PYTHON_LIB)
find_library(PYTHON_LINK_FLAGS ${PYTHON_LIB} PATHS ${PYTHON_LIBDIR} HINTS ${PYTHON_LIBDIR})
target_link_libraries(${PROJECT_NAME} PRIVATE ${PYTHON_LINK_FLAGS})
# Same as CONFIG += no_keywords to avoid syntax errors in object.h due to the usage of the word Slot
target_compile_definitions(${PROJECT_NAME} PRIVATE QT_NO_KEYWORDS)
if(WIN32)
# =============================================================================================
# !!! (The section below is deployment related, so in a real world application you will want to
# take care of this properly (this is simply to eliminate errors that users usually encounter.
# =============================================================================================
# Circumvent some "#pragma comment(lib)"s in "include/pyconfig.h" which might force to link
# against a wrong python shared library.
set(PYTHON_VERSIONS_LIST 3 32 33 34 35 36 37 38)
set(PYTHON_ADDITIONAL_LINK_FLAGS "")
foreach(VER ${PYTHON_VERSIONS_LIST})
set(PYTHON_ADDITIONAL_LINK_FLAGS
"${PYTHON_ADDITIONAL_LINK_FLAGS} /NODEFAULTLIB:\"python${VER}_d.lib\"")
set(PYTHON_ADDITIONAL_LINK_FLAGS
"${PYTHON_ADDITIONAL_LINK_FLAGS} /NODEFAULTLIB:\"python${VER}.lib\"")
endforeach()
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "${PYTHON_ADDITIONAL_LINK_FLAGS}")
# Add custom target to hard link PySide2 shared libraries (just like in qmake example), so you
# don't have to set PATH manually to point to the PySide2 package.
set(shared_libraries ${SHIBOKEN2_MODULE_SHARED_LIBRARIES} ${PYSIDE2_SHARED_LIBRARIES})
foreach(LIBRARY_PATH ${shared_libraries})
string(REGEX REPLACE ".lib$" ".dll" LIBRARY_PATH ${LIBRARY_PATH})
get_filename_component(BASE_NAME ${LIBRARY_PATH} NAME)
file(TO_NATIVE_PATH ${LIBRARY_PATH} SOURCE_PATH)
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME}" DEST_PATH)
add_custom_command(OUTPUT "${BASE_NAME}"
COMMAND mklink /H "${DEST_PATH}" "${SOURCE_PATH}"
DEPENDS ${LIBRARY_PATH}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Creating hardlink to PySide2 shared library ${BASE_NAME}")
# Fake target that depends on the previous one, but has special ALL keyword, which means
# it will always be executed.
add_custom_target("fake_${BASE_NAME}" ALL DEPENDS ${BASE_NAME})
endforeach()
# =============================================================================================
# !!! End of dubious section.
# =============================================================================================
endif()
|