aboutsummaryrefslogtreecommitdiffstats
path: root/examples/scriptableapplication
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2018-02-01 11:33:52 +0100
committerAlexandru Croitor <alexandru.croitor@qt.io>2018-02-22 10:37:19 +0000
commit2004278e04081b445686712a42698480bfce33e1 (patch)
treefe987654a6649a90a993308bf9aaf6a604a5c766 /examples/scriptableapplication
parent3db0d9e38b8c498927d76f541a3680ae58c4f9b9 (diff)
Add CMake-based build rules for scriptableapplication example
Mostly a clean-ish port of the qmake project file, which also uses the pyside_config.py for getting proper include and link paths. Task-number: PYSIDE-597 Change-Id: I87f71b93ecdcc27d49250ccc0710e64300532dab Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'examples/scriptableapplication')
-rw-r--r--examples/scriptableapplication/CMakeLists.txt187
-rw-r--r--examples/scriptableapplication/README.CMake.txt28
-rw-r--r--examples/scriptableapplication/pyside2_config.py100
3 files changed, 294 insertions, 21 deletions
diff --git a/examples/scriptableapplication/CMakeLists.txt b/examples/scriptableapplication/CMakeLists.txt
new file mode 100644
index 000000000..40e29346a
--- /dev/null
+++ b/examples/scriptableapplication/CMakeLists.txt
@@ -0,0 +1,187 @@
+cmake_minimum_required(VERSION 3.1)
+cmake_policy(VERSION 3.1)
+
+# 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.9 REQUIRED COMPONENTS Core Gui Widgets)
+
+# 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 "${CMAKE_SOURCE_DIR}/pyside2_config.py" ${option}
+ OUTPUT_VARIABLE ${output_var}
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ if ("${${output_var}}" STREQUAL "")
+ message(FATAL_ERROR "Got empty string when running: pyside2_config.py ${option}")
+ endif()
+ if(is_list)
+ string (REPLACE " " ";" ${output_var} "${${output_var}}")
+ endif()
+endmacro()
+
+# Get relevant general paths, include paths and linker flags.
+pyside2_config(--pyside2 PYSIDE2_PATH)
+set(SHIBOKEN_PATH "${PYSIDE2_PATH}/shiboken2")
+
+if(NOT EXISTS ${SHIBOKEN_PATH})
+ message(FATAL_ERROR "Shiboken executable not found at path: ${SHIBOKEN_PATH}")
+endif()
+
+pyside2_config(--python-include PYTHON_INCLUDE_DIR)
+pyside2_config(--pyside2-include PYSIDE2_INCLUDE_DIR 1)
+pyside2_config(--python-link-cmake PYTHON_LINKING_DATA 1)
+pyside2_config(--pyside2-shared-libraries-cmake PYSIDE2_SHARED_LIBRARIES 1)
+
+# 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()
+
+# 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 FALSE)
+SET(CMAKE_INSTALL_RPATH "")
+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 ${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 ${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} 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.
+ foreach(LIBRARY_PATH ${PYSIDE2_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()
diff --git a/examples/scriptableapplication/README.CMake.txt b/examples/scriptableapplication/README.CMake.txt
new file mode 100644
index 000000000..ea658efd5
--- /dev/null
+++ b/examples/scriptableapplication/README.CMake.txt
@@ -0,0 +1,28 @@
+For general information read README.txt instead.
+
+To build this example you will need:
+* A recent version of CMake (3.1+)
+* Make sure that a --standalone PySide2 package (bundled with Qt libraries) is installed into the
+ current active Python environment (system or virtualenv)
+* qmake to be in your PATH (so that CMake find_package(Qt5) works; used for include headers)
+* use the same Qt version for building the example application, as was used for building
+* PySide2, this is to ensure binary compatibility between the newly generated bindings libraries,
+ the PySide2 libraries and the Qt libraries.
+
+For Windows you will also need:
+* Visual studio environment to be active in your terminal
+* Correct visual studio architecture chosen (32 vs 64 bit)
+* Make sure that your Qt + Python + PySide + CMake app build configuration is the same (either or
+ all Release (which is more likely) or all Debug).
+
+You can build this example by executing the following commands (slightly adapted to your file
+system) in a terminal:
+
+cd ~/pyside-setup/examples/scriptableapplication
+(or cd C:\pyside-setup\examples\scriptableapplication)
+mkdir build
+cd build
+cmake -H.. -B. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release
+(or cmake -H.. -B. -G "NMake Makefiles JOM" -DCMAKE_BUILD_TYPE=Release)
+make (or nmake / jom)
+./scriptableapplication (or scriptableapplication.exe)
diff --git a/examples/scriptableapplication/pyside2_config.py b/examples/scriptableapplication/pyside2_config.py
index c81d81827..361043aef 100644
--- a/examples/scriptableapplication/pyside2_config.py
+++ b/examples/scriptableapplication/pyside2_config.py
@@ -73,6 +73,15 @@ def sharedLibraryGlobPattern():
glob = '*.' + sharedLibrarySuffix()
return glob if sys.platform == 'win32' else 'lib' + glob
+def filterPySide2SharedLibraries(list):
+ def predicate(item):
+ basename = os.path.basename(item)
+ if 'shiboken' in basename or 'pyside2' in basename:
+ return True
+ return False
+ result = [item for item in list if predicate(item)]
+ return result
+
# Return qmake link option for a library file name
def linkOption(lib):
baseName = os.path.splitext(os.path.basename(lib))[0]
@@ -99,54 +108,86 @@ def pythonVersion():
def pythonInclude():
return sysconfig.get_python_inc()
-def pythonLink():
+def pythonLinkQmake():
+ flags = pythonLinkData()
+ if sys.platform == 'win32' or sys.platform == 'darwin':
+ return '-L{} -l{}'.format(flags['libdir'], flags['lib'])
+
+ # Linux and anything else
+ return '-l{}'.format(flags['lib'])
+
+def pythonLinkCmake():
+ flags = pythonLinkData()
+ libdir = flags['libdir']
+ lib = re.sub(r'.dll$', '.lib', flags['lib'])
+ return '{} {}'.format(libdir, lib)
+
+def pythonLinkData():
# @TODO Fix to work with static builds of Python
libdir = sysconfig.get_config_var('LIBDIR')
version = pythonVersion()
version_no_dots = version.replace('.', '')
+ flags = {}
+ flags['libdir'] = libdir
if sys.platform == 'win32':
suffix = '_d' if any([tup[0].endswith('_d.pyd') for tup in imp.get_suffixes()]) else ''
- return "-L%s -lpython%s%s" % (libdir, version_no_dots, suffix)
+ flags['lib'] = 'python{}{}'.format(version_no_dots, suffix)
- if sys.platform == 'darwin':
- return '-L%s -lpython%s' % (libdir, version)
+ elif sys.platform == 'darwin':
+ flags['lib'] = 'python{}'.format(version)
# Linux and anything else
- if sys.version_info[0] < 3:
- suffix = '_d' if any([tup[0].endswith('_d.so') for tup in imp.get_suffixes()]) else ''
- return "-lpython%s%s" % (version, suffix)
else:
- return "-lpython%s%s" % (version, sys.abiflags)
+ if sys.version_info[0] < 3:
+ suffix = '_d' if any([tup[0].endswith('_d.so') for tup in imp.get_suffixes()]) else ''
+ flags['lib'] = 'python{}{}'.format(version, suffix)
+ else:
+ flags['lib'] = 'python{}{}'.format(version, sys.abiflags)
+
+ return flags
def pyside2Include():
pySide2 = findPySide2()
if pySide2 is None:
return None
- return "%s/include/PySide2 %s/include/shiboken2" % (pySide2, pySide2)
+ return "{0}/include/PySide2 {0}/include/shiboken2".format(pySide2)
def pyside2Link():
pySide2 = findPySide2()
if pySide2 is None:
return None
- link = "-L%s" % pySide2
- for lib in glob.glob(os.path.join(pySide2, sharedLibraryGlobPattern())):
+ link = "-L{}".format(pySide2)
+ glob_result = glob.glob(os.path.join(pySide2, sharedLibraryGlobPattern()))
+ for lib in filterPySide2SharedLibraries(glob_result):
link += ' '
link += linkOption(lib)
return link
-def pyside2SharedLibraries():
+def pyside2SharedLibrariesData():
pySide2 = findPySide2()
if pySide2 is None:
return None
+ glob_result = glob.glob(os.path.join(pySide2, sharedLibraryGlobPattern()))
+ filtered_libs = filterPySide2SharedLibraries(glob_result)
+ libs = []
if sys.platform == 'win32':
- libs = []
- for lib in glob.glob(os.path.join(pySide2, sharedLibraryGlobPattern())):
+ for lib in filtered_libs:
libs.append(os.path.realpath(lib))
+ else:
+ for lib in filtered_libs:
+ libs.append(lib)
+ return libs
+
+def pyside2SharedLibraries():
+ libs = pyside2SharedLibrariesData()
+ if libs is None:
+ return None
+
+ if sys.platform == 'win32':
if not libs:
return ''
-
dlls = ''
for lib in libs:
dll = os.path.splitext(lib)[0] + '.dll'
@@ -154,10 +195,15 @@ def pyside2SharedLibraries():
return dlls
else:
- libs = ''
- for lib in glob.glob(os.path.join(pySide2, sharedLibraryGlobPattern())):
- libs += ' ' + lib
- return libs
+ libs_string = ''
+ for lib in libs:
+ libs_string += ' ' + lib
+ return libs_string
+
+def pyside2SharedLibrariesCmake():
+ libs = pyside2SharedLibrariesData()
+ result = ' '.join(libs)
+ return result
def clangBinPath():
source = 'LLVM_INSTALL_DIR'
@@ -207,7 +253,13 @@ if option == '--python-include' or option == '-a':
print(i)
if option == '--python-link' or option == '-a':
- l = pythonLink()
+ l = pythonLinkQmake()
+ if l is None:
+ sys.exit('Unable to locate Python')
+ print(l)
+
+if option == '--python-link-cmake' or option == '-a':
+ l = pythonLinkCmake()
if l is None:
sys.exit('Unable to locate Python')
print(l)
@@ -215,7 +267,13 @@ if option == '--python-link' or option == '-a':
if option == '--pyside2-shared-libraries' or option == '-a':
l = pyside2SharedLibraries()
if l is None:
- sys.exit('Unable to locate the PySide sahred libraries')
+ sys.exit('Unable to locate the PySide2 shared libraries')
+ print(l)
+
+if option == '--pyside2-shared-libraries-cmake' or option == '-a':
+ l = pyside2SharedLibrariesCmake()
+ if l is None:
+ sys.exit('Unable to locate the PySide2 shared libraries')
print(l)
if option == '--clang-bin-dir' or option == '-a':