blob: 754c98ce76ea708111dcdd6dae0e0948a2174d80 [file] [log] [blame]
#-------------------------------------------------------------------------------
# Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
#-------------------------------------------------------------------------------
#[===[.rst:
PropertyCopy.cmake
------------------
This module allows saving interface properties of a target to a set of variables and to translate
the variables to cmake script fragment of compiler and linker flag lists.
The main purpose is to allow transferring settings to sub-projects which need strong separation
(i.e. ExternalProject is used) or use a non CMake build system.
For CMake projects the data-flow is to save the settings to variables, translate these to cmake code
fragment, and then inject these to the sub-projects using a generated initial cache file.
Alternatively translate the saved values to list variables `<PREFIX>_CMAKE_C_FLAGS_INIT` and
`<PREFIX>_CMAKE_EXE_LINKER_FLAGS_INIT`, and pass these to the sub-project using the -D command-line
parameter.
For non CMake projects the data-flow is to save the properties to variables and the translate to
compiler and linker argument lists. Then use the generated `<PREFIX>_CMAKE_C_FLAGS_INIT` and
`<PREFIX>_CMAKE_EXE_LINKER_FLAGS_INIT` variables in a build system specific way (e.g. setting
`CFLAGS` and `LDFLAGS` environment variables) to configure the sub-project.
#]===]
#[===[.rst:
.. cmake:variable:: PROPERTYCOPY_DEFAULT_PROPERTY_LIST
Default list of properties to save and restore. It is used by functions in this file. Some of these
allow using a custom list instead by passing appropriate parameters.
#]===]
set(PROPERTYCOPY_DEFAULT_PROPERTY_LIST INTERFACE_COMPILE_DEFINITIONS
INTERFACE_COMPILE_OPTIONS INTERFACE_INCLUDE_DIRECTORIES
INTERFACE_LINK_DIRECTORIES INTERFACE_LINK_LIBRARIES INTERFACE_LINK_OPTIONS
INTERFACE_POSITION_INDEPENDENT_CODE
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
#[===[.rst:
.. cmake:command:: save_interface_target_properties
.. code-block:: cmake
save_interface_target_properties(TGT stdlib:c PREFIX LIBC)
save_interface_target_properties(TGT stdlib:c PREFIX LIBC
PROPERTIES INTERFACE_LINK_DIRECTORIES INTERFACE_LINK_LIBRARIES)
Save interface properties of a target to a set of variables. Variables are named after the
properties prefixed with the parameter <PREFIX>_. (i.e. FOO_INTERFACE_COMPILE_OPTIONS if the
prefix was "FOO").
The list of properties to be saved can be set using the PROPERTIES parameter. If this is not
set, the :variable:`PROPERTYCOPY_DEFAULT_PROPERTY_LIST` is used.
Inputs:
``TGT``
Target to copy properties from.
``PROPERTIES``
Optional. List of properties to save. If not set, the default list is used. See:
:variable:`PROPERTYCOPY_DEFAULT_PROPERTY_LIST`.
``PREFIX``
Prefix to use for output variable names.
Outputs:
A set of variables (see description).
#]===]
function(save_interface_target_properties)
set(_OPTIONS_ARGS)
set(_ONE_VALUE_ARGS TGT PREFIX)
set(_MULTI_VALUE_ARGS PROPERTIES)
cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
if (NOT DEFINED _MY_PARAMS_PREFIX)
message(FATAL_ERROR "Mandatory parameter PREFIX is not defined.")
endif()
if (NOT DEFINED _MY_PARAMS_TGT)
message(FATAL_ERROR "Mandatory parameter TGT is not defined.")
endif()
if(NOT TARGET ${_MY_PARAMS_TGT})
message(FATAL_ERROR "Target \"${_MY_PARAMS_TGT}\" does not exist.")
endif()
if (NOT DEFINED _MY_PARAMS_PROPERTIES)
set(_MY_PARAMS_PROPERTIES ${PROPERTYCOPY_DEFAULT_PROPERTY_LIST})
endif()
foreach(_prop IN LISTS _MY_PARAMS_PROPERTIES )
get_property(_set TARGET ${_MY_PARAMS_TGT} PROPERTY ${_prop} SET)
if (_set)
get_property(${_MY_PARAMS_PREFIX}_${_prop} TARGET ${_MY_PARAMS_TGT} PROPERTY ${_prop})
set(${_MY_PARAMS_PREFIX}_${_prop} ${${_MY_PARAMS_PREFIX}_${_prop}} PARENT_SCOPE)
endif()
endforeach()
endfunction()
#[===[.rst:
.. cmake:command:: translate_interface_target_properties
.. code-block:: cmake
# To translate default set of properties saved to variables with ``LIBC_`` prefix
# using :command:`save_interface_target_properties`. Result string returned to
# ``_cmake_fragment``
translate_interface_target_properties(PREFIX LIBC RES _cmake_fragment)
# To translate default set of properties saved to variables with ``LIBC_`` prefix
# using :command:`save_interface_target_properties`. Results saved to lists prefixed
# with ``LIBC_``. List of generated lists is returned in ``_lists``
translate_interface_target_properties(PREFIX LIBC RES _lists)
Construct a string of cmake script fragment setting global cmake variables configuring
build properties to match saved target interface settings. The script fragment is returned
in ``RES``
Intended usage is to help transferring target specific settings to sub projects using
initial cache files.
Warning: quotation in property values is not handled. This can cause problems e.g. with
computed includes.
If ``TO_LIST`` is passed translation will be done to a lists. ``RES`` will hold a list of
list names where the settings are saved.
This mode allows further processing on the lists, e.g. to be converted to ``CFLAGS`` or
``LDFLAGS`` environment variables.
Works in tandem with :command:`save_interface_target_properties`.
Inputs:
``PREFIX``
Target to set properties on.
``VARS``
Name of variables to copy from.
``TO_LIST``
Translate to lists instead of cmake script fragment.
Outputs
``RES``
Name of variable to store the results to.
#]===]
function(translate_interface_target_properties)
set(_OPTIONS_ARGS TO_LIST)
set(_ONE_VALUE_ARGS PREFIX RES)
set(_MULTI_VALUE_ARGS VARS)
cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
if (NOT DEFINED _MY_PARAMS_PREFIX)
message(FATAL_ERROR "Mandatory parameter PREFIX is not defined.")
endif()
string(LENGTH "${_MY_PARAMS_PREFIX}_" _PREFIX_LENGT)
if (NOT DEFINED _MY_PARAMS_RES)
message(FATAL_ERROR "Mandatory parameter RES is not defined.")
endif()
if (DEFINED _MY_PARAMS_VARS)
foreach(_VAR_NAME IN LISTS _MY_PARAMS_VARS)
if (NOT DEFINED ${_VAR_NAME})
message(FATAL_ERROR "Attempt to translate undefined variable \"${_VAR_NAME}\"")
endif()
string(SUBSTRING "${_VAR_NAME}" ${_PREFIX_LENGT} -1 _prop)
_prc_translate(PROP "${_prop}" VALUE ${${_VAR_NAME}} RES _res)
if(NOT "${_res}" STREQUAL "")
list(GET _res 0 _global_var_name)
list(GET _res 1 _global_var_value)
list(APPEND ${_MY_PARAMS_PREFIX}_${_global_var_name} ${_global_var_value})
if (NOT "${_MY_PARAMS_PREFIX}_${_global_var_name}" IN_LIST _RES)
list(APPEND _RES "${_MY_PARAMS_PREFIX}_${_global_var_name}")
endif()
endif()
endforeach()
else()
foreach(_prop IN LISTS PROPERTYCOPY_DEFAULT_PROPERTY_LIST)
set(_VAR_NAME "${_MY_PARAMS_PREFIX}_${_prop}")
# Is the variable holding the value of the property available?
if (DEFINED ${_VAR_NAME})
_prc_translate(PROP "${_prop}" VALUE ${${_VAR_NAME}} RES _res)
if(NOT "${_res}" STREQUAL "")
list(GET _res 0 _global_var_name)
list(GET _res 1 _global_var_value)
list(APPEND ${_MY_PARAMS_PREFIX}_${_global_var_name} ${_global_var_value})
if (NOT "${_MY_PARAMS_PREFIX}_${_global_var_name}" IN_LIST _RES)
list(APPEND _RES "${_MY_PARAMS_PREFIX}_${_global_var_name}")
endif()
endif()
endif()
endforeach()
endif()
if (_MY_PARAMS_TO_LIST)
foreach(_list_name IN LISTS _RES)
set(${_list_name} ${${_list_name}} PARENT_SCOPE)
endforeach()
set(${_MY_PARAMS_RES} ${_RES} PARENT_SCOPE)
else()
foreach(_list_name IN LISTS _RES)
string(SUBSTRING "${_list_name}" ${_PREFIX_LENGT} -1 _short_name)
string(REPLACE ";" " " _list_value "${${_list_name}}")
string(APPEND _STRING_RES "set(${_short_name} \"\${${_short_name}} ${_list_value}\" CACHE STRING \"\" FORCE)\n")
endforeach()
set(${_MY_PARAMS_RES} ${_STRING_RES} PARENT_SCOPE)
endif()
endfunction()
#[===[.rst:
.. cmake:command:: translate_value_as_property
.. code-block:: cmake
translate_value_as_property(VALUE "/foo/bar/include;/foo/bar/include1"
PROPERTY INTERFACE_INCLUDE_DIRECTORIES
RES _cmake_fragment)
Translate a value as the specified property would be. Can be used to translate variables not saved
with :command:`save_interface_target_properties`
Inputs:
``VALUE``
Value to be converted.
``PROPERTY``
The interface property to set conversion type.
Outputs:
``RES``
Name of variable to write result string to.
#]===]
function(translate_value_as_property)
set(_OPTIONS_ARGS)
set(_ONE_VALUE_ARGS VALUE PROPERTY RES)
set(_MULTI_VALUE_ARGS)
cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
if (NOT DEFINED _MY_PARAMS_VALUE)
message(FATAL_ERROR "Mandatory parameter VALUE is not defined.")
endif()
if (NOT DEFINED _MY_PARAMS_RES)
message(FATAL_ERROR "Mandatory parameter RES is not defined.")
endif()
if (NOT DEFINED _MY_PARAMS_PROPERTY)
message(FATAL_ERROR "Mandatory parameter PROPERTY is not defined.")
endif()
set(A_${_MY_PARAMS_PROPERTY} ${_MY_PARAMS_VALUE})
translate_interface_target_properties(PREFIX A RES _cmake_fragment
VARS A_${_MY_PARAMS_PROPERTY})
set(${_MY_PARAMS_RES} ${_cmake_fragment} PARENT_SCOPE)
endfunction()
#[===[.rst:
.. cmake:command:: unset_saved_properties
.. code-block:: cmake
unset_saved_properties("LIBC")
Unset saved properties. For cleaning up the variable name space.
Inputs:
``PREFIX``
Prefix to use for output variable names.
#]===]
macro(unset_saved_properties PREFIX)
foreach(_prc_prop IN LISTS PROPERTYCOPY_DEFAULT_PROPERTY_LIST )
set(_PRC_VAR_NAME ${PREFIX}_${_prc_prop})
unset(${_PRC_VAR_NAME})
endforeach()
unset(_PRC_VAR_NAME)
unset(_prc_prop)
endmacro()
#[===[.rst:
.. cmake:command:: unset_translated_lists
.. code-block:: cmake
unset_translated_lists(_lists)
Unset saved properties. Can be used for cleaning up the variable name space.
Inputs:
``LISTVAR``
Prefix to use for output variable names.
#]===]
macro(unset_translated_lists LISTVAR)
foreach(_list_name IN LISTS ${LISTVAR} )
unset(${_list_name})
endforeach()
unset(_list_name)
endmacro()
#[===[.rst:
.. cmake:command:: print_saved_properties
.. code-block:: cmake
print_saved_properties(PREFIX LIBC)
Print the value of all target interface properties saved with the specified prefix.
Can be used for debugging.
Inputs:
``PREFIX``
Prefix to use for output variable names.
#]===]
function(print_saved_properties)
set(_OPTIONS_ARGS)
set(_ONE_VALUE_ARGS PREFIX)
set(_MULTI_VALUE_ARGS )
cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
if (NOT DEFINED _MY_PARAMS_PREFIX)
message(FATAL_ERROR "Mandatory parameter PREFIX is not defined.")
endif()
string(LENGTH "${_MY_PARAMS_PREFIX}_" _PREFIX_LENGT)
message(STATUS "Properties saved with prefix \"${_MY_PARAMS_PREFIX}\"")
foreach(_prop IN LISTS PROPERTYCOPY_DEFAULT_PROPERTY_LIST )
set(_VAR_NAME "${_MY_PARAMS_PREFIX}_${_prop}")
string(SUBSTRING "${_VAR_NAME}" ${_PREFIX_LENGT} -1 _prop)
if (NOT DEFINED ${_VAR_NAME})
set(_value "<Not set.>")
else()
set(_value ${${_VAR_NAME}})
endif()
message(STATUS " ${_prop}:${_value}")
endforeach()
endfunction()
#[===[.rst:
.. cmake:command:: print_translated_lists
.. code-block:: cmake
print_translated_lists(PREFIX LIBC)
Print the value of all lists translated from interface properties by calling
translate_interface_target_properties() with TO_LISTS set.
Can be used for debugging.
Inputs:
``LIST``
Name of list of lists set by :command:`translate_interface_target_properties`
#]===]
function(print_translated_lists)
set(_OPTIONS_ARGS)
set(_ONE_VALUE_ARGS LIST)
set(_MULTI_VALUE_ARGS )
cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
if (NOT DEFINED _MY_PARAMS_LIST)
message(FATAL_ERROR "Mandatory parameter LIST is not defined.")
endif()
message(STATUS "Translated lists from \"${_MY_PARAMS_LIST}\"")
foreach(_list IN LISTS ${_MY_PARAMS_LIST})
message(STATUS " ${_list}=${${_list}}")
endforeach()
endfunction()
# These properties are cmake specific and can not be translated.
# INTERFACE_COMPILE_FEATURES, INTERFACE_LINK_DEPENDS, INTERFACE_SOURCES
# LINK_INTERFACE_LIBRARIES
# Translate target property to command line switch.
function(_prc_translate)
set(_OPTIONS_ARGS)
set(_ONE_VALUE_ARGS PROP RES)
set(_MULTI_VALUE_ARGS VALUE)
cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
if ("${_MY_PARAMS_VALUE}" STREQUAL "")
set(_res "")
else()
if (_MY_PARAMS_PROP STREQUAL INTERFACE_INCLUDE_DIRECTORIES)
_prc_translate_include_list("${_MY_PARAMS_VALUE}" _res)
elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
_prc_translate_system_include_list("${_MY_PARAMS_VALUE}" _res)
elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_COMPILE_DEFINITIONS)
_prc_translate_macro_list("${_MY_PARAMS_VALUE}" _res)
elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_COMPILE_OPTIONS)
_prc_translate_compile_option_list("${_MY_PARAMS_VALUE}" _res)
elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_LINK_OPTIONS)
_prc_translate_link_option_list("${_MY_PARAMS_VALUE}" _res)
elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_LINK_DIRECTORIES)
_prc_translate_link_directory_list("${_MY_PARAMS_VALUE}" _res)
elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_LINK_LIBRARIES)
_prc_translate_link_library_list("${_MY_PARAMS_VALUE}" _res)
else()
message(FATAL_ERROR "Can not translate target property \"${_MY_PARAMS_PROP}\" to global setting.")
endif()
endif()
set(${_MY_PARAMS_RES} "${_res}" PARENT_SCOPE)
endfunction()
# Translate list of include directories to compiler flags.
function(_prc_translate_include_list VALUE OUT)
if(NOT "${VALUE}" STREQUAL "")
string(REPLACE ";" " ${CMAKE_INCLUDE_FLAG_C} " _tmp "${VALUE}")
else()
set(_tmp "")
endif()
set(${OUT} "CMAKE_C_FLAGS_INIT;${CMAKE_INCLUDE_FLAG_C} ${_tmp}" PARENT_SCOPE)
endfunction()
# Translate list of system include directories to compiler flags.
function(_prc_translate_system_include_list VALUE OUT)
if(NOT "${VALUE}" STREQUAL "")
string(REPLACE ";" " ${CMAKE_INCLUDE_SYSTEM_FLAG_C} " _tmp "${VALUE}")
else()
set(_tmp "")
endif()
set(${OUT} "CMAKE_C_FLAGS_INIT;${CMAKE_INCLUDE_SYSTEM_FLAG_C} ${_tmp}" PARENT_SCOPE)
endfunction()
# Translate list of C macro definitions to compiler flags.
function(_prc_translate_macro_list VALUE OUT)
if(NOT "${VALUE}" STREQUAL "")
string(REPLACE ";" " -D " _tmp "${VALUE}")
else()
set(_tmp "")
endif()
set(${OUT} "CMAKE_C_FLAGS_INIT;-D ${_tmp}" PARENT_SCOPE)
endfunction()
# Translate list of compilation options to compiler flags.
function(_prc_translate_compile_option_list VALUE OUT)
if(NOT "${VALUE}" STREQUAL "")
string(REPLACE ";" " " _tmp "${VALUE}")
else()
set(_tmp "")
endif()
set(${OUT} "CMAKE_C_FLAGS_INIT;${_tmp}" PARENT_SCOPE)
endfunction()
# Translate list of link options to linker flags.
function(_prc_translate_link_option_list VALUE OUT)
if(NOT "${VALUE}" STREQUAL "")
string(REPLACE ";" " " _tmp "${VALUE}")
else()
set(_tmp "")
endif()
set(${OUT} "CMAKE_EXE_LINKER_FLAGS_INIT;${_tmp}" PARENT_SCOPE)
endfunction()
# Translate list of linker search paths to linker flags.
function(_prc_translate_link_directory_list VALUE OUT)
if(NOT "${VALUE}" STREQUAL "")
string(REPLACE ";" " ${CMAKE_LIBRARY_PATH_FLAG} " _tmp "${VALUE}")
else()
set(_tmp "")
endif()
set(${OUT} "CMAKE_EXE_LINKER_FLAGS_INIT;${CMAKE_LIBRARY_PATH_FLAG} ${_tmp}" PARENT_SCOPE)
endfunction()
# Translate list of libraries to linker flags.
function(_prc_translate_link_library_list VALUE OUT)
if(NOT "${VALUE}" STREQUAL "")
string(REPLACE ";" " ${CMAKE_LINK_LIBRARY_FLAG} " _tmp "${VALUE}")
else()
set(_tmp "")
endif()
set(${OUT} "CMAKE_EXE_LINKER_FLAGS_INIT;${CMAKE_LINK_LIBRARY_FLAG} ${_tmp}" PARENT_SCOPE)
endfunction()