Add first version of CMake framework code
This commit adds the core elements of the framework and documentation:
- Map utility: store key-value pairs.
- Group utility: store build configuration options.
- STGT API: describe the targets.
- Compiler abstraction functions for GCC.
- Other helper functions.
- New CMake system type called "Embedded", using correct output file
prefixes and extensions.
- Sphinx based documentation which includes:
- licensing information
- version numbering policy
- documentation on how-to build the documentation
In addition the following utility files are added:
- .editorconfig
- .gitignore
Change-Id: If19a171ef066775d3544fba82f1cc70a5fb0e7d7
Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Co-authored-by: Gyorgy Szing <gyorgy.szing@arm.com>
Co-authored-by: Bence Szépkúti <bence.szepkuti@arm.com>
diff --git a/Common/Group.cmake b/Common/Group.cmake
new file mode 100644
index 0000000..a76eb1d
--- /dev/null
+++ b/Common/Group.cmake
@@ -0,0 +1,292 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#[===[.rst:
+Group utility file
+------------------
+Group is an unordered collection of interrelated :cmake:module:`map`\s.
+
+The main purpose of this utility is to pack a set of coherent configuration data
+description into one unit, which can be applied on a target in one step. This
+means e.g. defines, compiler flags, include paths, etc.
+
+Internally the utility uses global properties and maps. Global property
+``GROUPS.<name of group>`` is defined to indicate that the group is defined, and
+for each element in the :cmake:variable:`GROUPS_VALID_TYPES`:
+
+ * a map is created, using ``<name of group>.<type>`` as name,
+ * a global property ``GROUPS.<name of group>.<type>`` is defined to indicate
+ that the type is valid in the group.
+
+.. todo:: Investigate alternatives to global properties (name collision possible).
+.. todo:: Investigate if import/export group functions would be necessary.
+
+#]===]
+
+include_guard(DIRECTORY)
+include(Common/Map)
+include(Common/Utils)
+
+#[===[.rst:
+.. cmake:variable:: GROUPS_VALID_TYPES
+
+ List of configuration data types that can be stored in a group. The
+ implementation of :cmake:command:`group_new` creates a map for each of these
+ types automatically.
+
+#]===]
+set(GROUPS_VALID_TYPES "CONFIG;DEFINE;CFLAG;ASFLAG;LDFLAG;INCLUDE")
+
+#[===[.rst:
+.. cmake:command:: group_new
+
+ .. code-block:: cmake
+
+ group_new(NAME foo)
+
+ Create a new group.
+
+ Inputs:
+
+ ``NAME``
+ Name of the new group, use |C identifier like string|. The name must be
+ unique within the global namespace, otherwise an error is generated.
+
+#]===]
+function(group_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(group_new NAME)
+
+ group_is_defined(NAME ${_MY_PARAMS_NAME} RET _is_defined)
+ if(_is_defined)
+ message(FATAL_ERROR "group_new(): '${_MY_PARAMS_NAME}' is already defined.")
+ endif()
+
+ set(_null " ")
+ define_property(GLOBAL PROPERTY GROUPS.${_MY_PARAMS_NAME} BRIEF_DOCS ${_null} FULL_DOCS ${_null})
+
+ foreach(_type IN LISTS GROUPS_VALID_TYPES)
+ define_property(GLOBAL PROPERTY GROUPS.${_MY_PARAMS_NAME}.${_type} BRIEF_DOCS ${_null} FULL_DOCS ${_null})
+ map_new(NAME ${_MY_PARAMS_NAME}.${_type})
+ endforeach()
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: group_is_defined
+
+ .. code-block:: cmake
+
+ group_is_defined(NAME foo RET ret)
+
+ Helper function to check if a group has been defined.
+
+ Inputs:
+
+ ``NAME``
+ Name of the group.
+
+ Outputs:
+
+ ``RET``
+ Name of the variable in the parent scope, where the return value is written.
+
+#]===]
+function(group_is_defined)
+ set(_OPTIONS_ARGS)
+ set(_ONE_VALUE_ARGS NAME RET)
+ set(_MULTI_VALUE_ARGS)
+ cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+ check_args(group_is_defined NAME RET)
+
+ get_property(_is_defined GLOBAL PROPERTY GROUPS.${_MY_PARAMS_NAME} DEFINED)
+ set(${_MY_PARAMS_RET} ${_is_defined} PARENT_SCOPE)
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: group_add
+
+ .. code-block:: cmake
+
+ group_add(NAME foo TYPE type KEY one VAL 1)
+
+ Add new key-value pair to the map called ``type`` within a group. Multiple
+ types can be defined in one function call, the key-value pair will be added to
+ each of those types.
+
+ Inputs:
+
+ ``NAME``
+ Name of the group. Trying to add to a non-existing group generates an error.
+
+ ``TYPE`` (multi)
+ Type can be one or more of the values defined in
+ :cmake:variable:`GROUPS_VALID_TYPES`.
+
+ ``KEY``
+ New key to add. Same constraints apply as described in
+ :cmake:command:`map_add`.
+
+ ``VAL`` (optional)
+ Value for new key. Same constraints apply as described in
+ :cmake:command:`map_add`.
+
+#]===]
+function(group_add)
+ set(_OPTIONS_ARGS)
+ set(_ONE_VALUE_ARGS NAME KEY VAL)
+ set(_MULTI_VALUE_ARGS TYPE)
+ cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+ check_args(group_add NAME TYPE KEY)
+
+ group_is_defined(NAME ${_MY_PARAMS_NAME} RET _is_group_defined)
+ if(NOT _is_group_defined)
+ message(FATAL_ERROR "group_add(): '${_MY_PARAMS_NAME}' group is not defined.")
+ endif()
+
+ if(DEFINED _MY_PARAMS_VAL)
+ if(DEFINED CFG_${_MY_PARAMS_KEY})
+ set(_val ${CFG_${_MY_PARAMS_KEY}})
+ message(STATUS "group_add(): '${_MY_PARAMS_KEY}' in group '${_MY_PARAMS_NAME}' is
+ overwritten by command line parameter.")
+ else()
+ set(_val ${_MY_PARAMS_VAL})
+ endif()
+ else()
+ set(_val " ")
+ endif()
+
+ foreach(_type IN LISTS _MY_PARAMS_TYPE)
+ get_property(_is_type_defined GLOBAL PROPERTY GROUPS.${_MY_PARAMS_NAME}.${_type} DEFINED)
+ if(NOT _is_type_defined)
+ message(FATAL_ERROR "group_add(): '${_type}' type is invalid.")
+ endif()
+
+ map_add(NAME ${_MY_PARAMS_NAME}.${_type} KEY ${_MY_PARAMS_KEY} VAL ${_val})
+ endforeach()
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: group_apply
+
+ .. code-block:: cmake
+
+ group_apply(NAME foo TARGETS target1 target2)
+
+ Apply a group onto the selected target(s). All settings contained in the group
+ will be added to the targets. Use this function only for the build targets
+ created with :cmake:command:`stgt_create`.
+
+ Inputs:
+
+ ``NAME``
+ Name of the group.
+
+ ``TARGETS`` (multi)
+ Name of the targets.
+
+ .. todo:: Move this to STGT?
+ .. todo:: Revise implementation for INCLUDE type.
+
+#]===]
+function(group_apply)
+ set(_OPTIONS_ARGS)
+ set(_ONE_VALUE_ARGS NAME)
+ set(_MULTI_VALUE_ARGS TARGETS)
+ cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+ check_args(group_apply NAME TARGETS)
+
+ group_is_defined(NAME ${_MY_PARAMS_NAME} RET _is_defined)
+ if(NOT _is_defined)
+ message(FATAL_ERROR "group_apply(): '${_MY_PARAMS_NAME}' group is not defined.")
+ endif()
+
+ foreach(_target IN LISTS _MY_PARAMS_TARGETS)
+ if(NOT TARGET ${_target})
+ message(FATAL_ERROR "group_apply(): target '${_target}' is not defined.")
+ endif()
+
+ map_read(NAME ${_MY_PARAMS_NAME}.CONFIG KEYS _keys VALS _vals)
+ if(_keys AND _vals)
+ list(LENGTH _keys _count)
+ math(EXPR _count "${_count}-1")
+
+ foreach(i RANGE ${_count})
+ list(GET _keys ${i} _key)
+ list(GET _vals ${i} _val)
+ set_property(TARGET ${_target} PROPERTY ${_key} ${_val})
+ endforeach()
+ endif()
+
+ map_read(NAME ${_MY_PARAMS_NAME}.DEFINE KEYS _keys VALS _vals)
+ if(_keys AND _vals)
+ map_to_list(KEYS ${_keys} VALS ${_vals} OUT _defines)
+ target_compile_definitions(${_target} PRIVATE ${_defines})
+ endif()
+
+ map_read(NAME ${_MY_PARAMS_NAME}.CFLAG KEYS _keys VALS _vals)
+ if(_keys AND _vals)
+ map_to_list(KEYS ${_keys} VALS ${_vals} OUT _cflags)
+ target_compile_options(${_target} PRIVATE ${_cflags})
+ endif()
+
+ map_read(NAME ${_MY_PARAMS_NAME}.ASFLAG KEYS _keys VALS _vals)
+ if(_keys AND _vals)
+ map_to_list(KEYS ${_keys} VALS ${_vals} OUT _asflags)
+ target_compile_options(${_target} PRIVATE $<$<COMPILE_LANGUAGE:ASM>:${_asflags}>)
+ endif()
+
+ map_read(NAME ${_MY_PARAMS_NAME}.LDFLAG KEYS _keys VALS _vals)
+ if(_keys AND _vals)
+ map_to_list(KEYS ${_keys} VALS ${_vals} OUT _ldflags)
+ target_link_options(${_target} PRIVATE ${_ldflags})
+ endif()
+
+ map_read(NAME ${_MY_PARAMS_NAME}.INCLUDE KEYS _keys VALS _vals)
+ if(_keys)
+ stgt_add_inc_param(NAME ${_target} KEY ARCH INC ${_keys})
+ endif()
+ endforeach()
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: group_print
+
+ .. code-block:: cmake
+
+ group_print(NAME foo)
+
+ Print contents of the group, for debug purposes.
+
+ Inputs:
+
+ ``NAME``
+ Name of the group.
+
+#]===]
+function(group_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})
+
+ if (NOT DEFINED _MY_PARAMS_NAME)
+ message(FATAL_ERROR "group_print(): mandatory parameter 'NAME' missing.")
+ endif()
+
+ message("\n====Group '${_MY_PARAMS_NAME}'====")
+ foreach(_type IN LISTS GROUPS_VALID_TYPES)
+ map_print(NAME ${_MY_PARAMS_NAME}.${_type})
+ endforeach()
+ message("====Group '${_MY_PARAMS_NAME}' end====\n")
+endfunction()
\ No newline at end of file