blob: 754c98ce76ea708111dcdd6dae0e0948a2174d80 [file] [log] [blame]
Gyorgy Szing24f2ffd2022-03-05 00:31:25 +00001#-------------------------------------------------------------------------------
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()