diff options
Diffstat (limited to 'sources/shiboken6/cmake/ShibokenHelpers.cmake')
-rw-r--r-- | sources/shiboken6/cmake/ShibokenHelpers.cmake | 884 |
1 files changed, 884 insertions, 0 deletions
diff --git a/sources/shiboken6/cmake/ShibokenHelpers.cmake b/sources/shiboken6/cmake/ShibokenHelpers.cmake new file mode 100644 index 000000000..8bc066102 --- /dev/null +++ b/sources/shiboken6/cmake/ShibokenHelpers.cmake @@ -0,0 +1,884 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +include(CMakeParseArguments) + +macro(set_limited_api) + if (WIN32 AND NOT EXISTS "${PYTHON_LIMITED_LIBRARIES}") + message(FATAL_ERROR "The Limited API was enabled, but ${PYTHON_LIMITED_LIBRARIES} was not found!") + endif() + message(STATUS "******************************************************") + message(STATUS "** Limited API enabled ${PYTHON_LIMITED_LIBRARIES}") + message(STATUS "******************************************************") +endmacro() + +macro(set_debug_build) + set(SHIBOKEN_BUILD_TYPE "Debug") + + if(NOT Python_LIBRARIES) + message(WARNING "Python debug shared library not found; \ + assuming python was built with shared library support disabled.") + endif() + + if(NOT PYTHON_WITH_DEBUG) + message(WARNING "Compiling shiboken6 with debug enabled, \ + but the python executable was not compiled with debug support.") + else() + set(SBK_PKG_CONFIG_PY_DEBUG_DEFINITION " -DPy_DEBUG") + endif() + + if (PYTHON_WITH_COUNT_ALLOCS) + set(SBK_PKG_CONFIG_PY_DEBUG_DEFINITION "${SBK_PKG_CONFIG_PY_DEBUG_DEFINITION} -DCOUNT_ALLOCS") + endif() +endmacro() + +macro(setup_sanitize_address) + # Currently this does not check that the clang / gcc version used supports Address sanitizer, + # so once again, use at your own risk. + add_compile_options("-fsanitize=address" "-g" "-fno-omit-frame-pointer") + # We need to add the sanitize address option to all linked executables / shared libraries + # so that proper sanitizer symbols are linked in. + # + # Note that when running tests, you may need to set an additional environment variable + # in set_tests_properties for shiboken6 / pyside tests, or exported in your shell. Address + # sanitizer will tell you what environment variable needs to be exported. For example: + # export DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Toolchains/ + # ./XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib + set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_STANDARD_LIBRARIES} -fsanitize=address") +endmacro() + +macro(set_cmake_cxx_flags) +if(MSVC) + # Qt5: this flag has changed from /Zc:wchar_t- in Qt4.X + set(CMAKE_CXX_FLAGS "/Zc:wchar_t /GR /EHsc /DWIN32 /D_WINDOWS /D_SCL_SECURE_NO_WARNINGS") + #set(CMAKE_CXX_FLAGS "/Zc:wchar_t /GR /EHsc /DNOCOLOR /DWIN32 /D_WINDOWS /D_SCL_SECURE_NO_WARNINGS") # XXX +else() + set (gcc_warnings_options "-Wall -Wextra -Wno-strict-aliasing") + # Clang has -Wno-bad-function-cast, but does not need it. + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set (gcc_warnings_options "${gcc_warnings_options} -Wno-cast-function-type") + endif() + if(CMAKE_HOST_UNIX AND NOT CYGWIN) + add_definitions(-fPIC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${gcc_warnings_options} -fvisibility=hidden") + endif() + set(CMAKE_CXX_FLAGS_DEBUG "-g") + option(ENABLE_GCC_OPTIMIZATION "Enable specific GCC flags to optimization library \ + size and performance. Only available on Release Mode" 0) + if(ENABLE_GCC_OPTIMIZATION) + set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Os -Wl,-O1") + if(NOT CMAKE_HOST_APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--hash-style=gnu") + endif() + endif() + if(CMAKE_HOST_APPLE) + # ALTERNATIVE_QT_INCLUDE_DIR is deprecated, because CMake takes care of finding the proper + # include folders using the qmake found in the environment. Only use it for now in case + # something goes wrong with the cmake process. + if(ALTERNATIVE_QT_INCLUDE_DIR AND NOT QT_INCLUDE_DIR) + set(QT_INCLUDE_DIR ${ALTERNATIVE_QT_INCLUDE_DIR}) + endif() + endif() +endif() + +endmacro() + +function(qfp_strip_library target) + # Strip unless macOS (/strip: error: symbols referenced by indirect symbol + # table entries that can't be stripped). + if (CMAKE_STRIP AND UNIX AND NOT APPLE AND NOT QFP_NO_STRIP + AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + set(post_command COMMAND ${CMAKE_STRIP} $<TARGET_FILE:${target}>) + add_custom_command(TARGET ${target} POST_BUILD ${post_command}) + endif() + +endfunction() + +macro(shiboken_internal_set_python_site_packages) + # When cross-building, we can't run the target python executable to find out the information, + # so we allow an explicit variable assignment or use a default / sensible value. + if(SHIBOKEN_IS_CROSS_BUILD OR PYSIDE_IS_CROSS_BUILD OR QFP_FIND_NEW_PYTHON_PACKAGE) + # Allow manual assignment. + if(QFP_PYTHON_SITE_PACKAGES) + set(PYTHON_SITE_PACKAGES "${QFP_PYTHON_SITE_PACKAGES}") + else() + # Assumes POSIX. + # Convention can be checked in cpython's source code in + # Lib/sysconfig.py's _INSTALL_SCHEMES + set(__version_major_minor + "${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}") + + set(PYTHON_SITE_PACKAGES_WITHOUT_PREFIX + "lib/python${__version_major_minor}/site-packages") + set(PYTHON_SITE_PACKAGES + "${CMAKE_INSTALL_PREFIX}/${PYTHON_SITE_PACKAGES_WITHOUT_PREFIX}") + unset(__version_major_minor) + endif() + else() + execute_process( + COMMAND ${Python_EXECUTABLE} -c "if True: + import sysconfig + from os.path import sep + + # /home/qt/dev/env/lib/python3.9/site-packages + lib_path = sysconfig.get_path('purelib') + + # /home/qt/dev/env + data_path = sysconfig.get_path('data') + + # /lib/python3.9/site-packages + rel_path = lib_path.replace(data_path, '') + + print(f'${CMAKE_INSTALL_PREFIX}{rel_path}'.replace(sep, '/')) + " + OUTPUT_VARIABLE PYTHON_SITE_PACKAGES + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif() + if (NOT PYTHON_SITE_PACKAGES) + message(FATAL_ERROR "Could not detect Python module installation directory.") + elseif (APPLE) + message(STATUS "!!! The generated bindings will be installed on ${PYTHON_SITE_PACKAGES}, \ + is it right!?") + endif() +endmacro() + +macro(set_python_config_suffix) + if (PYTHON_LIMITED_API) + if(WIN32) + set(PYTHON_EXTENSION_SUFFIX "") + else() + set(PYTHON_EXTENSION_SUFFIX ".abi3") + endif() + set(PYTHON_CONFIG_SUFFIX ".abi3") + else() + set(PYTHON_CONFIG_SUFFIX "${PYTHON_EXTENSION_SUFFIX}") + endif() +endmacro() + +macro(setup_clang) + # Find libclang using the environment variables LLVM_INSTALL_DIR, + # CLANG_INSTALL_DIR using standard cmake. + # Use CLANG_INCLUDE_DIRS and link to libclang. + if(DEFINED ENV{LLVM_INSTALL_DIR}) + list(PREPEND CMAKE_PREFIX_PATH "$ENV{LLVM_INSTALL_DIR}") + list(PREPEND CMAKE_FIND_ROOT_PATH "$ENV{LLVM_INSTALL_DIR}") + elseif(DEFINED ENV{CLANG_INSTALL_DIR}) + list(PREPEND CMAKE_PREFIX_PATH "$ENV{CLANG_INSTALL_DIR}") + list(PREPEND CMAKE_FIND_ROOT_PATH "$ENV{CLANG_INSTALL_DIR}") + endif() + + find_package(Clang CONFIG REQUIRED) + # Need to explicitly handle the version check, because the Clang package doesn't. + if (LLVM_PACKAGE_VERSION AND LLVM_PACKAGE_VERSION VERSION_LESS "9.0") + message(FATAL_ERROR "You need LLVM version 9.0 or greater to build.") + endif() + + # CLANG_LIBRARY is read out from the cmake cache to deploy libclang + get_target_property(CLANG_BUILD_TYPE libclang IMPORTED_CONFIGURATIONS) + get_target_property(CLANG_LIBRARY_NAME libclang IMPORTED_LOCATION_${CLANG_BUILD_TYPE}) + set(CLANG_LIBRARY "${CLANG_LIBRARY_NAME}" CACHE FILEPATH "libclang") + message(STATUS "CLANG: ${Clang_DIR}, ${CLANG_LIBRARY} detected") +endmacro() + +macro(set_quiet_build) + # Don't display "up-to-date / install" messages when installing, to reduce visual clutter. + set(CMAKE_INSTALL_MESSAGE NEVER) + # Override message not to display info messages when doing a quiet build. + function(message) + list(GET ARGV 0 MessageType) + if (MessageType STREQUAL FATAL_ERROR OR + MessageType STREQUAL SEND_ERROR OR + MessageType STREQUAL WARNING OR + MessageType STREQUAL AUTHOR_WARNING) + list(REMOVE_AT ARGV 0) + _message(${MessageType} "${ARGV}") + endif() + endfunction() +endmacro() + +macro(get_python_extension_suffix) + # When cross-building, we can't run the target python executable to find out the information, + # so we rely on Python_SOABI being set by find_package(Python). + # Python_SOABI is only set by CMake 3.17+ + # TODO: Lower this to CMake 3.16 if possible. + if(SHIBOKEN_IS_CROSS_BUILD) + # For android platform armv7a FindPython module return Python_SOABI as empty because + # it is unable to set Python_CONFIG i.e. find `python3-config` script + # This workaround sets the Python_SOABI manually for this platform. + if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7-a") + set(Python_SOABI "cpython-311}") + endif() + if(NOT Python_SOABI) + message(FATAL_ERROR "Python_SOABI variable is empty.") + endif() + set(PYTHON_EXTENSION_SUFFIX ".${Python_SOABI}") + else() + # See PYSIDE-1841 / https://bugs.python.org/issue39825 for distutils vs sysconfig + execute_process( + COMMAND ${Python_EXECUTABLE} -c "if True: + import sys + if sys.version_info >= (3, 8, 2): + import sysconfig + suffix = sysconfig.get_config_var('EXT_SUFFIX') + else: + from distutils import sysconfig + suffix = sysconfig.get_config_var('EXT_SUFFIX') + pos = suffix.rfind('.') + if pos > 0: + print(suffix[:pos]) + else: + print(f'Unable to determine PYTHON_EXTENSION_SUFFIX from EXT_SUFFIX: \"{suffix}\"', + file=sys.stderr) + " + OUTPUT_VARIABLE PYTHON_EXTENSION_SUFFIX + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif() + message(STATUS "PYTHON_EXTENSION_SUFFIX: " ${PYTHON_EXTENSION_SUFFIX}) +endmacro() + +macro(shiboken_parse_all_arguments prefix type flags options multiopts) + cmake_parse_arguments(${prefix} "${flags}" "${options}" "${multiopts}" ${ARGN}) + if(DEFINED ${prefix}_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments were passed to ${type} (${${prefix}_UNPARSED_ARGUMENTS}).") + endif() +endmacro() + +macro(shiboken_check_if_limited_api) + # TODO: Figure out how to use limited API libs when cross-building to Windows, if that's ever + # needed. Perhaps use host python to walk the libs of the target python installation. + + if(NOT SHIBOKEN_IS_CROSS_BUILD AND WIN32) + # On Windows, PYTHON_LIBRARIES can be a list. Example: + # optimized;C:/Python36/libs/python36.lib;debug;C:/Python36/libs/python36_d.lib + # On other platforms, this result is not used at all. + execute_process( + COMMAND ${Python_EXECUTABLE} -c "if True: + from pathlib import Path + libs = r'${Python_LIBRARIES}' + libs = libs.split(';') + for lib in libs: + if ('\\\\' in lib or '/' in lib) and Path(lib).is_file(): + lib = Path(lib) + prefix = lib.parent + py = lib.name + if py.startswith('python3'): + print(prefix / 'python3.lib') + break + " + OUTPUT_VARIABLE PYTHON_LIMITED_LIBRARIES + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif() + + message(STATUS "PYTHON_LIMITED_LIBRARIES: " ${PYTHON_LIMITED_LIBRARIES}) + + if(FORCE_LIMITED_API OR SHIBOKEN_PYTHON_LIMITED_API) + set(PYTHON_LIMITED_API 1) + if(WIN32) + set(SHIBOKEN_PYTHON_LIBRARIES ${PYTHON_LIMITED_LIBRARIES}) + endif() + endif() +endmacro() + + +macro(shiboken_find_required_python) + set(_shiboken_find_python_version_args "") + if(${ARGC} GREATER 0) + list(APPEND _shiboken_find_python_version_args "${ARGV0}") + endif() + # This function can also be called by consumers of ShibokenConfig.cmake package like pyside, + # that's why we also check for PYSIDE_IS_CROSS_BUILD (which is set by pyside project) + # and QFP_FIND_NEW_PYTHON_PACKAGE for an explicit opt in. + # + # We have to use FindPython package instead of FindPythonInterp to get required target Python + # information. + if(SHIBOKEN_IS_CROSS_BUILD OR PYSIDE_IS_CROSS_BUILD OR QFP_FIND_NEW_PYTHON_PACKAGE) + # We want FindPython to look in the sysroot for the python-config executable, + # but toolchain files might set CMAKE_FIND_ROOT_PATH_MODE_PROGRAM to NEVER because + # programs are mostly found for running and you usually can't run a target executable on + # a host platform. python-config can likely be ran though, because it's a shell script + # to be run on a host Linux. + set(_shiboken_backup_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM + "${CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}") + set(_shiboken_backup_CMAKE_FIND_ROOT_PATH + "${CMAKE_FIND_ROOT_PATH}") + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) + if(Python_ROOT_DIR) + list(PREPEND CMAKE_FIND_ROOT_PATH "${Python_ROOT_DIR}") + endif() + + # We can't look for the Python interpreter because FindPython tries to execute it, which + # usually won't work on a host platform due to different architectures / platforms. + # Thus we only look for the Python include and lib directories which are part of the + # Development component. + find_package( + Python + ${_shiboken_find_python_version_args} + REQUIRED + COMPONENTS Development + ) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM + "${_shiboken_backup_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}") + set(CMAKE_FIND_ROOT_PATH + "${_shiboken_backup_CMAKE_FIND_ROOT_PATH}") + else() + find_package( + Python + ${_shiboken_find_python_version_args} + REQUIRED + COMPONENTS Interpreter Development + ) + endif() + + shiboken_validate_python_version() + + set(SHIBOKEN_PYTHON_INTERPRETER "${Python_EXECUTABLE}") + set_property(GLOBAL PROPERTY SHIBOKEN_PYTHON_INTERPRETER "${Python_EXECUTABLE}") +endmacro() + +macro(shiboken_validate_python_version) + if(Python_VERSION_MAJOR EQUAL "3" AND Python_VERSION_MINOR LESS "7") + message(FATAL_ERROR + "Shiboken requires Python 3.7+.") + endif() +endmacro() + +macro(shiboken_compute_python_includes) + shiboken_parse_all_arguments( + "SHIBOKEN_COMPUTE_INCLUDES" "shiboken_compute_python_includes" + "IS_CALLED_FROM_EXPORT" "" "" ${ARGN}) + + + # If the installed shiboken config file is used, + # append the found Python include dirs as an interface property on the libshiboken target. + # This needs to be dynamic because the user of the library might have python installed + # in a different path than when shiboken was originally built. + # Otherwise if shiboken is currently being built itself (either as standalone, or super project + # build) append the include dirs as PUBLIC. + if (SHIBOKEN_COMPUTE_INCLUDES_IS_CALLED_FROM_EXPORT) + #TODO target_include_directories works on imported targets only starting with v3.11.0. + set_property(TARGET Shiboken6::libshiboken + APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Python_INCLUDE_DIRS}) + else() + target_include_directories(libshiboken + PUBLIC $<BUILD_INTERFACE:${Python_INCLUDE_DIRS}>) + endif() + + + set(SHIBOKEN_PYTHON_INCLUDE_DIRS "${Python_INCLUDE_DIRS}") + + set_property(GLOBAL PROPERTY shiboken_python_include_dirs "${SHIBOKEN_PYTHON_INCLUDE_DIRS}") + + message(STATUS + "SHIBOKEN_PYTHON_INCLUDE_DIRS computed to value: '${SHIBOKEN_PYTHON_INCLUDE_DIRS}'") +endmacro() + +# Given a list of the following form: +# optimized;C:/Python36/libs/python36.lib;debug;C:/Python36/libs/python36_d.lib +# choose the corresponding library to use, based on the current configuration type. +function(shiboken_get_library_for_current_config library_list current_config out_var) + list(FIND library_list "optimized" optimized_found) + list(FIND library_list "general" general_found) + list(FIND library_list "debug" debug_found) + + if (optimized_found OR general_found OR debug_found) + # Iterate over library list to find the most appropriate library. + foreach(token ${library_list}) + if(token STREQUAL "optimized" OR token STREQUAL "general") + set(is_debug 0) + set(token1 1) + set(lib "") + elseif(token STREQUAL "debug") + set(is_debug 1) + set(token1 1) + set(lib "") + elseif(EXISTS ${token}) + set(lib ${token}) + set(token2 1) + else() + set(token1 0) + set(token2 0) + set(lib "") + endif() + + if(token1 AND token2) + if((is_debug AND lib AND current_config STREQUAL "Debug") + OR (NOT is_debug AND lib AND (NOT current_config STREQUAL "Debug"))) + set(${out_var} ${lib} PARENT_SCOPE) + return() + endif() + endif() + endforeach() + else() + # No configuration specific libraries found, just set the original value. + set(${out_var} "${library_list}" PARENT_SCOPE) + endif() + +endfunction() + +macro(shiboken_compute_python_libraries) + shiboken_parse_all_arguments( + "SHIBOKEN_COMPUTE_LIBS" "shiboken_compute_python_libraries" + "IS_CALLED_FROM_EXPORT" "" "" ${ARGN}) + + if (NOT SHIBOKEN_PYTHON_LIBRARIES) + set(SHIBOKEN_PYTHON_LIBRARIES "") + endif() + + if(WIN32 AND NOT SHIBOKEN_PYTHON_LIBRARIES) + set(SHIBOKEN_PYTHON_LIBRARIES ${Python_LIBRARIES}) + endif() + + # If the resulting variable + # contains a "debug;X;optimized;Y" list like described in shiboken_check_if_limited_api, + # make sure to pick just one, so that the final generator expressions are valid. + shiboken_get_library_for_current_config("${SHIBOKEN_PYTHON_LIBRARIES}" "${CMAKE_BUILD_TYPE}" "SHIBOKEN_PYTHON_LIBRARIES") + + if(APPLE) + set(SHIBOKEN_PYTHON_LIBRARIES "-undefined dynamic_lookup") + endif() + + # If the installed shiboken config file is used, + # append the computed Python libraries as an interface property on the libshiboken target. + # This needs to be dynamic because the user of the library might have python installed + # in a different path than when shiboken was originally built. + # Otherwise if shiboken is currently being built itself (either as standalone, or super project + # build) append the libraries as PUBLIC. + if (SHIBOKEN_COMPUTE_LIBS_IS_CALLED_FROM_EXPORT) + #TODO target_link_libraries works on imported targets only starting with v3.11.0. + set_property(TARGET Shiboken6::libshiboken + APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${SHIBOKEN_PYTHON_LIBRARIES}) + else() + target_link_libraries(libshiboken + PUBLIC $<BUILD_INTERFACE:${SHIBOKEN_PYTHON_LIBRARIES}>) + endif() + + set_property(GLOBAL PROPERTY shiboken_python_libraries "${SHIBOKEN_PYTHON_LIBRARIES}") + + message(STATUS "SHIBOKEN_PYTHON_LIBRARIES computed to value: '${SHIBOKEN_PYTHON_LIBRARIES}'") +endmacro() + +function(shiboken_check_if_built_and_target_python_are_compatible) + if(NOT SHIBOKEN_PYTHON_VERSION_MAJOR STREQUAL Python_VERSION_MAJOR) + message(FATAL_ERROR "The detected Python major version is not \ +compatible with the Python major version which was used when Shiboken was built. +Built with: '${SHIBOKEN_PYTHON_VERSION_MAJOR}.${SHIBOKEN_PYTHON_VERSION_MINOR}' \ +Detected: '${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}'") + else() + if(NOT SHIBOKEN_PYTHON_LIMITED_API + AND NOT SHIBOKEN_PYTHON_VERSION_MINOR STREQUAL Python_VERSION_MINOR) + message(FATAL_ERROR + "The detected Python minor version is not compatible with the Python minor \ +version which was used when Shiboken was built. Consider building shiboken with \ +FORCE_LIMITED_API set to '1', so that only the Python major version matters. +Built with: '${SHIBOKEN_PYTHON_VERSION_MAJOR}.${SHIBOKEN_PYTHON_VERSION_MINOR}' \ +Detected: '${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}'") + endif() + endif() +endfunction() + +function(shiboken_internal_disable_pkg_config) + # Disable pkg-config by setting an empty executable path. There's no documented way to + # mark the package as not found, but we can force all pkg_check_modules calls to do nothing + # by setting the variable to an empty value. + set(PKG_CONFIG_EXECUTABLE "" CACHE STRING "Disabled pkg-config usage." FORCE) +endfunction() + +function(shiboken_internal_disable_pkg_config_if_needed) + if(SHIBOKEN_SKIP_PKG_CONFIG_ADJUSTMENT) + return() + endif() + + # pkg-config should not be used by default on Darwin platforms. + if(APPLE) + set(pkg_config_enabled OFF) + else() + set(pkg_config_enabled ON) + endif() + + if(NOT pkg_config_enabled) + shiboken_internal_disable_pkg_config() + endif() +endfunction() + +function(shiboken_internal_detect_if_cross_building) + if(CMAKE_CROSSCOMPILING OR QFP_SHIBOKEN_HOST_PATH) + set(is_cross_build TRUE) + else() + set(is_cross_build FALSE) + endif() + set(SHIBOKEN_IS_CROSS_BUILD "${is_cross_build}" PARENT_SCOPE) + message(STATUS "SHIBOKEN_IS_CROSS_BUILD: ${is_cross_build}") +endfunction() + +function(shiboken_internal_decide_parts_to_build) + set(build_libs_default ON) + option(SHIBOKEN_BUILD_LIBS "Build shiboken libraries" ${build_libs_default}) + message(STATUS "SHIBOKEN_BUILD_LIBS: ${SHIBOKEN_BUILD_LIBS}") + + if(SHIBOKEN_IS_CROSS_BUILD) + set(build_tools_default OFF) + else() + set(build_tools_default ON) + endif() + option(SHIBOKEN_BUILD_TOOLS "Build shiboken tools" ${build_tools_default}) + message(STATUS "SHIBOKEN_BUILD_TOOLS: ${SHIBOKEN_BUILD_TOOLS}") + + if(SHIBOKEN_IS_CROSS_BUILD) + set(_shiboken_build_tests_default OFF) + elseif(SHIBOKEN_BUILD_LIBS) + set(_shiboken_build_tests_default ON) + endif() + option(BUILD_TESTS "Build tests." ${_shiboken_build_tests_default}) + message(STATUS "BUILD_TESTS: ${BUILD_TESTS}") +endfunction() + +function(shiboken_internal_find_host_shiboken_tools) + if(SHIBOKEN_IS_CROSS_BUILD) + set(find_package_extra_args) + if(QFP_SHIBOKEN_HOST_PATH) + list(APPEND find_package_extra_args PATHS "${QFP_SHIBOKEN_HOST_PATH}/lib/cmake") + list(PREPEND CMAKE_FIND_ROOT_PATH "${QFP_SHIBOKEN_HOST_PATH}") + endif() + find_package( + Shiboken6Tools 6 CONFIG + ${find_package_extra_args} + ) + + if(NOT Shiboken6Tools_DIR) + message(FATAL_ERROR + "Shiboken6Tools package was not found. " + "Please set QFP_SHIBOKEN_HOST_PATH to the location where the Shiboken6Tools CMake " + "package is installed.") + endif() + endif() +endfunction() + +function(shiboken_internal_set_up_extra_dependency_paths) + set(extra_root_path_vars + QFP_QT_TARGET_PATH + QFP_PYTHON_TARGET_PATH + ) + foreach(root_path IN LISTS extra_root_path_vars) + set(new_root_path_value "${${root_path}}") + if(new_root_path_value) + set(new_prefix_path "${CMAKE_PREFIX_PATH}") + list(PREPEND new_prefix_path "${new_root_path_value}/lib/cmake") + set(CMAKE_PREFIX_PATH "${new_prefix_path}") + set(CMAKE_PREFIX_PATH "${new_prefix_path}" PARENT_SCOPE) + + # Need to adjust the prefix and root paths so that find_package(Qt) and other 3rd + # party packages are found successfully when they are located outside of the + # default sysroot (whatever that maybe for the target platform). + if(SHIBOKEN_IS_CROSS_BUILD) + set(new_root_path "${CMAKE_FIND_ROOT_PATH}") + list(PREPEND new_root_path "${new_root_path_value}") + set(CMAKE_FIND_ROOT_PATH "${new_root_path}") + set(CMAKE_FIND_ROOT_PATH "${new_root_path}" PARENT_SCOPE) + endif() + endif() + endforeach() +endfunction() + +macro(compute_config_py_values + full_version_var_name + ) + set(QT_MACOS_DEPLOYMENT_TARGET "") + if (Qt${QT_MAJOR_VERSION}Core_FOUND) + get_target_property(darwin_target Qt6::Core QT_DARWIN_MIN_DEPLOYMENT_TARGET) + if(darwin_target) + set(QT_MACOS_DEPLOYMENT_TARGET + "__qt_macos_min_deployment_target__ = '${darwin_target}'") + endif() + elseif(APPLE) + message(FATAL_ERROR "Qt6::Core should be found before calling this macro") + endif() + + string(TIMESTAMP PACKAGE_BUILD_DATE "%Y-%m-%dT%H:%M:%S+00:00" UTC) + if (PACKAGE_BUILD_DATE) + set(PACKAGE_BUILD_DATE "__build_date__ = '${PACKAGE_BUILD_DATE}'") + endif() + + if (PACKAGE_SETUP_PY_PACKAGE_VERSION) + set(PACKAGE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT "__setup_py_package_version__ = '${PACKAGE_SETUP_PY_PACKAGE_VERSION}'") + set(FINAL_PACKAGE_VERSION ${PACKAGE_SETUP_PY_PACKAGE_VERSION}) + else() + set(FINAL_PACKAGE_VERSION ${${full_version_var_name}}) + endif() + + if (PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP) + set(PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT "__setup_py_package_timestamp__ = '${PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP}'") + else() + set(PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT "") + endif() + + find_package(Git) + if(GIT_FOUND) + # Check if current source folder is inside a git repo, so that commit information can be + # queried. + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --git-dir + OUTPUT_VARIABLE PACKAGE_SOURCE_IS_INSIDE_REPO + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(PACKAGE_SOURCE_IS_INSIDE_REPO) + # Force git dates to be UTC-based. + set(ENV{TZ} UTC) + execute_process( + COMMAND ${GIT_EXECUTABLE} --no-pager show --date=format-local:%Y-%m-%dT%H:%M:%S+00:00 -s --format=%cd HEAD + OUTPUT_VARIABLE PACKAGE_BUILD_COMMIT_DATE + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(PACKAGE_BUILD_COMMIT_DATE) + set(PACKAGE_BUILD_COMMIT_DATE "__build_commit_date__ = '${PACKAGE_BUILD_COMMIT_DATE}'") + endif() + unset(ENV{TZ}) + + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + OUTPUT_VARIABLE PACKAGE_BUILD_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(PACKAGE_BUILD_COMMIT_HASH) + set(PACKAGE_BUILD_COMMIT_HASH "__build_commit_hash__ = '${PACKAGE_BUILD_COMMIT_HASH}'") + endif() + + execute_process( + COMMAND ${GIT_EXECUTABLE} describe HEAD + OUTPUT_VARIABLE PACKAGE_BUILD_COMMIT_HASH_DESCRIBED + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(PACKAGE_BUILD_COMMIT_HASH_DESCRIBED) + set(PACKAGE_BUILD_COMMIT_HASH_DESCRIBED "__build_commit_hash_described__ = '${PACKAGE_BUILD_COMMIT_HASH_DESCRIBED}'") + endif() + + endif() + endif() + +endmacro() + +# Creates a new target called "${library_name}_generator" which +# depends on the mjb_rejected_classes.log file generated by shiboken. +# This target is added as a dependency to ${library_name} target. +# This file's timestamp informs cmake when the last generation was +# done, without force-updating the timestamps of the generated class +# cpp files. +# In practical terms this means that changing some injection code in +# an xml file that modifies only one specific class cpp file, will +# not force rebuilding all the cpp files, and thus allow for better +# incremental builds. +macro(create_generator_target library_name) + add_custom_target(${library_name}_generator DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log") + add_dependencies(${library_name} ${library_name}_generator) +endmacro() + +# Generate a shell script wrapper that sets environment variables for executing a specific tool. +# +# tool_name should be a unique tool name, preferably without spaces. +# Returns the wrapper path in path_out_var. +# +# Currently adds the Qt lib dir and libclang to PATH / LD_LIBRARY_PATH / DYLD_LIBRARY_PATH. +# Meant to be used as the first argument to add_custom_command's COMMAND option. +# TODO: Remove tool_name as the tool_name for this function is always shiboken. +function(shiboken_get_tool_shell_wrapper tool_name path_out_var) + # Generate the wrapper only once during the execution of CMake. + get_property(is_called GLOBAL PROPERTY "_shiboken_tool_wrapper_${tool_name}_created") + + if(is_called) + get_property(wrapper_path GLOBAL PROPERTY "_shiboken_tool_wrapper_${tool_name}_path") + set(${path_out_var} "${wrapper_path}" PARENT_SCOPE) + return() + endif() + + set(path_dirs "") + set(path_dirs_native "") + + if(CMAKE_HOST_WIN32) + set(wrapper_script_extension ".bat") + else() + set(wrapper_script_extension ".sh") + endif() + + # Try to get original host shiboken paths from exported target properties. + shiboken_get_host_tool_wrapper_properties(orig_qt_library_dir_absolute orig_libclang_lib_dir) + + # Get path to the Qt bin/lib dir depending on the platform and developer input. + # Prefer values given on the command line, then the original host path if it exists, otherwise + # try to use the Qt install prefix and libclang env vars. + # + # Note that in a cross-compiling case, using the Qt install prefix is very likely + # wrong, because you want to use the location of the host Qt, not the target Qt. Same for + # libclang. Unfortunately we currently don't provide a host Qt and host libclang option via + # setup.py, so the manual cmake vars will have to suffice. + if(SHIBOKEN_WRAPPER_HOST_QT_LIB_PATH AND EXISTS "${SHIBOKEN_WRAPPER_HOST_QT_LIB_PATH}") + set(qt_library_dir_absolute "${SHIBOKEN_WRAPPER_HOST_QT_LIB_PATH}") + elseif(orig_qt_library_dir_absolute AND EXISTS "${orig_qt_library_dir_absolute}") + set(qt_library_dir_absolute "${orig_qt_library_dir_absolute}") + elseif(CMAKE_HOST_WIN32) + # in Windows the Qt dll are store `bin` in directory + set(qt_library_dir ${QT6_INSTALL_BINS}) + else() + # in Unix the .so are stored in `lib` directory + set(qt_library_dir ${QT6_INSTALL_LIBS}) + endif() + + # Assert that Qt is already found. + if((QT6_INSTALL_PREFIX AND qt_library_dir) OR orig_qt_library_dir_absolute) + else() + message(FATAL_ERROR "Qt should have been found already by now.") + endif() + + if(NOT qt_library_dir_absolute) + set(qt_library_dir_absolute "${QT6_INSTALL_PREFIX}/${qt_library_dir}") + endif() + list(APPEND path_dirs "${qt_library_dir_absolute}") + + # Get libclang lib dir path. + # Prefer values given on the command line, then the original host path if it exists. + if(SHIBOKEN_WRAPPER_HOST_CLANG_LIB_PATH AND EXISTS "${SHIBOKEN_WRAPPER_HOST_CLANG_LIB_PATH}") + set(libclang_lib_dir "${SHIBOKEN_WRAPPER_HOST_CLANG_LIB_PATH}") + elseif(orig_libclang_lib_dir AND EXISTS "${orig_libclang_lib_dir}") + set(libclang_lib_dir "${orig_libclang_lib_dir}") + else() + # find libclang + find_libclang() + endif() + + if(libclang_lib_dir) + list(APPEND path_dirs "${libclang_lib_dir}") + endif() + + # Convert the paths from unix-style to native Windows style. + foreach(path_dir IN LISTS path_dirs) + if(EXISTS "${path_dir}") + file(TO_NATIVE_PATH "${path_dir}" path_dir_native) + list(APPEND path_dirs_native "${path_dir_native}") + endif() + endforeach() + + set(wrapper_dir "${CMAKE_BINARY_DIR}/.qfp/bin") + file(MAKE_DIRECTORY "${wrapper_dir}") + set(wrapper_path "${wrapper_dir}/${tool_name}_wrapper${wrapper_script_extension}") + + if(CMAKE_HOST_WIN32) + file(WRITE "${wrapper_path}" "@echo off +set PATH=${path_dirs_native};%PATH% +%*") + elseif(CMAKE_HOST_APPLE) + string(REPLACE ";" ":" path_dirs_native "${path_dirs_native}") + file(WRITE "${wrapper_path}" "#!/bin/bash +export DYLD_LIBRARY_PATH=${path_dirs_native}:$DYLD_LIBRARY_PATH +export DYLD_FRAMEWORK_PATH=${path_dirs_native}:$DYLD_FRAMEWORK_PATH +$@") + else() + string(REPLACE ";" ":" path_dirs_native "${path_dirs_native}") + file(WRITE "${wrapper_path}" "#!/bin/bash +export LD_LIBRARY_PATH=${path_dirs_native}:$LD_LIBRARY_PATH +$@") + endif() + + # Remember the creation of the file for a specific tool. + set_property(GLOBAL PROPERTY "_shiboken_tool_wrapper_${tool_name}_path" "${wrapper_path}") + set_property(GLOBAL PROPERTY "_shiboken_tool_wrapper_${tool_name}_created" TRUE) + + # Save original host paths for future cross-builds. + shiboken_save_host_tool_wrapper_properties("${qt_library_dir_absolute}" "${libclang_lib_dir}") + + # give execute permission to run the file + if(CMAKE_HOST_UNIX) + execute_process(COMMAND chmod +x ${wrapper_path}) + endif() + + set(${path_out_var} "${wrapper_path}" PARENT_SCOPE) +endfunction() + +# Retrieve the original host shiboken runtime dependency paths from the installed (namespaced) +# shiboken generator target. +function(shiboken_get_host_tool_wrapper_properties out_qt_library_dir out_libclang_lib_dir) + if(TARGET Shiboken6::shiboken6) + get_target_property(qt_library_dir Shiboken6::shiboken6 _shiboken_original_qt_lib_dir) + if(NOT qt_library_dir) + set(qt_library_dir "") + endif() + get_target_property(libclang_lib_dir Shiboken6::shiboken6 + _shiboken_original_libclang_lib_dir) + if(NOT libclang_lib_dir) + set(libclang_lib_dir "") + endif() + endif() + + set(${out_qt_library_dir} "${qt_library_dir}" PARENT_SCOPE) + set(${out_libclang_lib_dir} "${libclang_lib_dir}" PARENT_SCOPE) +endfunction() + +# Save original host shiboken runtime dependency paths as target properties, so they can be used +# when generating the wrapper file for cross-builds. +# Should only be done when shiboken is being built (aka it's a non-imported target). +function(shiboken_save_host_tool_wrapper_properties qt_library_dir libclang_lib_dir) + if(TARGET shiboken6) + get_target_property(is_imported shiboken6 IMPORTED) + if(is_imported) + return() + endif() + + set_target_properties(shiboken6 PROPERTIES + _shiboken_original_qt_lib_dir "${qt_library_dir}") + set_property(TARGET shiboken6 APPEND PROPERTY + EXPORT_PROPERTIES _shiboken_original_qt_lib_dir) + if(libclang_lib_dir) + set_target_properties(shiboken6 PROPERTIES + _shiboken_original_libclang_lib_dir "${libclang_lib_dir}") + set_property(TARGET shiboken6 APPEND PROPERTY + EXPORT_PROPERTIES _shiboken_original_libclang_lib_dir) + endif() + endif() +endfunction() + +# Returns the platform-specific relative rpath base token, if it's supported. +# If it's not supported, returns the string NO_KNOWN_RPATH_REL_BASE. +function(get_rpath_base_token out_var) + if(APPLE) + set(rpath_rel_base "@loader_path") + elseif(UNIX) + set(rpath_rel_base "$ORIGIN") + else() + #has no effect on Windows + set(rpath_rel_base "NO_KNOWN_RPATH_REL_BASE") + endif() + set(${out_var} "${rpath_rel_base}" PARENT_SCOPE) +endfunction() + +# Get path to libclang.dll/libclang.so depending on the platform +macro(find_libclang) + if(CMAKE_HOST_WIN32) + set(libclang_directory_suffix "bin") + set(libclang_suffix ".dll") + else() + set(libclang_directory_suffix "lib") + if(CMAKE_HOST_APPLE) + set(libclang_suffix ".dylib") + else() + set(libclang_suffix ".so") + endif() + endif() + + set(libclang_lib_dir "") + if(DEFINED ENV{LLVM_INSTALL_DIR}) + set(libclang_lib_dir "$ENV{LLVM_INSTALL_DIR}/${libclang_directory_suffix}") + elseif(DEFINED ENV{CLANG_INSTALL_DIR}) + set(libclang_lib_dir "$ENV{CLANG_INSTALL_DIR}/${libclang_directory_suffix}") + else() + message(WARNING + "Couldn't find libclang${libclang_suffix} " + "You will likely need to add it manually to PATH to ensure the build succeeds.") + endif() +endmacro() + +# Allow setting a shiboken debug level from the the build system or from the environment +# to all shiboken invocations. +function(shiboken_get_debug_level out_var) + set(debug_level "") + if(SHIBOKEN_DEBUG_LEVEL) + set(debug_level "--debug-level=${SHIBOKEN_DEBUG_LEVEL}") + elseif(DEFINED $ENV{SHIBOKEN_DEBUG_LEVEL}) + set(debug_level "--debug-level=$ENV{SHIBOKEN_DEBUG_LEVEL}") + endif() + set(${out_var} "${debug_level}" PARENT_SCOPE) +endfunction() |