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
diff --git a/Common/Map.cmake b/Common/Map.cmake
new file mode 100644
index 0000000..710f9b3
--- /dev/null
+++ b/Common/Map.cmake
@@ -0,0 +1,306 @@
+#-------------------------------------------------------------------------------
+# 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()
\ No newline at end of file
diff --git a/Common/STGT.cmake b/Common/STGT.cmake
new file mode 100644
index 0000000..b7705fd
--- /dev/null
+++ b/Common/STGT.cmake
@@ -0,0 +1,717 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#[===[.rst:
+STGT utility file
+-----------------
+The STGT ("simple target") API is a collection of functions which wrap the CMake
+target functions and extend the built-in functionality. The main purpose of this
+utility is to connect the :cmake:module:`group`\s and the build targets. It
+handles the creation of new build targets, collecting the groups that will be
+applied on the target, and setting the target type. Also it provides functions
+to add source files and include paths in view of the properties already applied
+to the target, assign a linker script to the target, etc.
+
+After the configuration step, the targets created by STGT are the same, in the
+end, as normal CMake binary targets. However, the concept of STGT is to have
+"virtual" targets when collecting the setting groups, etc. and only "tie" them
+to a CMake target at the end of setup. This provides more flexibilty and
+expandabilty for the future, without additional complexity to the user, because
+the functionality is hidden behind the API.
+
+Internally the utility uses global properties and groups. Global property
+``STGT.<name of target>`` is defined to indicate that the target is defined.
+Global property ``STGT.<name of target>.GROUPS`` stores the list of groups added
+to the target. Adding a group to a target only results in modifying this list,
+the parameters stored in the group are actually applied only by the
+:cmake:command:`stgt_set_target` function. This function creates a normal CMake
+binary target for the STGT target and calls :cmake:command:`group_apply`, which
+transfers all the parameters stored in the groups selected for the STGT target
+into normal CMake target properties (compile flags, defines, etc.).
+
+.. todo:: How to include compiler file?
+
+#]===]
+
+include_guard(DIRECTORY)
+include(Common/Group)
+include(Common/Utils)
+
+#[===[.rst:
+.. cmake:command:: stgt_create
+
+  .. code-block:: cmake
+
+    stgt_create(NAME foo)
+
+  Create new target.
+
+  Inputs:
+
+  ``NAME``
+    Name of the new target, use |C identifier like string|. The name must be
+    unique within the global namespace, otherwise an error is generated.
+
+  .. todo:: TYPE and SRCS properties unused.
+
+#]===]
+function(stgt_create)
+	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(stgt_create NAME)
+
+	set(_null " ")
+	define_property(GLOBAL PROPERTY STGT.${_MY_PARAMS_NAME} BRIEF_DOCS ${_null} FULL_DOCS ${_null})
+	foreach(_type IN ITEMS GROUPS TYPE SRCS)
+		define_property(GLOBAL PROPERTY STGT.${_MY_PARAMS_NAME}.${_type} BRIEF_DOCS ${_null} FULL_DOCS ${_null})
+	endforeach()
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_is_defined
+
+  .. code-block:: cmake
+
+    stgt_is_defined(NAME foo RET ret)
+
+  Helper function to check if a target has been defined.
+
+  Inputs:
+
+  ``NAME``
+    Name of the target.
+
+  Outputs:
+
+  ``RET``
+    Name of the variable in the parent scope, where the return value is written.
+
+#]===]
+function(stgt_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(stgt_is_defined NAME RET)
+
+	get_property(_is_defined GLOBAL PROPERTY STGT.${_MY_PARAMS_NAME} DEFINED)
+	set(${_MY_PARAMS_RET} ${_is_defined} PARENT_SCOPE)
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_add_setting
+
+  .. code-block:: cmake
+
+    stgt_add_setting(NAME foo GROUPS group1 group2)
+
+  Add setting groups to a target. The groups are not applied yet, only collected
+  to the group list of the target. Multiple groups can be added in one call.
+
+  Inputs:
+
+  ``NAME``
+    Name of the target.
+
+  ``GROUPS`` (multi)
+    Name of the groups.
+
+#]===]
+function(stgt_add_setting)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS NAME)
+	set(_MULTI_VALUE_ARGS GROUPS)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_add_setting NAME GROUPS)
+
+	stgt_is_defined(NAME ${_MY_PARAMS_NAME} RET _is_stgt_defined)
+	if(NOT _is_stgt_defined)
+		message(FATAL_ERROR "stgt_add_setting(): '${_MY_PARAMS_NAME}' stgt is not defined.")
+	endif()
+
+	foreach(_group IN LISTS _MY_PARAMS_GROUPS)
+		group_is_defined(NAME ${_group} RET _is_group_defined)
+		if(NOT _is_group_defined)
+			message(FATAL_ERROR "stgt_add_setting(): '${_group}' group is not defined.")
+		endif()
+	endforeach()
+
+	get_property(_groups GLOBAL PROPERTY STGT.${_MY_PARAMS_NAME}.GROUPS)
+	list(APPEND _groups "${_MY_PARAMS_GROUPS}")
+	set_property(GLOBAL PROPERTY STGT.${_MY_PARAMS_NAME}.GROUPS ${_groups})
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_set_target
+
+  .. code-block:: cmake
+
+    stgt_set_target(NAME foo TYPE lib [ARGS arg1 ...])
+
+  Set target type. This function creates a normal CMake binary target, and
+  applies the list of groups previously associated to the target.
+
+  Inputs:
+
+  ``NAME``
+    Name of the target.
+
+  ``TYPE``
+    Type can be EXE, LIB, OBJLIB.
+
+  ``ARGS`` (multi)
+    Additional arguments to pass through to add_executable or add_library
+
+  .. todo:: New functionality might be needed to handle situations when order of
+            include paths matters.
+
+#]===]
+function(stgt_set_target)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS NAME TYPE)
+	set(_MULTI_VALUE_ARGS ARGS)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_set_target NAME TYPE)
+
+	string(TOUPPER ${_MY_PARAMS_TYPE} _type)
+	if(_type STREQUAL "EXE")
+		add_executable(${_MY_PARAMS_NAME} ${_MY_PARAMS_ARGS})
+	elseif(_type STREQUAL "LIB")
+		add_library(${_MY_PARAMS_NAME} STATIC ${_MY_PARAMS_ARGS})
+	elseif(_type STREQUAL "OBJLIB")
+		add_library(${_MY_PARAMS_NAME} OBJECT ${_MY_PARAMS_ARGS})
+	else()
+		message(FATAL_ERROR "stgt_set_target(): '${_MY_PARAMS_TYPE}' target type is invalid.")
+	endif()
+
+	get_property(_groups GLOBAL PROPERTY STGT.${_MY_PARAMS_NAME}.GROUPS)
+	foreach(_group IN LISTS _groups)
+		group_apply(NAME ${_group} TARGETS ${_MY_PARAMS_NAME})
+	endforeach()
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_get_param
+
+  .. code-block:: cmake
+
+    stgt_get_param(NAME foo KEY one RET ret)
+
+  Get the value of a property on a specific target.
+
+  Inputs:
+
+  ``NAME``
+    Name of the target.
+
+  ``KEY``
+    Which property to read.
+
+  Outputs:
+
+  ``RET``
+    Name of the variable in the parent scope, where the return value is written.
+
+#]===]
+function(stgt_get_param)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS NAME KEY RET)
+	set(_MULTI_VALUE_ARGS)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_get_param NAME KEY RET)
+
+	stgt_is_defined(NAME ${_MY_PARAMS_NAME} RET _is_stgt_defined)
+	if(NOT _is_stgt_defined)
+		message(FATAL_ERROR "stgt_get_param(): '${_MY_PARAMS_NAME}' stgt is not defined.")
+	endif()
+
+	get_target_property(_val ${_MY_PARAMS_NAME} ${_MY_PARAMS_KEY})
+	if(_val MATCHES ".*-NOTFOUND")
+		message(FATAL_ERROR "stgt_get_param(): parameter '${_MY_PARAMS_KEY}' of target
+							'${_MY_PARAMS_NAME}' is missing.")
+	endif()
+
+	set(${_MY_PARAMS_RET} ${_val} PARENT_SCOPE)
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_add_src
+
+  .. code-block:: cmake
+
+    stgt_add_src(NAME foo SRC main.c)
+
+  Add source files to a target. Multiple source files can be added to multiple
+  targets in one call.
+
+  Inputs:
+
+  ``NAME`` (multi)
+    Name of the targets.
+
+  ``SRC`` (multi)
+    Source files to add.
+
+#]===]
+function(stgt_add_src)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS)
+	set(_MULTI_VALUE_ARGS NAME SRC)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_add_src NAME SRC)
+
+	foreach(_target IN LISTS _MY_PARAMS_NAME)
+		if(NOT TARGET ${_target})
+			message(FATAL_ERROR "stgt_add_src(): '${_target}' target is not defined.")
+		endif()
+
+		get_property(_object_lib TARGET "${_target}" PROPERTY "OBJECT_LIB")
+		if(_object_lib)
+			target_sources(${_object_lib} PRIVATE ${_MY_PARAMS_SRC})
+		else()
+			target_sources(${_target} PRIVATE ${_MY_PARAMS_SRC})
+		endif()
+	endforeach()
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_add_src_cond
+
+  .. code-block:: cmake
+
+    stgt_add_src_cond(NAME foo bar SRC uart.c spi.c KEY one VAL 1)
+    stgt_add_src_cond(NAME foo bar SRC uart.c spi.c KEY one VAL 1 OPTIONAL true)
+
+  Add source files to a target based on a condition: if value of KEY property on
+  the target equals VAL, the source files will be added. Multiple source files
+  can be added to multiple targets in one call. In this case the condition is
+  evaluated for each target separately. If the KEY property is not defined for
+  the target and OPTIONAL is not true, an error will be generated.
+
+  Inputs:
+
+  ``NAME`` (multi)
+    Name of the targets.
+
+  ``SRC`` (multi)
+    Source files to add.
+
+  ``KEY``
+    Which property to read.
+
+  ``VAL``
+    Condition is true if value of KEY equals this value.
+
+  ``OPTIONAL`` (bool)
+    If true, no error will be generated if KEY is not defined for the target.
+    Can be omitted, default value is false.
+
+#]===]
+function(stgt_add_src_cond)
+	set(_OPTIONS_ARGS OPTIONAL)
+	set(_ONE_VALUE_ARGS KEY VAL)
+	set(_MULTI_VALUE_ARGS NAME SRC)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_add_src_cond NAME KEY VAL SRC)
+
+	foreach(_target IN LISTS _MY_PARAMS_NAME)
+		get_target_property(_val ${_target} ${_MY_PARAMS_KEY})
+		if((_val MATCHES ".*-NOTFOUND") AND (NOT _MY_PARAMS_OPTIONAL))
+			message(FATAL_ERROR "stgt_add_src_cond: mandatory parameter '${_MY_PARAMS_KEY}'
+								of target '${_target}' is missing.")
+		endif()
+
+		if(${_val} STREQUAL ${_MY_PARAMS_VAL})
+			stgt_add_src(NAME ${_target} SRC ${_MY_PARAMS_SRC})
+		endif()
+	endforeach()
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_add_src_param
+
+  .. code-block:: cmake
+
+    stgt_add_src_param(NAME foo bar SRC ../@prop_name@/uart.c KEY prop_name)
+
+  Add source files to a target using a parameter dependent path. The name of KEY
+  can be used in the path of the source file with the @prop_name@ syntax. This
+  field will be replaced by the value of the property on the target. Multiple
+  source files can be added to multiple targets in one call. In this case the
+  parameter value is evaluated for each target separately.
+
+  Inputs:
+
+  ``NAME`` (multi)
+    Name of the targets.
+
+  ``SRC`` (multi)
+    Source files to add.
+
+  ``KEY``
+    Which property to read.
+
+  .. todo:: Fix syntax highlight warning.
+
+#]===]
+function(stgt_add_src_param)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS KEY)
+	set(_MULTI_VALUE_ARGS NAME SRC)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_add_src_param NAME KEY SRC)
+
+	foreach(_target IN LISTS _MY_PARAMS_NAME)
+		get_target_property(_val ${_target} ${_MY_PARAMS_KEY})
+		if(_val MATCHES ".*-NOTFOUND")
+			message(FATAL_ERROR "stgt_add_src_param: mandatory parameter '${_MY_PARAMS_KEY}'
+								of target '${_target}' is missing.")
+		endif()
+
+		set(_src ${_MY_PARAMS_SRC})
+		list(TRANSFORM _src REPLACE @${_MY_PARAMS_KEY}@ ${_val})
+		stgt_add_src(NAME ${_target} SRC ${_src})
+	endforeach()
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_add_inc
+
+  .. code-block:: cmake
+
+    stgt_add_inc(NAME foo INC ${ROOT_DIR}/include)
+
+  Add include paths to a target. Multiple paths can be added to a target in one
+  call.
+
+  Inputs:
+
+  ``NAME``
+    Name of the target.
+
+  ``INC`` (multi)
+    Include paths to add.
+
+#]===]
+function(stgt_add_inc)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS NAME)
+	set(_MULTI_VALUE_ARGS INC)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_add_inc NAME INC)
+
+	if(NOT TARGET ${_MY_PARAMS_NAME})
+		message(FATAL_ERROR "stgt_add_inc(): '${_MY_PARAMS_NAME}' target is not defined.")
+	endif()
+
+	target_include_directories(${_MY_PARAMS_NAME} PRIVATE ${_MY_PARAMS_INC})
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_add_inc_cond
+
+  .. code-block:: cmake
+
+    stgt_add_inc_cond(NAME foo INC ${ROOT_DIR}/include KEY one VAL 1)
+    stgt_add_inc_cond(NAME foo INC ${ROOT_DIR}/include KEY one VAL 1 OPTIONAL true)
+
+  Add include paths to a target based on a condition: if value of KEY property
+  on the target equals VAL, the include paths will be added. Multiple paths can
+  be added in one call. If the KEY property is not defined for the target and
+  OPTIONAL is not true, an error will be generated.
+
+  Inputs:
+
+  ``NAME``
+    Name of the target.
+
+  ``INC`` (multi)
+    Include paths to add.
+
+  ``KEY``
+    Which property to read.
+
+  ``VAL``
+    Condition is true if value of KEY equals this value.
+
+  ``OPTIONAL`` (bool)
+    If true, no error will be generated if KEY is not defined for the target.
+    Can be omitted, default value is false.
+
+#]===]
+function(stgt_add_inc_cond)
+	set(_OPTIONS_ARGS OPTIONAL)
+	set(_ONE_VALUE_ARGS NAME KEY VAL)
+	set(_MULTI_VALUE_ARGS INC)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_add_inc_cond NAME KEY VAL INC)
+
+	get_target_property(_val ${_MY_PARAMS_NAME} ${_MY_PARAMS_KEY})
+	if(NOT (_val OR _MY_PARAMS_OPTIONAL))
+		message(FATAL_ERROR "stgt_add_inc_cond: mandatory parameter '${_MY_PARAMS_KEY}'
+							of target '${_MY_PARAMS_NAME}' is missing.")
+	endif()
+
+	if(${_val} STREQUAL ${_MY_PARAMS_VAL})
+		stgt_add_inc(NAME ${_MY_PARAMS_NAME} INC ${_MY_PARAMS_INC})
+	endif()
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_add_inc_param
+
+  .. code-block:: cmake
+
+    stgt_add_inc_param(NAME foo INC ../@prop_name@/include KEY prop_name)
+
+  Add include paths to a target using a parameter dependent path. The name of
+  KEY can be used in the include path with the @prop_name@ syntax. This field
+  will be replaced by the value of the property on the target. Multiple paths
+  can be added in one call.
+
+  Inputs:
+
+  ``NAME``
+    Name of the target.
+
+  ``INC`` (multi)
+    Include paths to add.
+
+  ``KEY``
+    Which property to read.
+
+  .. todo:: Fix syntax highlight warning.
+
+#]===]
+function(stgt_add_inc_param)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS NAME KEY)
+	set(_MULTI_VALUE_ARGS INC)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_add_inc_param NAME KEY INC)
+
+	get_target_property(_val ${_MY_PARAMS_NAME} ${_MY_PARAMS_KEY})
+	if(NOT _val)
+		message(FATAL_ERROR "stgt_add_inc_param: mandatory parameter '${_MY_PARAMS_KEY}'
+							of target '${_MY_PARAMS_NAME}' is missing.")
+	endif()
+
+	list(TRANSFORM _MY_PARAMS_INC REPLACE @${_MY_PARAMS_KEY}@ ${_val})
+	stgt_add_inc(NAME ${_MY_PARAMS_NAME} INC ${_MY_PARAMS_INC})
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_link_libraries
+
+  .. code-block:: cmake
+
+    stgt_link_libraries(NAME foo LIBS lib1 lib2)
+
+  Link libraries to target. Multiple libraries can be linked in one call.
+
+  Inputs:
+
+  ``NAME``
+    Name of the target.
+
+  ``LIBS`` (multi)
+    Libraries to link.
+
+#]===]
+function(stgt_link_libraries)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS NAME)
+	set(_MULTI_VALUE_ARGS LIBS)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_link_libraries NAME LIBS)
+
+	target_link_libraries(${_MY_PARAMS_NAME} PRIVATE ${_MY_PARAMS_LIBS})
+
+	get_property(_object_lib TARGET "${_MY_PARAMS_NAME}" PROPERTY "OBJECT_LIB")
+	if(_object_lib)
+		get_property(_sources TARGET "${_MY_PARAMS_NAME}" PROPERTY "SOURCES")
+		set_property(TARGET "${_object_lib}" APPEND PROPERTY "SOURCES" "$<TARGET_GENEX_EVAL:${_MY_PARAMS_NAME},${_sources}>")
+		set_property(TARGET "${_MY_PARAMS_NAME}" PROPERTY "SOURCES" "")
+
+		set(_deps "${_MY_PARAMS_LIBS}")
+		foreach(_lib ${_MY_PARAMS_LIBS})
+			get_target_property(_type "${_lib}" "TYPE")
+			if(_type STREQUAL "OBJECT_LIBRARY")
+				list(APPEND _deps "$<TARGET_OBJECTS:${_lib}>")
+			else()
+				list(APPEND _deps "$<TARGET_FILE:${_lib}>")
+			endif()
+		endforeach()
+
+		get_property(_build_messages TARGET "${_MY_PARAMS_NAME}" PROPERTY "BUILD_MESSAGES")
+		foreach(_message ${_build_messages})
+			set_property(TARGET "${_message}" APPEND PROPERTY "BUILD_MESSAGE_DEPENDS" "${_deps}")
+		endforeach()
+	endif()
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_link_build_messages
+
+  .. code-block:: cmake
+
+    stgt_link_build_messages(NAME target LIBS msg1 [msg2 ...])
+
+  Link one or more object libraries to the target, treating them as "build messages".
+  This function configures the libraries as order-only dependencies of the target,
+  and appends the list of all (non-build message) libraries linked to the target,
+  as well as their output files to the property named BUILD_MESSAGE_DEPENDS, found
+  on the build message libraries.
+
+  This property can be used with add_custom_command to cause the build message to rebuild
+  if any of these linker-input files are modified in the following way:
+
+  .. code-block:: cmake
+
+    add_custom_command([...] DEPENDS "$<TARGET_GENEX_EVAL:msg1,$<TARGET_PROPERTY:msg1,BUILD_MESSAGE_DEPENDS>>")
+
+  Calling this function on a target causes it to be split into two targets behind the scenes:
+  an object library, which compiles all the source files, and the original target, which
+  performs the linking step.
+
+  The practical implication of this is that the SOURCES property on the original target will
+  appear empty. Please refrain from updating this property directly, or using native
+  CMake commands that update this variable. Use the equivalent STGT functions instead.
+
+  If you need to use the value of the SOURCES property, you can check for the presence
+  of the OBJECT_LIB property. This will contain the name of the object library target, which
+  will have its SOURCES property populated normally.
+
+  With generator expressions you would acomplish this in the following way:
+
+  .. code-block:: cmake
+
+    set(foo_src_tgt "$<IF:$<BOOL:$<TARGET_PROPERTY:foo,OBJECT_LIB>>,$<TARGET_PROPERTY:foo,OBJECT_LIB>,foo>")
+    $<TARGET_GENEX_EVAL:${foo_src_tgt},$<TARGET_PROPERTY:${foo_src_tgt},SOURCES>>
+
+  Inputs:
+
+  ``NAME``
+    Name of the target.
+
+  ``LIBS`` (multi)
+    Libraries to link.
+
+#]===]
+function(stgt_link_build_messages)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS NAME)
+	set(_MULTI_VALUE_ARGS LIBS)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_add_build_message NAME LIBS)
+
+	get_property(_OBJECT_LIB TARGET "${_MY_PARAMS_NAME}" PROPERTY "OBJECT_LIB")
+	if(NOT _OBJECT_LIB)
+		set(_OBJECT_LIB "${_MY_PARAMS_NAME}_o")
+		set_property(TARGET "${_MY_PARAMS_NAME}" PROPERTY "OBJECT_LIB" "${_OBJECT_LIB}")
+
+		stgt_create(NAME "${_OBJECT_LIB}")
+		stgt_set_target(NAME "${_OBJECT_LIB}" TYPE objlib ARGS EXCLUDE_FROM_ALL)
+
+		get_property(_sources TARGET "${_MY_PARAMS_NAME}" PROPERTY "SOURCES")
+		set_property(TARGET "${_OBJECT_LIB}" PROPERTY "SOURCES" "$<TARGET_GENEX_EVAL:${_MY_PARAMS_NAME},${_sources}>")
+		set_property(TARGET "${_MY_PARAMS_NAME}" PROPERTY "SOURCES" "")
+
+		foreach(_prop "COMPILE_DEFINITIONS" "COMPILE_OPTIONS" "LINK_OPTIONS" "INCLUDE_DIRECTORIES")
+			set_property(TARGET "${_OBJECT_LIB}" PROPERTY "${_prop}" "$<TARGET_GENEX_EVAL:${_MY_PARAMS_NAME},$<TARGET_PROPERTY:${_MY_PARAMS_NAME},${_prop}>>")
+		endforeach()
+
+		stgt_link_libraries(NAME "${_MY_PARAMS_NAME}" LIBS "${_OBJECT_LIB}")
+	endif()
+
+	set_property(TARGET "${_MY_PARAMS_NAME}" APPEND PROPERTY "BUILD_MESSAGES" "${_MY_PARAMS_LIBS}")
+
+	get_property(_libs TARGET "${_MY_PARAMS_NAME}" PROPERTY "LINK_LIBRARIES")
+	set(_deps "${_libs}" "$<TARGET_GENEX_EVAL:${_MY_PARAMS_NAME},$<TARGET_PROPERTY:${_MY_PARAMS_NAME},LINK_DEPENDS>>")
+	foreach(_lib ${_libs})
+		get_target_property(_type "${_lib}" "TYPE")
+		if(_type STREQUAL "OBJECT_LIBRARY")
+			list(APPEND _deps "$<TARGET_OBJECTS:${_lib}>")
+		else()
+			list(APPEND _deps "$<TARGET_FILE:${_lib}>")
+		endif()
+	endforeach()
+
+	foreach(_message ${_MY_PARAMS_LIBS})
+		set_property(TARGET "${_message}" APPEND PROPERTY "BUILD_MESSAGE_DEPENDS" "${_deps}")
+		target_link_options("${_MY_PARAMS_NAME}" PRIVATE "$<TARGET_OBJECTS:${_message}>")
+	endforeach()
+
+	add_dependencies("${_MY_PARAMS_NAME}" ${_MY_PARAMS_LIBS})
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: stgt_set_linker_script
+
+  .. code-block:: cmake
+
+    stgt_set_linker_script(NAME foo FILE foo.ld)
+    stgt_set_linker_script(NAME foo FILE foo.ld.S DEF RAM_SIZE=1024 INC include/mem)
+
+  Set linker file for target. FILE will be assigned to the target as linker
+  script. The function uses the compiler_set_linker_script() function call,
+  therefore the compiler abstraction API should be included. DEF and INC are
+  also passed to this function, which should use them when preprocessing the
+  linker script. The compiler_set_linker_script() function should add an LDFLAG
+  using the toolchain specific syntax to the TARGET_linker_script group.
+
+  Inputs:
+
+  ``NAME``
+    Name of the target.
+
+  ``FILE``
+    Linker script file for the target.
+
+  ``DEF`` (multi, optional)
+    Defines for the linker script preprocessor.
+
+  ``INC`` (multi, optional)
+    Include paths for the linker script preprocessor.
+
+#]===]
+function(stgt_set_linker_script)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS NAME FILE)
+	set(_MULTI_VALUE_ARGS DEF INC)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	check_args(stgt_set_linker_script NAME FILE)
+
+	if(NOT TARGET ${_MY_PARAMS_NAME})
+		message(FATAL_ERROR "stgt_set_linker_script(): '${_MY_PARAMS_NAME}' target is not defined.")
+	endif()
+
+	group_new(NAME "${_MY_PARAMS_NAME}_linker_script")
+	compiler_set_linker_script(
+		TARGET "${_MY_PARAMS_NAME}"
+		FILE "${_MY_PARAMS_FILE}"
+		DEF "${_MY_PARAMS_DEF}"
+		INC "${_MY_PARAMS_INC}"
+	)
+	group_apply(NAME "${_MY_PARAMS_NAME}_linker_script" TARGETS "${_MY_PARAMS_NAME}")
+endfunction()
\ No newline at end of file
diff --git a/Common/Utils.cmake b/Common/Utils.cmake
new file mode 100644
index 0000000..d93dcee
--- /dev/null
+++ b/Common/Utils.cmake
@@ -0,0 +1,37 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#[===[.rst:
+Misc utilities
+--------------
+#]===]
+
+include_guard(DIRECTORY)
+
+#[===[.rst:
+.. cmake:command:: check_args
+
+  .. code-block:: cmake
+
+    check_args(func_name REQ_ARG1 REQ_ARG2)
+
+  Helper macro for argument checking in functions. First argument *func_name* is
+  the name of the function, other arguments are the names of the required
+  arguments to that function. The macro iterates through the list, and prints
+  and error message if not all arguments are defined.
+
+#]===]
+macro(check_args)
+	set(_argv "${ARGV}")
+	list(SUBLIST _argv 0 1 _func)
+	list(SUBLIST _argv 1 -1 _args)
+	foreach(_arg IN LISTS _args)
+		if (NOT DEFINED _MY_PARAMS_${_arg})
+			message(FATAL_ERROR "${_func}(): mandatory parameter '${_arg}' missing.")
+		endif()
+	endforeach()
+endmacro()