1#------------------------------------------------------------------------------- 2# Copyright (c) 2022, Arm Limited and Contributors. All rights reserved. 3# 4# SPDX-License-Identifier: BSD-3-Clause 5# 6#------------------------------------------------------------------------------- 7 8#[===[.rst: 9PropertyCopy.cmake 10------------------ 11 12This module allows saving interface properties of a target to a set of variables and to translate 13the variables to cmake script fragment of compiler and linker flag lists. 14The main purpose is to allow transferring settings to sub-projects which need strong separation 15(i.e. ExternalProject is used) or use a non CMake build system. 16 17For CMake projects the data-flow is to save the settings to variables, translate these to cmake code 18fragment, and then inject these to the sub-projects using a generated initial cache file. 19Alternatively translate the saved values to list variables `<PREFIX>_CMAKE_C_FLAGS_INIT` and 20`<PREFIX>_CMAKE_EXE_LINKER_FLAGS_INIT`, and pass these to the sub-project using the -D command-line 21parameter. 22 23For non CMake projects the data-flow is to save the properties to variables and the translate to 24compiler and linker argument lists. Then use the generated `<PREFIX>_CMAKE_C_FLAGS_INIT` and 25`<PREFIX>_CMAKE_EXE_LINKER_FLAGS_INIT` variables in a build system specific way (e.g. setting 26`CFLAGS` and `LDFLAGS` environment variables) to configure the sub-project. 27 28#]===] 29 30#[===[.rst: 31.. cmake:variable:: PROPERTYCOPY_DEFAULT_PROPERTY_LIST 32 33Default list of properties to save and restore. It is used by functions in this file. Some of these 34allow using a custom list instead by passing appropriate parameters. 35#]===] 36set(PROPERTYCOPY_DEFAULT_PROPERTY_LIST INTERFACE_COMPILE_DEFINITIONS 37 INTERFACE_COMPILE_OPTIONS INTERFACE_INCLUDE_DIRECTORIES 38 INTERFACE_LINK_DIRECTORIES INTERFACE_LINK_LIBRARIES INTERFACE_LINK_OPTIONS 39 INTERFACE_POSITION_INDEPENDENT_CODE 40 INTERFACE_SYSTEM_INCLUDE_DIRECTORIES) 41 42#[===[.rst: 43.. cmake:command:: save_interface_target_properties 44 45 .. code-block:: cmake 46 47 save_interface_target_properties(TGT stdlib:c PREFIX LIBC) 48 save_interface_target_properties(TGT stdlib:c PREFIX LIBC 49 PROPERTIES INTERFACE_LINK_DIRECTORIES INTERFACE_LINK_LIBRARIES) 50 51 Save interface properties of a target to a set of variables. Variables are named after the 52 properties prefixed with the parameter <PREFIX>_. (i.e. FOO_INTERFACE_COMPILE_OPTIONS if the 53 prefix was "FOO"). 54 The list of properties to be saved can be set using the PROPERTIES parameter. If this is not 55 set, the :variable:`PROPERTYCOPY_DEFAULT_PROPERTY_LIST` is used. 56 57 Inputs: 58 ``TGT`` 59 Target to copy properties from. 60 ``PROPERTIES`` 61 Optional. List of properties to save. If not set, the default list is used. See: 62 :variable:`PROPERTYCOPY_DEFAULT_PROPERTY_LIST`. 63 ``PREFIX`` 64 Prefix to use for output variable names. 65 Outputs: 66 A set of variables (see description). 67#]===] 68function(save_interface_target_properties) 69 set(_OPTIONS_ARGS) 70 set(_ONE_VALUE_ARGS TGT PREFIX) 71 set(_MULTI_VALUE_ARGS PROPERTIES) 72 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) 73 74 if (NOT DEFINED _MY_PARAMS_PREFIX) 75 message(FATAL_ERROR "Mandatory parameter PREFIX is not defined.") 76 endif() 77 if (NOT DEFINED _MY_PARAMS_TGT) 78 message(FATAL_ERROR "Mandatory parameter TGT is not defined.") 79 endif() 80 if(NOT TARGET ${_MY_PARAMS_TGT}) 81 message(FATAL_ERROR "Target \"${_MY_PARAMS_TGT}\" does not exist.") 82 endif() 83 if (NOT DEFINED _MY_PARAMS_PROPERTIES) 84 set(_MY_PARAMS_PROPERTIES ${PROPERTYCOPY_DEFAULT_PROPERTY_LIST}) 85 endif() 86 87 foreach(_prop IN LISTS _MY_PARAMS_PROPERTIES ) 88 get_property(_set TARGET ${_MY_PARAMS_TGT} PROPERTY ${_prop} SET) 89 if (_set) 90 get_property(${_MY_PARAMS_PREFIX}_${_prop} TARGET ${_MY_PARAMS_TGT} PROPERTY ${_prop}) 91 set(${_MY_PARAMS_PREFIX}_${_prop} ${${_MY_PARAMS_PREFIX}_${_prop}} PARENT_SCOPE) 92 endif() 93 endforeach() 94endfunction() 95 96#[===[.rst: 97.. cmake:command:: translate_interface_target_properties 98 99 .. code-block:: cmake 100 101 # To translate default set of properties saved to variables with ``LIBC_`` prefix 102 # using :command:`save_interface_target_properties`. Result string returned to 103 # ``_cmake_fragment`` 104 translate_interface_target_properties(PREFIX LIBC RES _cmake_fragment) 105 106 # To translate default set of properties saved to variables with ``LIBC_`` prefix 107 # using :command:`save_interface_target_properties`. Results saved to lists prefixed 108 # with ``LIBC_``. List of generated lists is returned in ``_lists`` 109 translate_interface_target_properties(PREFIX LIBC RES _lists) 110 111 Construct a string of cmake script fragment setting global cmake variables configuring 112 build properties to match saved target interface settings. The script fragment is returned 113 in ``RES`` 114 Intended usage is to help transferring target specific settings to sub projects using 115 initial cache files. 116 Warning: quotation in property values is not handled. This can cause problems e.g. with 117 computed includes. 118 119 If ``TO_LIST`` is passed translation will be done to a lists. ``RES`` will hold a list of 120 list names where the settings are saved. 121 This mode allows further processing on the lists, e.g. to be converted to ``CFLAGS`` or 122 ``LDFLAGS`` environment variables. 123 124 Works in tandem with :command:`save_interface_target_properties`. 125 126 Inputs: 127 ``PREFIX`` 128 Target to set properties on. 129 ``VARS`` 130 Name of variables to copy from. 131 ``TO_LIST`` 132 Translate to lists instead of cmake script fragment. 133 Outputs 134 ``RES`` 135 Name of variable to store the results to. 136#]===] 137function(translate_interface_target_properties) 138 set(_OPTIONS_ARGS TO_LIST) 139 set(_ONE_VALUE_ARGS PREFIX RES) 140 set(_MULTI_VALUE_ARGS VARS) 141 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) 142 143 if (NOT DEFINED _MY_PARAMS_PREFIX) 144 message(FATAL_ERROR "Mandatory parameter PREFIX is not defined.") 145 endif() 146 string(LENGTH "${_MY_PARAMS_PREFIX}_" _PREFIX_LENGT) 147 148 if (NOT DEFINED _MY_PARAMS_RES) 149 message(FATAL_ERROR "Mandatory parameter RES is not defined.") 150 endif() 151 152 if (DEFINED _MY_PARAMS_VARS) 153 foreach(_VAR_NAME IN LISTS _MY_PARAMS_VARS) 154 if (NOT DEFINED ${_VAR_NAME}) 155 message(FATAL_ERROR "Attempt to translate undefined variable \"${_VAR_NAME}\"") 156 endif() 157 158 string(SUBSTRING "${_VAR_NAME}" ${_PREFIX_LENGT} -1 _prop) 159 _prc_translate(PROP "${_prop}" VALUE ${${_VAR_NAME}} RES _res) 160 161 if(NOT "${_res}" STREQUAL "") 162 list(GET _res 0 _global_var_name) 163 list(GET _res 1 _global_var_value) 164 list(APPEND ${_MY_PARAMS_PREFIX}_${_global_var_name} ${_global_var_value}) 165 if (NOT "${_MY_PARAMS_PREFIX}_${_global_var_name}" IN_LIST _RES) 166 list(APPEND _RES "${_MY_PARAMS_PREFIX}_${_global_var_name}") 167 endif() 168 endif() 169 endforeach() 170 else() 171 foreach(_prop IN LISTS PROPERTYCOPY_DEFAULT_PROPERTY_LIST) 172 set(_VAR_NAME "${_MY_PARAMS_PREFIX}_${_prop}") 173 # Is the variable holding the value of the property available? 174 if (DEFINED ${_VAR_NAME}) 175 _prc_translate(PROP "${_prop}" VALUE ${${_VAR_NAME}} RES _res) 176 if(NOT "${_res}" STREQUAL "") 177 list(GET _res 0 _global_var_name) 178 list(GET _res 1 _global_var_value) 179 list(APPEND ${_MY_PARAMS_PREFIX}_${_global_var_name} ${_global_var_value}) 180 if (NOT "${_MY_PARAMS_PREFIX}_${_global_var_name}" IN_LIST _RES) 181 list(APPEND _RES "${_MY_PARAMS_PREFIX}_${_global_var_name}") 182 endif() 183 endif() 184 endif() 185 endforeach() 186 endif() 187 188 if (_MY_PARAMS_TO_LIST) 189 foreach(_list_name IN LISTS _RES) 190 set(${_list_name} ${${_list_name}} PARENT_SCOPE) 191 endforeach() 192 set(${_MY_PARAMS_RES} ${_RES} PARENT_SCOPE) 193 else() 194 foreach(_list_name IN LISTS _RES) 195 string(SUBSTRING "${_list_name}" ${_PREFIX_LENGT} -1 _short_name) 196 string(REPLACE ";" " " _list_value "${${_list_name}}") 197 string(APPEND _STRING_RES "set(${_short_name} \"\${${_short_name}} ${_list_value}\" CACHE STRING \"\" FORCE)\n") 198 endforeach() 199 set(${_MY_PARAMS_RES} ${_STRING_RES} PARENT_SCOPE) 200 endif() 201endfunction() 202 203#[===[.rst: 204.. cmake:command:: translate_value_as_property 205 206 .. code-block:: cmake 207 208 translate_value_as_property(VALUE "/foo/bar/include;/foo/bar/include1" 209 PROPERTY INTERFACE_INCLUDE_DIRECTORIES 210 RES _cmake_fragment) 211 212 Translate a value as the specified property would be. Can be used to translate variables not saved 213 with :command:`save_interface_target_properties` 214 215 Inputs: 216 ``VALUE`` 217 Value to be converted. 218 ``PROPERTY`` 219 The interface property to set conversion type. 220 Outputs: 221 ``RES`` 222 Name of variable to write result string to. 223#]===] 224function(translate_value_as_property) 225 set(_OPTIONS_ARGS) 226 set(_ONE_VALUE_ARGS VALUE PROPERTY RES) 227 set(_MULTI_VALUE_ARGS) 228 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) 229 230 if (NOT DEFINED _MY_PARAMS_VALUE) 231 message(FATAL_ERROR "Mandatory parameter VALUE is not defined.") 232 endif() 233 if (NOT DEFINED _MY_PARAMS_RES) 234 message(FATAL_ERROR "Mandatory parameter RES is not defined.") 235 endif() 236 if (NOT DEFINED _MY_PARAMS_PROPERTY) 237 message(FATAL_ERROR "Mandatory parameter PROPERTY is not defined.") 238 endif() 239 240 set(A_${_MY_PARAMS_PROPERTY} ${_MY_PARAMS_VALUE}) 241 translate_interface_target_properties(PREFIX A RES _cmake_fragment 242 VARS A_${_MY_PARAMS_PROPERTY}) 243 set(${_MY_PARAMS_RES} ${_cmake_fragment} PARENT_SCOPE) 244endfunction() 245 246#[===[.rst: 247.. cmake:command:: unset_saved_properties 248 249 .. code-block:: cmake 250 251 unset_saved_properties("LIBC") 252 253 Unset saved properties. For cleaning up the variable name space. 254 255 Inputs: 256 ``PREFIX`` 257 Prefix to use for output variable names. 258#]===] 259macro(unset_saved_properties PREFIX) 260 foreach(_prc_prop IN LISTS PROPERTYCOPY_DEFAULT_PROPERTY_LIST ) 261 set(_PRC_VAR_NAME ${PREFIX}_${_prc_prop}) 262 unset(${_PRC_VAR_NAME}) 263 endforeach() 264 unset(_PRC_VAR_NAME) 265 unset(_prc_prop) 266endmacro() 267 268#[===[.rst: 269.. cmake:command:: unset_translated_lists 270 271 .. code-block:: cmake 272 273 unset_translated_lists(_lists) 274 275 Unset saved properties. Can be used for cleaning up the variable name space. 276 277 Inputs: 278 ``LISTVAR`` 279 Prefix to use for output variable names. 280#]===] 281macro(unset_translated_lists LISTVAR) 282 foreach(_list_name IN LISTS ${LISTVAR} ) 283 unset(${_list_name}) 284 endforeach() 285 unset(_list_name) 286endmacro() 287#[===[.rst: 288.. cmake:command:: print_saved_properties 289 290 .. code-block:: cmake 291 292 print_saved_properties(PREFIX LIBC) 293 294 Print the value of all target interface properties saved with the specified prefix. 295 Can be used for debugging. 296 297 Inputs: 298 ``PREFIX`` 299 Prefix to use for output variable names. 300#]===] 301function(print_saved_properties) 302 set(_OPTIONS_ARGS) 303 set(_ONE_VALUE_ARGS PREFIX) 304 set(_MULTI_VALUE_ARGS ) 305 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) 306 307 if (NOT DEFINED _MY_PARAMS_PREFIX) 308 message(FATAL_ERROR "Mandatory parameter PREFIX is not defined.") 309 endif() 310 string(LENGTH "${_MY_PARAMS_PREFIX}_" _PREFIX_LENGT) 311 312 message(STATUS "Properties saved with prefix \"${_MY_PARAMS_PREFIX}\"") 313 foreach(_prop IN LISTS PROPERTYCOPY_DEFAULT_PROPERTY_LIST ) 314 set(_VAR_NAME "${_MY_PARAMS_PREFIX}_${_prop}") 315 string(SUBSTRING "${_VAR_NAME}" ${_PREFIX_LENGT} -1 _prop) 316 if (NOT DEFINED ${_VAR_NAME}) 317 set(_value "<Not set.>") 318 else() 319 set(_value ${${_VAR_NAME}}) 320 endif() 321 message(STATUS " ${_prop}:${_value}") 322 endforeach() 323endfunction() 324 325#[===[.rst: 326.. cmake:command:: print_translated_lists 327 328 .. code-block:: cmake 329 330 print_translated_lists(PREFIX LIBC) 331 332 Print the value of all lists translated from interface properties by calling 333 translate_interface_target_properties() with TO_LISTS set. 334 Can be used for debugging. 335 336 Inputs: 337 ``LIST`` 338 Name of list of lists set by :command:`translate_interface_target_properties` 339#]===] 340function(print_translated_lists) 341 set(_OPTIONS_ARGS) 342 set(_ONE_VALUE_ARGS LIST) 343 set(_MULTI_VALUE_ARGS ) 344 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) 345 346 if (NOT DEFINED _MY_PARAMS_LIST) 347 message(FATAL_ERROR "Mandatory parameter LIST is not defined.") 348 endif() 349 350 message(STATUS "Translated lists from \"${_MY_PARAMS_LIST}\"") 351 foreach(_list IN LISTS ${_MY_PARAMS_LIST}) 352 message(STATUS " ${_list}=${${_list}}") 353 endforeach() 354endfunction() 355 356# These properties are cmake specific and can not be translated. 357# INTERFACE_COMPILE_FEATURES, INTERFACE_LINK_DEPENDS, INTERFACE_SOURCES 358# LINK_INTERFACE_LIBRARIES 359 360# Translate target property to command line switch. 361function(_prc_translate) 362 set(_OPTIONS_ARGS) 363 set(_ONE_VALUE_ARGS PROP RES) 364 set(_MULTI_VALUE_ARGS VALUE) 365 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) 366 367 if ("${_MY_PARAMS_VALUE}" STREQUAL "") 368 set(_res "") 369 else() 370 if (_MY_PARAMS_PROP STREQUAL INTERFACE_INCLUDE_DIRECTORIES) 371 _prc_translate_include_list("${_MY_PARAMS_VALUE}" _res) 372 elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_SYSTEM_INCLUDE_DIRECTORIES) 373 _prc_translate_system_include_list("${_MY_PARAMS_VALUE}" _res) 374 elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_COMPILE_DEFINITIONS) 375 _prc_translate_macro_list("${_MY_PARAMS_VALUE}" _res) 376 elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_COMPILE_OPTIONS) 377 _prc_translate_compile_option_list("${_MY_PARAMS_VALUE}" _res) 378 elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_LINK_OPTIONS) 379 _prc_translate_link_option_list("${_MY_PARAMS_VALUE}" _res) 380 elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_LINK_DIRECTORIES) 381 _prc_translate_link_directory_list("${_MY_PARAMS_VALUE}" _res) 382 elseif(_MY_PARAMS_PROP STREQUAL INTERFACE_LINK_LIBRARIES) 383 _prc_translate_link_library_list("${_MY_PARAMS_VALUE}" _res) 384 else() 385 message(FATAL_ERROR "Can not translate target property \"${_MY_PARAMS_PROP}\" to global setting.") 386 endif() 387 endif() 388 set(${_MY_PARAMS_RES} "${_res}" PARENT_SCOPE) 389endfunction() 390 391# Translate list of include directories to compiler flags. 392function(_prc_translate_include_list VALUE OUT) 393 if(NOT "${VALUE}" STREQUAL "") 394 string(REPLACE ";" " ${CMAKE_INCLUDE_FLAG_C} " _tmp "${VALUE}") 395 else() 396 set(_tmp "") 397 endif() 398 set(${OUT} "CMAKE_C_FLAGS_INIT;${CMAKE_INCLUDE_FLAG_C} ${_tmp}" PARENT_SCOPE) 399endfunction() 400 401# Translate list of system include directories to compiler flags. 402function(_prc_translate_system_include_list VALUE OUT) 403 if(NOT "${VALUE}" STREQUAL "") 404 string(REPLACE ";" " ${CMAKE_INCLUDE_SYSTEM_FLAG_C} " _tmp "${VALUE}") 405 else() 406 set(_tmp "") 407 endif() 408 set(${OUT} "CMAKE_C_FLAGS_INIT;${CMAKE_INCLUDE_SYSTEM_FLAG_C} ${_tmp}" PARENT_SCOPE) 409endfunction() 410 411# Translate list of C macro definitions to compiler flags. 412function(_prc_translate_macro_list VALUE OUT) 413 if(NOT "${VALUE}" STREQUAL "") 414 string(REPLACE ";" " -D " _tmp "${VALUE}") 415 else() 416 set(_tmp "") 417 endif() 418 set(${OUT} "CMAKE_C_FLAGS_INIT;-D ${_tmp}" PARENT_SCOPE) 419endfunction() 420 421# Translate list of compilation options to compiler flags. 422function(_prc_translate_compile_option_list VALUE OUT) 423 if(NOT "${VALUE}" STREQUAL "") 424 string(REPLACE ";" " " _tmp "${VALUE}") 425 else() 426 set(_tmp "") 427 endif() 428 set(${OUT} "CMAKE_C_FLAGS_INIT;${_tmp}" PARENT_SCOPE) 429endfunction() 430 431# Translate list of link options to linker flags. 432function(_prc_translate_link_option_list VALUE OUT) 433 if(NOT "${VALUE}" STREQUAL "") 434 string(REPLACE ";" " " _tmp "${VALUE}") 435 else() 436 set(_tmp "") 437 endif() 438 set(${OUT} "CMAKE_EXE_LINKER_FLAGS_INIT;${_tmp}" PARENT_SCOPE) 439endfunction() 440 441# Translate list of linker search paths to linker flags. 442function(_prc_translate_link_directory_list VALUE OUT) 443 if(NOT "${VALUE}" STREQUAL "") 444 string(REPLACE ";" " ${CMAKE_LIBRARY_PATH_FLAG} " _tmp "${VALUE}") 445 else() 446 set(_tmp "") 447 endif() 448 set(${OUT} "CMAKE_EXE_LINKER_FLAGS_INIT;${CMAKE_LIBRARY_PATH_FLAG} ${_tmp}" PARENT_SCOPE) 449endfunction() 450 451# Translate list of libraries to linker flags. 452function(_prc_translate_link_library_list VALUE OUT) 453 if(NOT "${VALUE}" STREQUAL "") 454 string(REPLACE ";" " ${CMAKE_LINK_LIBRARY_FLAG} " _tmp "${VALUE}") 455 else() 456 set(_tmp "") 457 endif() 458 set(${OUT} "CMAKE_EXE_LINKER_FLAGS_INIT;${CMAKE_LINK_LIBRARY_FLAG} ${_tmp}" PARENT_SCOPE) 459endfunction() 460