7e82577680
This replaces the qt_parse_all_arguments macro with the built-in `cmake_parse_arguments(PARSE_ARGV`. In addition, a new function, _qt_internal_validate_all_args_are_parsed, can be used to check whether any _UNPARSED_ARGUMENTS have been passed to the function. Fixes: QTBUG-99238 Change-Id: I8cee83dc92dc6acdaaf747ea6ff9084c11dc649b Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
254 lines
10 KiB
CMake
254 lines
10 KiB
CMake
# Copyright (C) 2022 The Qt Company Ltd.
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
# 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(qt_internal_get_relative_rpath_base_token out_var)
|
|
if(APPLE)
|
|
set(rpath_rel_base "@loader_path")
|
|
elseif(LINUX OR SOLARIS OR FREEBSD OR HURD)
|
|
set(rpath_rel_base "$ORIGIN")
|
|
else()
|
|
set(rpath_rel_base "NO_KNOWN_RPATH_REL_BASE")
|
|
endif()
|
|
set(${out_var} "${rpath_rel_base}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Computes a relative rpath between ${rpath} and ${install_location} using tokens
|
|
# like $ORIGIN / @loader_path
|
|
# Not all platforms support such tokens though, in which case the returned rpath will be invalid.
|
|
#
|
|
# install_location: a directory relative to CMAKE_INSTALL_PREFIX, where the binary will be installed
|
|
# rpath: an rpath to embed, can be either an absolute path or a path relative to
|
|
# ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR}.
|
|
function(qt_compute_relative_rpath_base rpath install_location out_var)
|
|
set(install_lib_dir_absolute "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR}")
|
|
get_filename_component(rpath_absolute "${rpath}"
|
|
ABSOLUTE BASE_DIR "${install_lib_dir_absolute}")
|
|
|
|
set(install_location_absolute "${install_location}")
|
|
if(NOT IS_ABSOLUTE "${install_location_absolute}")
|
|
set(install_location_absolute "${CMAKE_INSTALL_PREFIX}/${install_location}")
|
|
endif()
|
|
# Compute relative rpath from where the target will be installed, to the place where libraries
|
|
# will be placed (INSTALL_LIBDIR).
|
|
file(RELATIVE_PATH rpath_relative "${install_location_absolute}" "${rpath_absolute}")
|
|
|
|
if("${rpath_relative}" STREQUAL "")
|
|
# file(RELATIVE_PATH) returns an empty string if the given absolute paths are equal
|
|
set(rpath_relative ".")
|
|
endif()
|
|
|
|
# Prepend $ORIGIN / @loader_path style tokens (qmake's QMAKE_REL_RPATH_BASE), to make the
|
|
# relative rpaths work. qmake does this automatically when generating a project, so it wasn't
|
|
# needed in the .prf files, but for CMake we need to prepend them ourselves.
|
|
qt_internal_get_relative_rpath_base_token(rpath_rel_base)
|
|
if(rpath_rel_base STREQUAL "NO_KNOWN_RPATH_REL_BASE")
|
|
message(WARNING "No known RPATH_REL_BASE for target platform.")
|
|
endif()
|
|
|
|
if(rpath_relative STREQUAL ".")
|
|
set(rpath_relative "${rpath_rel_base}")
|
|
else()
|
|
set(rpath_relative "${rpath_rel_base}/${rpath_relative}")
|
|
endif()
|
|
|
|
set("${out_var}" "${rpath_relative}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Applies necessary rpaths to a target upon target installation.
|
|
# No-op when targeting Windows, Android.
|
|
#
|
|
# Since abf72395411b135054b5820f64f93dfbcda430b8 rpaths are also applied in non-prefix builds,
|
|
# to address -rpath-link issues when cross-compiling, although this might not be needed anymore
|
|
# due to 606124c5cceba0dd4a406a9278588b58bb9f9800.
|
|
# See QTBUG-86533 for the whole saga.
|
|
#
|
|
# If no RELATIVE_RPATH option is given, embeds an absolute path rpath to
|
|
# ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR} into the target.
|
|
|
|
# If RELATIVE_RPATH is given, the INSTALL_PATH value is used to compute the relative path from
|
|
# ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR} to wherever the target will be installed
|
|
# (the value of INSTALL_PATH).
|
|
# INSTALL_PATH is expected to be a relative directory where the binary / library will be installed.
|
|
|
|
# RELATIVE_RPATH is the equivalent of qmake's relative_qt_rpath.
|
|
# INSTALL_PATH is used to implement the equivalent of qmake's $$qtRelativeRPathBase().
|
|
#
|
|
# QT_DISABLE_RPATH can be set to disable embedding any Qt specific rpaths.
|
|
function(qt_apply_rpaths)
|
|
# No rpath support for win32 and android.
|
|
if(WIN32 OR ANDROID)
|
|
return()
|
|
endif()
|
|
|
|
cmake_parse_arguments(PARSE_ARGV 0 arg
|
|
"RELATIVE_RPATH"
|
|
"TARGET;INSTALL_PATH"
|
|
"")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
if(NOT arg_TARGET)
|
|
message(FATAL_ERROR "No target given to qt_apply_rpaths.")
|
|
else()
|
|
set(target "${arg_TARGET}")
|
|
endif()
|
|
|
|
# Rpaths explicitly disabled (like for uikit), equivalent to qmake's no_qt_rpath.
|
|
# Or feature was turned OFF.
|
|
if(QT_DISABLE_RPATH OR NOT QT_FEATURE_rpath)
|
|
set_target_properties(${target} PROPERTIES
|
|
SKIP_BUILD_RPATH ON
|
|
SKIP_INSTALL_RPATH ON
|
|
)
|
|
if(APPLE)
|
|
set_target_properties(${target} PROPERTIES
|
|
MACOSX_RPATH OFF
|
|
)
|
|
endif()
|
|
return()
|
|
endif()
|
|
|
|
# If a target is not built (which can happen for tools when crosscompiling, we shouldn't try
|
|
# to apply properties.
|
|
if(NOT TARGET "${target}")
|
|
return()
|
|
endif()
|
|
|
|
# Protect against interface libraries.
|
|
get_target_property(target_type "${target}" TYPE)
|
|
if (target_type STREQUAL "INTERFACE_LIBRARY")
|
|
return()
|
|
endif()
|
|
|
|
if(NOT arg_INSTALL_PATH)
|
|
message(FATAL_ERROR "No INSTALL_PATH given to qt_apply_rpaths.")
|
|
endif()
|
|
|
|
set(rpaths "")
|
|
|
|
# Modify the install path to contain the nested structure of a framework.
|
|
get_target_property(is_framework "${target}" FRAMEWORK)
|
|
if(is_framework)
|
|
qt_internal_get_framework_info(fw ${target})
|
|
if(UIKIT)
|
|
# Shallow framework
|
|
string(APPEND arg_INSTALL_PATH "/${fw_dir}")
|
|
else()
|
|
# Full framework
|
|
string(APPEND arg_INSTALL_PATH "/${fw_dir}/Versions/Current")
|
|
endif()
|
|
endif()
|
|
|
|
# Same but for an app bundle.
|
|
get_target_property(is_bundle "${target}" MACOSX_BUNDLE)
|
|
if(is_bundle AND NOT is_framework)
|
|
if(UIKIT)
|
|
# Shallow bundle
|
|
string(APPEND arg_INSTALL_PATH "/${target}.app")
|
|
else()
|
|
# Full bundle
|
|
string(APPEND arg_INSTALL_PATH "/${target}.app/Contents/MacOS")
|
|
endif()
|
|
endif()
|
|
|
|
qt_internal_get_relative_rpath_base_token(rpath_base_token)
|
|
if(rpath_base_token STREQUAL "NO_KNOWN_RPATH_REL_BASE")
|
|
set(relative_rpath_supported FALSE)
|
|
else()
|
|
set(relative_rpath_supported TRUE)
|
|
endif()
|
|
|
|
# Somewhat similar to mkspecs/features/qt.prf
|
|
# Embed either an absolute path to the installed Qt lib dir, or a relative one, based on
|
|
# where ${target} is installed.
|
|
# Don't embed relative rpaths if the platform does not support it.
|
|
if(arg_RELATIVE_RPATH AND relative_rpath_supported)
|
|
qt_compute_relative_rpath_base(
|
|
"${_default_install_rpath}" "${arg_INSTALL_PATH}" relative_rpath)
|
|
list(APPEND rpaths "${relative_rpath}")
|
|
else()
|
|
list(APPEND rpaths "${_default_install_rpath}")
|
|
endif()
|
|
|
|
# Somewhat similar to mkspecs/features/qt_build_extra.prf.
|
|
foreach(rpath ${QT_EXTRA_RPATHS})
|
|
if(IS_ABSOLUTE "${rpath}")
|
|
list(APPEND rpaths "${rpath}")
|
|
else()
|
|
if(relative_rpath_supported)
|
|
qt_compute_relative_rpath_base("${rpath}" "${arg_INSTALL_PATH}" relative_rpath)
|
|
list(APPEND rpaths "${relative_rpath}")
|
|
else()
|
|
# Any extra relative rpaths on a platform that does not support relative rpaths,
|
|
# need to be transformed into absolute ones.
|
|
set(install_lib_dir_absolute "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR}")
|
|
get_filename_component(rpath_absolute "${rpath}"
|
|
ABSOLUTE BASE_DIR "${install_lib_dir_absolute}")
|
|
list(APPEND rpaths "${rpath_absolute}")
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
|
|
if(rpaths)
|
|
list(REMOVE_DUPLICATES rpaths)
|
|
if(QT_WILL_INSTALL)
|
|
set(prop_name "INSTALL_RPATH")
|
|
else()
|
|
set(prop_name "BUILD_RPATH")
|
|
endif()
|
|
set_property(TARGET "${target}" APPEND PROPERTY "${prop_name}" ${rpaths})
|
|
endif()
|
|
endfunction()
|
|
|
|
# Overrides the CMAKE_STAGING_PREFIX in a subdirectory scope, to stop CMake from rewriting build
|
|
# rpaths to point into the original staging prefix, and thus breaking running executables from
|
|
# the build directory.
|
|
# See details at https://bugreports.qt.io/browse/QTBUG-102592
|
|
# and https://gitlab.kitware.com/cmake/cmake/-/issues/23421
|
|
#
|
|
# This macro is only meant to be called in functions like
|
|
# qt_internal_add_module / qt_internal_add_tool to ensure the variable is set in the
|
|
# subdirectory scope of the calling function, and not in the actual function scope (where the
|
|
# variable assignment would have no effect).
|
|
#
|
|
# This is the best workaround we can currently do, but it comes with the disadvantage that calling
|
|
# subdirectory-scoped install targets does not work anymore.
|
|
# e.g. calling ninja src/gui/install will try to install to the fake prefix and fail.
|
|
# A regular ninja install call works fine.
|
|
#
|
|
# Usage of this macro assumes there are no binaries or libraries added in the root CMakeLists.txt
|
|
# of the project because that would mean the macro is called at root level scope, which would
|
|
# break installation.
|
|
#
|
|
# The implementation has to be a macro, so we can propagate the variable into the calling
|
|
# subdirectory scope. The implementation can't use return().
|
|
macro(qt_internal_apply_staging_prefix_build_rpath_workaround)
|
|
set(__qt_internal_should_apply_staging_prefix_build_rpath_workaround TRUE)
|
|
# Allow an opt out.
|
|
if(QT_NO_STAGING_PREFIX_BUILD_RPATH_WORKAROUND)
|
|
set(__qt_internal_should_apply_staging_prefix_build_rpath_workaround FALSE)
|
|
endif()
|
|
|
|
# No need for workaround if CMAKE_STAGING_PREFIX is not set.
|
|
if(NOT CMAKE_STAGING_PREFIX)
|
|
set(__qt_internal_should_apply_staging_prefix_build_rpath_workaround FALSE)
|
|
endif()
|
|
|
|
# No rpath support for win32, android, ios, so nothing to do.
|
|
if(WIN32 OR ANDROID OR UIKIT)
|
|
set(__qt_internal_should_apply_staging_prefix_build_rpath_workaround FALSE)
|
|
endif()
|
|
|
|
# Set the staging prefix to a non-existent directory, which is unlikely to have permissions
|
|
# for installation.
|
|
# The verbose directory name is chosen to attract the user's attention in case if they end up
|
|
# calling a subdirectory-scope install file.
|
|
if(__qt_internal_should_apply_staging_prefix_build_rpath_workaround)
|
|
set_property(GLOBAL PROPERTY _qt_internal_staging_prefix_build_rpath_workaround TRUE)
|
|
set(CMAKE_STAGING_PREFIX
|
|
"/qt_fake_staging_prefix/check_qt_internal_apply_staging_prefix_build_rpath_workaround"
|
|
PARENT_SCOPE)
|
|
endif()
|
|
endmacro()
|