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/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