diff options
Diffstat (limited to 'sources/shiboken6/cmake/ShibokenHelpers.cmake')
-rw-r--r-- | sources/shiboken6/cmake/ShibokenHelpers.cmake | 351 |
1 files changed, 282 insertions, 69 deletions
diff --git a/sources/shiboken6/cmake/ShibokenHelpers.cmake b/sources/shiboken6/cmake/ShibokenHelpers.cmake index 567a79c62..8bc066102 100644 --- a/sources/shiboken6/cmake/ShibokenHelpers.cmake +++ b/sources/shiboken6/cmake/ShibokenHelpers.cmake @@ -1,3 +1,6 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(CMakeParseArguments) macro(set_limited_api) @@ -12,7 +15,7 @@ endmacro() macro(set_debug_build) set(SHIBOKEN_BUILD_TYPE "Debug") - if(NOT PYTHON_DEBUG_LIBRARIES) + if(NOT Python_LIBRARIES) message(WARNING "Python debug shared library not found; \ assuming python was built with shared library support disabled.") endif() @@ -50,9 +53,14 @@ if(MSVC) 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} -Wall -fvisibility=hidden -Wno-strict-aliasing") + 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 \ @@ -108,7 +116,7 @@ macro(shiboken_internal_set_python_site_packages) endif() else() execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "if True: + COMMAND ${Python_EXECUTABLE} -c "if True: import sysconfig from os.path import sep @@ -194,6 +202,12 @@ macro(get_python_extension_suffix) # 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() @@ -201,7 +215,7 @@ macro(get_python_extension_suffix) else() # See PYSIDE-1841 / https://bugs.python.org/issue39825 for distutils vs sysconfig execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "if True: + COMMAND ${Python_EXECUTABLE} -c "if True: import sys if sys.version_info >= (3, 8, 2): import sysconfig @@ -233,40 +247,44 @@ 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) + 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: - import os - for lib in '${PYTHON_LIBRARIES}'.split(';'): - if '/' in lib and os.path.isfile(lib): - prefix, py = lib.rsplit('/', 1) + 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') + print(prefix / 'python3.lib') break " OUTPUT_VARIABLE PYTHON_LIMITED_LIBRARIES OUTPUT_STRIP_TRAILING_WHITESPACE) endif() - if(FORCE_LIMITED_API STREQUAL "yes") - if (${PYTHON_VERSION_MAJOR} EQUAL 3 AND ${PYTHON_VERSION_MINOR} GREATER 4) - # GREATER_EQUAL is available only from cmake 3.7 on. We mean python 3.5 . - set(PYTHON_LIMITED_API 1) - 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) - if (${PYTHON_VERSION_MAJOR} EQUAL 3 AND ${PYTHON_VERSION_MINOR} GREATER 4) - # PYSIDE-560: XXX maybe add an option to setup.py as override - set(SHIBOKEN_PYTHON_LIBRARIES ${PYTHON_LIMITED_LIBRARIES}) - endif() + 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. @@ -274,11 +292,6 @@ macro(shiboken_find_required_python) # 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) - set(_shiboken_find_python_version_args "") - if(${ARGC} GREATER 0) - list(APPEND _shiboken_find_python_version_args "${ARGV0}") - endif() - # 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 @@ -307,42 +320,25 @@ macro(shiboken_find_required_python) "${_shiboken_backup_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}") set(CMAKE_FIND_ROOT_PATH "${_shiboken_backup_CMAKE_FIND_ROOT_PATH}") - - # Mirror the variables that FindPythonInterp sets, instead of conditionally checking - # and modifying all the places where the variables are used. - set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}") - set(PYTHON_VERSION "${Python_VERSION}") - set(PYTHON_LIBRARIES "${Python_LIBRARIES}") - set(PYTHON_INCLUDE_DIRS "${Python_INCLUDE_DIRS}") - set(PYTHONINTERP_FOUND "${Python_Interpreter_FOUND}") - set(PYTHONINTERP_FOUND "${Python_Interpreter_FOUND}") - set(PYTHONLIBS_FOUND "${Python_Development_FOUND}") - set(PYTHON_VERSION_MAJOR "${Python_VERSION_MAJOR}") - set(PYTHON_VERSION_MINOR "${Python_VERSION_MINOR}") - set(PYTHON_VERSION_PATCH "${Python_VERSION_PATCH}") else() - if(${ARGC} GREATER 0) - find_package(PythonInterp ${ARGV0} REQUIRED) - find_package(PythonLibs ${ARGV0} REQUIRED) - else() - # If no version is specified, just use any interpreter that can be found (from PATH). - # This is useful for super-project builds, so that the default system interpeter - # gets picked up (e.g. /usr/bin/python and not /usr/bin/python2.7). - find_package(PythonInterp REQUIRED) - find_package(PythonLibs REQUIRED) - endif() + 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}") + 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 "5") + if(Python_VERSION_MAJOR EQUAL "3" AND Python_VERSION_MINOR LESS "7") message(FATAL_ERROR - "Shiboken requires Python 3.5+.") + "Shiboken requires Python 3.7+.") endif() endmacro() @@ -361,14 +357,14 @@ macro(shiboken_compute_python_includes) 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}) + APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Python_INCLUDE_DIRS}) else() target_include_directories(libshiboken - PUBLIC $<BUILD_INTERFACE:${PYTHON_INCLUDE_DIRS}>) + PUBLIC $<BUILD_INTERFACE:${Python_INCLUDE_DIRS}>) endif() - set(SHIBOKEN_PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIRS}") + set(SHIBOKEN_PYTHON_INCLUDE_DIRS "${Python_INCLUDE_DIRS}") set_property(GLOBAL PROPERTY shiboken_python_include_dirs "${SHIBOKEN_PYTHON_INCLUDE_DIRS}") @@ -428,16 +424,8 @@ macro(shiboken_compute_python_libraries) set(SHIBOKEN_PYTHON_LIBRARIES "") endif() - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - if(WIN32 AND NOT SHIBOKEN_PYTHON_LIBRARIES) - set(SHIBOKEN_PYTHON_LIBRARIES ${PYTHON_DEBUG_LIBRARIES}) - endif() - endif() - - if(CMAKE_BUILD_TYPE STREQUAL "Release") - if(WIN32 AND NOT SHIBOKEN_PYTHON_LIBRARIES) - set(SHIBOKEN_PYTHON_LIBRARIES ${PYTHON_LIBRARIES}) - endif() + if(WIN32 AND NOT SHIBOKEN_PYTHON_LIBRARIES) + set(SHIBOKEN_PYTHON_LIBRARIES ${Python_LIBRARIES}) endif() # If the resulting variable @@ -470,20 +458,20 @@ macro(shiboken_compute_python_libraries) endmacro() function(shiboken_check_if_built_and_target_python_are_compatible) - if(NOT SHIBOKEN_PYTHON_VERSION_MAJOR STREQUAL PYTHON_VERSION_MAJOR) + 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}'") +Detected: '${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}'") else() if(NOT SHIBOKEN_PYTHON_LIMITED_API - AND NOT SHIBOKEN_PYTHON_VERSION_MINOR STREQUAL PYTHON_VERSION_MINOR) + 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}'") +Detected: '${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}'") endif() endif() endfunction() @@ -594,6 +582,17 @@ 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}'") @@ -669,3 +668,217 @@ 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() |