| #------------------------------------------------------------------------------- |
| # Copyright (c) 2019-2020, Arm Limited and Contributors. All rights reserved. |
| # |
| # SPDX-License-Identifier: BSD-3-Clause |
| # |
| #------------------------------------------------------------------------------- |
| |
| #[===[.rst: |
| Map utility file |
| ---------------- |
| Map is a key-value pair storage (also known as associative array) |
| implementation. It can hold multiple key-value pairs, the keys have to be unique |
| in a map. Trying to add a new pair whose key already exists in the map will |
| cause an error. Keys and values have a one-to-one relation, i.e. a key has |
| exactly one value associated to it, which cannot be empty. If an empty/null |
| value is needed, use a single space character instead. The value associated to a |
| key cannot be overwritten, however it is possible to remove a key-value pair |
| from the map then add the same key again with a new value. |
| |
| The main purpose of this utility is to store configuration data for the project, |
| i.e. build options, compiler flags, etc. |
| |
| Internally the utility uses global properties to store the data. The global |
| property ``MAPS.<name of map>`` is created as an indicator that the map is |
| defined, while global properties ``MAPS.<name of map>.KEYS`` and ``MAPS.<name of |
| map>.VALS`` hold the corresponding data as lists. A value for a key is |
| identified by having the same index in the VALS lists, as the key has in the |
| KEYS list. |
| |
| .. todo:: Investigate alternatives to global properties (name collision possible). |
| |
| #]===] |
| |
| include_guard(DIRECTORY) |
| include(Common/Utils) |
| |
| #[===[.rst: |
| .. cmake:command:: map_new |
| |
| .. code-block:: cmake |
| |
| map_new(NAME foo) |
| |
| Create a new named set of key-value pairs. |
| |
| Inputs: |
| |
| ``NAME`` |
| Name of the new map, use |C identifier like string|. The name must be unique |
| within the global namespace, otherwise an error is generated. |
| |
| #]===] |
| function(map_new) |
| set(_OPTIONS_ARGS) |
| set(_ONE_VALUE_ARGS NAME) |
| set(_MULTI_VALUE_ARGS) |
| cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) |
| |
| check_args(map_new NAME) |
| |
| get_property(is_defined GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME} DEFINED) |
| if(is_defined) |
| message(FATAL_ERROR "map_new(): '${_MY_PARAMS_NAME}' is already defined.") |
| endif() |
| |
| set(_null " ") |
| define_property(GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME} BRIEF_DOCS ${_null} FULL_DOCS ${_null}) |
| define_property(GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.KEYS BRIEF_DOCS ${_null} FULL_DOCS ${_null}) |
| define_property(GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.VALS BRIEF_DOCS ${_null} FULL_DOCS ${_null}) |
| endfunction() |
| |
| #[===[.rst: |
| .. cmake:command:: map_add |
| |
| .. code-block:: cmake |
| |
| map_add(NAME foo KEY one VAL 1) |
| |
| Add new key-value pair to a map. |
| |
| ``KEY`` and ``VAL`` are stored as CMake properties (string list) which permit |
| a broad, but not clearly defined set of characters. Semicolons must be escaped |
| as ``\;`` in all cases. To minimize the amount of possible bugs, both ``KEY`` |
| and ``VAL`` should use |C identifier like string|. Exceptions are e.g. when a |
| path is stored as the value. |
| |
| Inputs: |
| |
| ``NAME`` |
| Name of the map. Trying to add to a non-existing map generates an error. |
| |
| ``KEY`` |
| New key to add. Key must be unique, trying to add a new pair whose key |
| already exists in the map will cause an error. |
| |
| ``VAL`` |
| Value for new key. |
| |
| #]===] |
| function(map_add) |
| set(_OPTIONS_ARGS) |
| set(_ONE_VALUE_ARGS NAME KEY VAL) |
| set(_MULTI_VALUE_ARGS) |
| cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) |
| |
| check_args(map_add NAME KEY VAL) |
| |
| get_property(_is_defined GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME} DEFINED) |
| if(NOT _is_defined) |
| message(FATAL_ERROR "map_add(): '${_MY_PARAMS_NAME}' is not defined.") |
| endif() |
| |
| get_property(_keys GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.KEYS) |
| get_property(_vals GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.VALS) |
| |
| if(${_MY_PARAMS_KEY} IN_LIST _keys) |
| message(FATAL_ERROR "map_add(): key '${_MY_PARAMS_KEY}' is already defined.") |
| else() |
| list(APPEND _keys ${_MY_PARAMS_KEY}) |
| list(APPEND _vals ${_MY_PARAMS_VAL}) |
| |
| set_property(GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.KEYS ${_keys}) |
| set_property(GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.VALS ${_vals}) |
| endif() |
| endfunction() |
| |
| #[===[.rst: |
| .. cmake:command:: map_remove |
| |
| .. code-block:: cmake |
| |
| map_remove(NAME foo KEY one) |
| |
| Remove existing key-value pair from a map. |
| |
| Inputs: |
| |
| ``NAME`` |
| Name of the map. Trying to remove from a non-existing map generates an |
| error. |
| |
| ``KEY`` |
| Key to remove. Trying to remove a non-existing key generates an error. |
| |
| #]===] |
| function(map_remove) |
| set(_OPTIONS_ARGS) |
| set(_ONE_VALUE_ARGS NAME KEY) |
| set(_MULTI_VALUE_ARGS) |
| cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) |
| |
| check_args(map_remove NAME KEY) |
| |
| get_property(_is_defined GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME} DEFINED) |
| if(NOT _is_defined) |
| message(FATAL_ERROR "map_remove(): '${_MY_PARAMS_NAME}' is not defined.") |
| endif() |
| |
| get_property(_keys GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.KEYS) |
| get_property(_vals GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.VALS) |
| |
| list(FIND _keys ${_MY_PARAMS_KEY} _index) |
| if(_index EQUAL -1) |
| message(FATAL_ERROR "map_remove(): key '${_MY_PARAMS_KEY}' does not exist.") |
| endif() |
| |
| list(REMOVE_AT _keys ${_index}) |
| list(REMOVE_AT _vals ${_index}) |
| |
| set_property(GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.KEYS ${_keys}) |
| set_property(GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.VALS ${_vals}) |
| endfunction() |
| |
| #[===[.rst: |
| .. cmake:command:: map_read |
| |
| .. code-block:: cmake |
| |
| map_read(NAME foo KEYS _keys VALS _vals) |
| |
| Read the keys and the values of the map into two separate lists in the parent |
| scope. |
| |
| Inputs: |
| |
| ``NAME`` |
| Name of the map. Trying to read a non-existing map generates an error. |
| |
| Outputs: |
| |
| ``KEYS`` |
| Read the keys list of the map into this variable of the parent scope. |
| |
| ``VALS`` |
| Read the values list of the map into this variable of the parent scope. |
| |
| #]===] |
| function(map_read) |
| set(_OPTIONS_ARGS) |
| set(_ONE_VALUE_ARGS NAME KEYS VALS) |
| set(_MULTI_VALUE_ARGS) |
| cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) |
| |
| check_args(map_read NAME KEYS VALS) |
| |
| get_property(_is_defined GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME} DEFINED) |
| if(NOT _is_defined) |
| message(FATAL_ERROR "map_read(): '${_MY_PARAMS_NAME}' is not defined.") |
| endif() |
| |
| get_property(_keys GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.KEYS) |
| get_property(_vals GLOBAL PROPERTY MAPS.${_MY_PARAMS_NAME}.VALS) |
| |
| set(${_MY_PARAMS_KEYS} ${_keys} PARENT_SCOPE) |
| set(${_MY_PARAMS_VALS} ${_vals} PARENT_SCOPE) |
| endfunction() |
| |
| #[===[.rst: |
| .. cmake:command:: map_to_list |
| |
| .. code-block:: cmake |
| |
| map_to_list(KEYS ${_keys} VALS ${_vals} OUT _combined) |
| |
| Combine the keys and values list of a map (provided by |
| :cmake:command:`map_read` function) into a single list in the parent scope. |
| |
| * If a key 'FOO' has the value 'BAR', in the combined list it will be |
| 'FOO=BAR'. |
| * If a key 'FOO' has a single space character value, in the combined list it |
| will be 'FOO'. |
| |
| Inputs: |
| |
| ``KEYS`` |
| Keys list of a map. |
| |
| ``VALS`` |
| Values list of a map. |
| |
| Outputs: |
| |
| ``OUT`` |
| Write the combined list of key-value pairs into this variable of the parent |
| scope. |
| |
| #]===] |
| function(map_to_list) |
| set(_OPTIONS_ARGS) |
| set(_ONE_VALUE_ARGS OUT) |
| set(_MULTI_VALUE_ARGS KEYS VALS) |
| cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) |
| |
| check_args(map_to_list KEYS VALS OUT) |
| |
| list(LENGTH _MY_PARAMS_KEYS _count) |
| math(EXPR _count "${_count}-1") |
| foreach(i RANGE ${_count}) |
| list(GET _MY_PARAMS_KEYS ${i} _key) |
| list(GET _MY_PARAMS_VALS ${i} _val) |
| |
| if(${_val} STREQUAL " ") |
| list(APPEND _out "${_key}") |
| else() |
| list(APPEND _out "${_key}=${_val}") |
| endif() |
| endforeach() |
| |
| set(${_MY_PARAMS_OUT} ${_out} PARENT_SCOPE) |
| endfunction() |
| |
| #[===[.rst: |
| .. cmake:command:: map_print |
| |
| .. code-block:: cmake |
| |
| map_print(NAME foo) |
| |
| Print each key-value pair in a map, for debug purposes. |
| |
| Inputs: |
| |
| ``NAME`` |
| Name of the map to print. |
| |
| #]===] |
| function(map_print) |
| set(_OPTIONS_ARGS) |
| set(_ONE_VALUE_ARGS NAME) |
| set(_MULTI_VALUE_ARGS) |
| cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) |
| |
| check_args(map_print NAME) |
| |
| map_read(NAME ${_MY_PARAMS_NAME} KEYS _keys VALS _vals) |
| |
| message("====Map '${_MY_PARAMS_NAME}'====") |
| list(LENGTH _keys _count) |
| math(EXPR _count "${_count}-1") |
| foreach(i RANGE ${_count}) |
| list(GET _keys ${i} _key) |
| list(GET _vals ${i} _val) |
| message("[${_key}]: ${_val}") |
| endforeach() |
| message("====Map '${_MY_PARAMS_NAME}' end====\n") |
| endfunction() |