# ============================================================================
# Copyright (c) 2011-2012 University of Pennsylvania
# Copyright (c) 2013-2016 Andreas Schuh
# All rights reserved.
#
# See COPYING file for license information or visit
# https://cmake-basis.github.io/download.html#license
# ============================================================================
##############################################################################
# @file ImportTools.cmake
# @brief Functions and macros for the import of targets.
#
# @ingroup CMakeTools
##############################################################################
if (__BASIS_IMPORTTOOLS_INCLUDED)
return ()
else ()
set (__BASIS_IMPORTTOOLS_INCLUDED TRUE)
endif ()
## @addtogroup CMakeUtilities
# @{
# ----------------------------------------------------------------------------
## @brief Set target property.
#
# This function is overwritten by BASIS in order to update the information
# about imported build targets.
#
# @note Do not use this function in your CMakeLists.txt configuration files.
# Use basis_set_target_properties() instead.
#
# @note Due to a bug in CMake (http://www.cmake.org/Bug/view.php?id=12303),
# except of the first property given directly after the @c PROPERTIES keyword,
# only properties listed in @c BASIS_PROPERTIES_ON_TARGETS can be set.
#
# @param [in] ARGN List of arguments for
#
# set_target_properties().
#
# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_target_properties
function (set_target_properties)
# target names
list (FIND ARGN "PROPERTIES" IDX)
if (IDX EQUAL -1)
message (FATAL_ERROR "Missing PROPERTIES argument!")
elseif (IDX EQUAL 0)
message (FATAL_ERROR "No targets specified!")
endif ()
set (INDICES)
set (I 0)
while (I LESS IDX)
list (APPEND INDICES ${I})
math (EXPR I "${I} + 1")
endwhile ()
list (GET ARGN ${INDICES} TARGETS)
# remaining arguments are property value pairs
list (REMOVE_AT ARGN ${INDICES} ${IDX})
# set target properties
#
# Note: By iterating over the properties, the empty property values
# are correctly passed on to CMake's set_target_properties()
# command, while
# _set_target_properties(${TARGET_UIDS} PROPERTIES ${ARGN})
# (erroneously) discards the empty elements in ARGN.
if (BASIS_DEBUG)
message ("** set_target_properties:")
message ("** Target(s): ${TARGETS}")
message ("** Properties: [${ARGN}]")
endif ()
list (LENGTH ARGN N)
while (N GREATER 1)
list (GET ARGN 0 PROPERTY)
list (GET ARGN 1 VALUE)
list (REMOVE_AT ARGN 0 1)
list (LENGTH ARGN N)
# The following loop is only required b/c CMake's ARGV and ARGN
# lists do not support arguments which are themselves lists.
# Therefore, we need a way to decide when the list of values for a
# property is terminated. Hence, we only allow known properties
# to be set, except for the first property where the name follows
# directly after the PROPERTIES keyword.
while (N GREATER 0)
list (GET ARGN 0 ARG)
if (ARG MATCHES "${BASIS_PROPERTIES_ON_TARGETS_RE}")
break ()
endif ()
list (APPEND VALUE "${ARG}")
list (REMOVE_AT ARGN 0)
list (LENGTH ARGN N)
endwhile ()
if (BASIS_DEBUG)
message ("** -> ${PROPERTY} = [${VALUE}]")
endif ()
# check property name
if (PROPERTY MATCHES "^$")
message (FATAL_ERROR "Empty property name given!")
# if property is related to the location of an imported target,
# update corresponding project properties
elseif (PROPERTY MATCHES "^IMPORTED_LOCATION")
list (GET TARGETS 0 TARGET)
basis_update_imported_location (${TARGET} ${PROPERTY} "${VALUE}")
# if property is related to the type of an imported target,
# update corresponding project properties
elseif (PROPERTY MATCHES "^BASIS_TYPE$")
list (GET TARGETS 0 TARGET)
basis_update_imported_type (${TARGET} "${VALUE}")
endif ()
# set target property
_set_target_properties (${TARGETS} PROPERTIES ${PROPERTY} "${VALUE}")
endwhile ()
# make sure that every property had a corresponding value
if (NOT N EQUAL 0)
message (FATAL_ERROR "No value given for target property ${ARGN}")
endif ()
endfunction ()
# ----------------------------------------------------------------------------
## @brief Add imported target.
#
# Imported targets are only valid in the scope where they were imported.
# In order to be able to add the information of the imported executable targets
# to the ExecutableTargetInfo modules of the BASIS utilities which are configured
# during the finalization of the (top-level) project, the information of
# imported targets has to be stored in the global scope. Therefore, internal
# cache variables prefixed by the name of the project are used
# (see basis_set_project_property()):
#
#
#
# @tp @b IMPORTED_TARGETS @endtp
# | List of imported targets. |
#
#
# @tp @b IMPORTED_TYPES @endtp
# | Types of imported targets. |
#
#
# @tp @b IMPORTED_LOCATIONS @endtp
# | Locations of imported target files. |
#
#
# @tp @b IMPORTED_RANKS @endtp
# | Rank of current imported locations. This rank value is used to decide
# whether the current location takes precedence over another imported
# location. For example, IMPORTED_LOCATION_<a>, may be preferred
# over IMPORTED_LOCATION_<b>.
# |
#
#
# @param [in] TARGET Name (UID) of the imported target.
# @param [in] TYPE Type of the imported target.
#
# @sa basis_update_imported_location()
function (basis_add_imported_target TARGET TYPE)
if (BASIS_DEBUG AND BASIS_VERBOSE)
message ("** basis_add_imported_target:")
message ("** Target: ${TARGET}")
message ("** Type: ${TYPE}")
message ("** Bundled: ${BUNDLE_PROJECT}")
endif ()
if (NOT TYPE STREQUAL "INTERFACE")
if (BUNDLE_PROJECT)
_set_target_properties (${TARGET} PROPERTIES BUNDLED TRUE)
else ()
_set_target_properties (${TARGET} PROPERTIES BUNDLED FALSE)
endif ()
endif ()
# if target was added before
basis_get_project_property (TARGETS PROPERTY IMPORTED_TARGETS)
if (TARGETS)
list (FIND TARGETS "${TARGET}" IDX)
if (NOT IDX EQUAL -1)
# do nothing
return ()
endif ()
endif ()
# otherwise, add it to the project properties
basis_set_project_property (APPEND PROPERTY IMPORTED_TARGETS "${TARGET}")
basis_set_project_property (APPEND PROPERTY IMPORTED_TYPES "${TYPE}")
basis_set_project_property (APPEND PROPERTY IMPORTED_LOCATIONS "NOTFOUND")
basis_set_project_property (APPEND PROPERTY IMPORTED_RANKS 10)
endfunction ()
# ----------------------------------------------------------------------------
## @brief Update location of imported target.
#
# @param [in] TARGET Name (UID) of the imported target.
# @param [in] PROPERTY Target location property. Either IMPORTED_LOCATION
# or IMPORTED_LOCATION_<config>, where <config>
# is one of the imported build configurations.
# This argument is used to decide whether to keep
# the current target information or to replace it
# by the new one.
# @param [in] LOCATION Location of imported target.
function (basis_update_imported_location TARGET PROPERTY LOCATION)
if (BASIS_DEBUG)
message ("** basis_update_imported_location:")
message ("** Target: ${TARGET}")
message ("** Location: ${LOCATION}")
endif ()
# get index of imported target
basis_get_project_property (TARGETS PROPERTY IMPORTED_TARGETS)
list (FIND TARGETS "${TARGET}" IDX)
if (IDX EQUAL -1)
# imported targets have to be added via basis_add_imported_target() first
# otherwise, ignore target here and do not update the non-existent information
return ()
endif ()
# get current information of target
basis_get_project_property (TYPES PROPERTY IMPORTED_TYPES)
basis_get_project_property (LOCATIONS PROPERTY IMPORTED_LOCATIONS)
basis_get_project_property (RANKS PROPERTY IMPORTED_RANKS)
list (GET TYPES ${IDX} TYPE)
list (GET RANKS ${IDX} CURRENT_RANK)
# decide whether current information shall be overwritten
if (CMAKE_BUILD_TYPE)
string (TOUPPER "${CMAKE_BUILD_TYPE}" C)
else ()
set (C "NOCONFIG")
endif ()
set (
RANKING
# first pick
"IMPORTED_LOCATION_${C}" # 0) prefer location corresponding to current configuration
"IMPORTED_LOCATION" # 1) then use non-configuration specific location
"IMPORTED_LOCATION_RELEASE" # 2) otherwise use RELEASE version if available
# 3) last pick, use first imported executable
)
list (FIND RANKING "${PROPERTY}" RANK)
if (RANK EQUAL -1)
set (RANK 3)
endif ()
# bail out if current information shall be kept
if (NOT "${RANK}" LESS "${CURRENT_RANK}")
return ()
endif ()
# remove current information
list (REMOVE_AT TYPES ${IDX})
list (REMOVE_AT LOCATIONS ${IDX})
list (REMOVE_AT RANKS ${IDX})
# add imported information
list (LENGTH TYPES N)
if (IDX LESS N)
list (INSERT TYPES ${IDX} "${TYPE}")
list (INSERT LOCATIONS ${IDX} "${LOCATION}")
list (INSERT RANKS ${IDX} "${RANK}")
else ()
list (APPEND TYPES "${TYPE}")
list (APPEND LOCATIONS "${LOCATION}")
list (APPEND RANKS "${RANK}")
endif ()
# update project properties
basis_set_project_property (PROPERTY IMPORTED_TYPES "${TYPES}")
basis_set_project_property (PROPERTY IMPORTED_LOCATIONS "${LOCATIONS}")
basis_set_project_property (PROPERTY IMPORTED_RANKS "${RANKS}")
endfunction ()
# ----------------------------------------------------------------------------
## @brief Update type of imported target.
#
# This function is in particular called in basis_set_target_properties()
# if the BASIS_TYPE property of custom BASIS targets is set after the
# imported target was added with the initial type UNKNOWN.
#
# @param [in] TARGET Name (UID) of the imported target.
# @param [in] TYPE Type of imported target.
function (basis_update_imported_type TARGET TYPE)
# get index of imported target
basis_get_project_property (TARGETS PROPERTY IMPORTED_TARGETS)
list (FIND TARGETS "${TARGET}" IDX)
if (IDX EQUAL -1)
# imported targets have to be added via basis_add_imported_target() first
# otherwise, ignore target here and do not update the non-existent information
return ()
endif ()
# get current type of imported target
basis_get_project_property (TYPES PROPERTY IMPORTED_TYPES)
list (GET TYPES ${IDX} CURRENT_TYPE)
# bail out if current type shall be kept
if (NOT CURRENT_TYPE MATCHES "^UNKNOWN$")
return ()
endif ()
# replace current type
list (REMOVE_AT TYPES ${IDX})
list (LENGTH TYPES N)
if (IDX LESS N)
list (INSERT TYPES ${IDX} ${TYPE})
else ()
list (APPEND TYPES ${TYPE})
endif ()
# update project property
basis_set_project_property (PROPERTY IMPORTED_TYPES "${TYPES}")
endfunction ()
## @}
# end of Doxygen group