#-------------------------------------------------------------------------------
# Copyright (c) 2017-2019, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
#-------------------------------------------------------------------------------

#This file defines project-specific overrides to cmake default behaviour for:
#	* compiler detection
#	* target system detection
cmake_minimum_required(VERSION 3.3) #IN_LIST was introduced in 3.3
cmake_policy(SET CMP0057 NEW)

Include(CMakeParseArguments)

#The CMAKE_SYSTEM_XXX settings make cmake to stop applying some target system specific settings.
#Tell cmake we are compiling for ARM chips.
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE INTERNAL "Set target processor type to force cmake include some of our scripts." FORCE)
#Tell cmake this is an "Embedded" system
set(CMAKE_SYSTEM_NAME "Embedded"  CACHE INTERNAL "Set target system name to force cmake include some of our scripts." FORCE)

#Stop built in CMakeDetermine<lang>.cmake scripts to run.
set (CMAKE_CXX_COMPILER_ID_RUN 1)
#Stop cmake run compiler tests.
set (CMAKE_CXX_COMPILER_FORCED true)
#Stop built in CMakeDetermine<lang>.cmake scripts to run.
set (CMAKE_C_COMPILER_ID_RUN 1)
#Stop cmake run compiler tests.
set (CMAKE_C_COMPILER_FORCED true)

#This macro is used to enforce the ARM project structure.
#Inputs: (This macro uses some "global" variables)
#	global variable PROJ_CONFIG - a global configuration file to be used by all projects.
#								  It overrides value of CONFIG parameter.
#	CONFIG - the configuration file which shall be used
#Examples
#	To use global project config:
#		embedded_project_start()
#	To use config file relative to the top level CmakeLists.txt:
#		embedded_project_start(./ConfigDefault.cmake)
#	To use config file relative to the CmakeLists.txt file where this macro is used:
#		embedded_project_start(${CMAKE_CURRENT_LIST_DIR}/ConfigDefault.cmake)
macro(embedded_project_start)
	#Default project configuration file
	if (DEFINED PROJ_CONFIG) #Take the global setting as default value
		set(_PROJ_CONFIG ${PROJ_CONFIG})
	endif()

	set( _OPTIONS_ARGS )					#No option (on/off) arguments
	set( _ONE_VALUE_ARGS CONFIG)	#Single option arguments (e.g. PROJ_NAME "bubu_project")
	set( _MULTI_VALUE_ARGS )		#One list argument (e.g. LANGUAGES C ASM CXX)
	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )

	#Cehck passed parameters
	if(NOT _MY_PARAMS_CONFIG)
		if(NOT DEFINED _PROJ_CONFIG)
			set(_PROJ_CONFIG "./ConfigDefault.cmake")
			message(STATUS "embedded_project_start: no project configuration file defined, falling back to default.")
		endif()
	elseif(NOT DEFINED PROJ_CONFIG)
		set(_PROJ_CONFIG ${_MY_PARAMS_CONFIG})
	endif()

	get_filename_component(_ABS_PROJ_CONFIG ${_PROJ_CONFIG} ABSOLUTE)
	message( STATUS "embedded_project_start: using project specific config file (PROJ_CONFIG = ${_ABS_PROJ_CONFIG})")
	include("${_PROJ_CONFIG}")
endmacro()

#Override CMake default behaviour
macro(embedded_project_fixup)
	get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES)

	#Merge CPU and configuration specific compiler and linker flags.
	foreach(LNG ${languages})
		include(Common/CompilerDetermine${LNG})
		#since all CMake "built in" scripts already executed, we need fo fix up some things here.
		embedded_fixup_build_type_vars(${LNG})

		#Apply CPU specific and configuration specific compile flags.
		if(NOT CMAKE_${LNG}_FLAGS MATCHES ".*${CMAKE_${LNG}_FLAGS_CPU}.*")
			set(CMAKE_${LNG}_FLAGS "${CMAKE_${LNG}_FLAGS} ${CMAKE_${LNG}_FLAGS_CPU}")
		endif()
		#Fix output file extension.
		set (CMAKE_EXECUTABLE_SUFFIX_${LNG} ".axf")
		unset(ALL_SRC_${LNG})
		unset(ALL_SRC_${LNG}_S)
		unset(ALL_SRC_${LNG}_NS)
	endforeach()
	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_LINK_FLAGS_CPU}")
endmacro()

#Allow project specific script to do configuration after all targets are specified; it has to be done for each target defined
macro (embedded_project_end TARGET)
	get_property(_MAC DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY MACROS)
	if ("embedded_project_build_config_apply" IN_LIST _MAC)
		message(WARNING "embedded_project_end(): macro embedded_project_build_config_apply() is defined. Your build config file may be out-dated.")
	endif()

	#Apply compile flags.
	_embedded_apply_compile_flags(${TARGET})
	#Apply macro definitions
	_embedded_apply_compile_defines(${TARGET})
	#Apply include paths
	_embedded_apply_include_directories(${TARGET})
	#Apply linker flags.
	_embedded_apply_link_flags(${TARGET})
	#If target is executable, apply linker command file setting.
	get_property(_TGT_TYPE TARGET ${TARGET} PROPERTY TYPE)
	if(_TGT_TYPE STREQUAL "EXECUTABLE")
		_embedded_apply_linker_cmd_file_setting(${TARGET})
	endif()
endmacro()

macro(embedded_fixup_build_type_vars LANG)
	#since all CMake "built in" scripts already executed, we need fo fix up some things here.
	set (CMAKE_${LANG}_FLAGS_DEBUG "${CMAKE_${LANG}_FLAGS_DEBUG_INIT}" CACHE STRING "Flags used by the compiler during debug builds." FORCE)
	set (CMAKE_${LANG}_FLAGS_MINSIZEREL "${CMAKE_${LANG}_FLAGS_MINSIZEREL_INIT}" CACHE STRING "Flags used by the compiler during release builds for minimum size." FORCE)
	set (CMAKE_${LANG}_FLAGS_RELEASE "${CMAKE_${LANG}_FLAGS_RELEASE_INIT}" CACHE STRING "Flags used by the compiler during release builds." FORCE)
	set (CMAKE_${LANG}_FLAGS_RELWITHDEBINFO "${CMAKE_${LANG}_FLAGS_RELWITHDEBINFO_INIT}" CACHE STRING "Flags used by the compiler during release builds with debug info." FORCE)
endmacro()


#Convert a CMake list to a string
#
#Examples:
#  list_to_string(my_string 1 2 3 4)
#  list_to_string(my_string ${CMAKE_C_FLAGS})
#
#INPUTS:
#    RES  - (mandatory) - name of variable to put result in
#    The list to be converted.
#
#OUTPUTS
#    List items concatenated to a string.
#
function(list_to_string RES)
	foreach(_ITEM ${ARGN})
		set(_RES "${_RES} ${_ITEM}")
	endforeach()
	set(${RES} "${_RES}" PARENT_SCOPE)
endfunction()

#Ensure current generator is supported.
#
# This function takes a list of supported generators (e.g. "Unix Makefiles") and
# exits with fatal error is the current generator is not on the list.
#Examples:
#  assert_generator_is("MSYS Makefiles" "MinGW Makefiles")
#
#INPUTS:
#    The list of supported generators.
#
#OUTPUTS
#    n/a
#
function(assert_generator_is)
		if (NOT CMAKE_GENERATOR IN_LIST ARGN)
			message(FATAL_ERROR "assert_generator_is(): Generator '${CMAKE_GENERATOR}' is not on the list of supported generators.")
		endif()
endfunction()

#Specify an include path for the compiler
#
# Specify a global include directory for all non external targets in the current
# build.
# The parameter ABSOLUTE can be set to true to ask CMake to convert the PATH to
# absolute. This gives better looking command line arguments, and also helps
# removing duplicates.
#
#Examples:
#  embedded_include_directories(PATH "C:/fo/bar/include")
#  embedded_include_directories(PATH "C:/fo/bar/include" ABSOLUTE)
#
#INPUTS:
#    PATH  - (mandatory) - the include path to add
#    ABSOLUTE - (optional) - whether the path shall be converted to absolute
#
#OUTPUTS
#    Modified list of include directories.
#
function (embedded_include_directories)
	#Parse our arguments
	set( _OPTIONS_ARGS ABSOLUTE)		#Option (on/off) arguments (e.g. IGNORE_CASE)
	set( _ONE_VALUE_ARGS  PATH)		#Single option arguments (e.g. PATH "./foo/bar")
	set( _MULTI_VALUE_ARGS )		#List arguments (e.g. LANGUAGES C ASM CXX)
	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )

	#Check mandatory parameters
	if(NOT _MY_PARAMS_PATH)
		failure("embedded_include_directories(): Missing PATH parameter!")
	endif()

	if(DEFINED _MY_PARAMS_ABSOLUTE AND ${_MY_PARAMS_ABSOLUTE})
		get_filename_component(_MY_PARAMS_PATH ${_MY_PARAMS_PATH} ABSOLUTE)
	endif()

	include_directories(${_MY_PARAMS_PATH})
endfunction()

#Return the language of a source file.
#
# Language is either specified by the LANGUAGE property of the source file or
# determined based on the extension of the file.
# Search is limited for languages enabled in the current project.
#
#Examples:
#  To get language of "foo/bar.c" written into _LNG:
#  embedded_get_source_language("foo/bar.c" _LNG)
#
#INPUTS:
#   FILE  - (mandatory) - The file to determine language of.
#   RES   - (mandatory) - Name of the variable to write the result into.
#
#OUTPUTS
#   ${RES} - language string (e.g. "C", "CXX", "ASM"). empty string for files
#			 with no language.
#
function(embedded_get_source_language FILE RES)
	if (ARGN)
		message(FATAL_ERROR "embedded_get_source_language(): too many parameters passed.")
	endif()
	#If language property is set, use that.
	get_property(_LNG SOURCE ${_SRC} PROPERTY LANGUAGE)
	if (NOT ${_LNG} STREQUAL "")
		set(${RES} ${_LNG} PARENT_SCOPE)
	else()
		#Set empty return value.
		set(${RES} "" PARENT_SCOPE)
		#Property not set, use extension of the file to determine
		#language.
		string(REGEX MATCH "[^.]*$" _EXT "${_SRC}")
		#Get list of enabled languages.
		get_property(_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
		foreach(_LNG ${_LANGUAGES})
			#See if the extension is contained in the list of file extensions
			#of this language.
			if(_EXT IN_LIST CMAKE_${_LNG}_SOURCE_FILE_EXTENSIONS)
				set(${RES} ${_LNG} PARENT_SCOPE)
				break()
			endif()
		endforeach()
	endif()
endfunction()

#Set compilation flags for the specified target.
#
# Store compilation flags for the specified target and language pair. Flags are
# stored in a global property and will
# be applied to source files in the current directory and sub-directories.
# Property name must follow a specific scheme (see outputs).
# See: _embedded_apply_compile_flags()
#
#Examples:
#  embedded_set_target_compile_flags(my_app C "-fchar-unsigned")
#  embedded_set_target_compile_flags(my_lib CXX "-fchar-unsigned")
#  embedded_set_target_compile_flags(my_app ASM "-fchar-unsigned")
#
#INPUTS:
#   TARGET   - (mandatory) - The target to apply settings to.
#   LANGUAGE - (mandatory) - Programming language of source files settings shall
#							 be applied to.
#	FLAGS    - (mandatory) - List with the compiler flags.
#	APPEND   - (optional)  - True if FLAGS shall be appended.
#
#OUTPUTS
#	Directory property EMBEDDED_COMPILE_FLAGS_TTT_LLL is set, where TTT is the
#	target name, and LLL is the language.
#
function (embedded_set_target_compile_flags)
	set( _OPTIONS_ARGS APPEND)		#Option (on/off) arguments (e.g. IGNORE_CASE)
	set( _ONE_VALUE_ARGS  TARGET LANGUAGE)	#Single option arguments (e.g. PATH "./foo/bar")
	set( _MULTI_VALUE_ARGS FLAGS)		#List arguments (e.g. LANGUAGES C ASM CXX)
	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )

	if (NOT DEFINED _MY_PARAMS_TARGET)
		message(FATAL_ERROR "embedded_set_target_compile_flags(): mandatory parameter 'TARGET' missing.")
	endif()

	if (NOT DEFINED _MY_PARAMS_LANGUAGE)
		message(FATAL_ERROR "embedded_set_target_compile_flags(): mandatory parameter 'LANGUAGE' missing.")
	endif()

	if (NOT DEFINED _MY_PARAMS_FLAGS)
		message(FATAL_ERROR "embedded_set_target_compile_flags(): mandatory parameter 'FLAGS' missing.")
	endif()

	if (_MY_PARAMS_APPEND)
		set_property(GLOBAL APPEND PROPERTY EMBEDDED_COMPILE_FLAGS_${_MY_PARAMS_TARGET}_${_MY_PARAMS_LANGUAGE} ${_MY_PARAMS_FLAGS})
	else()
		set_property(GLOBAL PROPERTY EMBEDDED_COMPILE_FLAGS_${_MY_PARAMS_TARGET}_${_MY_PARAMS_LANGUAGE} ${_MY_PARAMS_FLAGS})
	endif()
endfunction()

#Apply compilation flag settings for the specified target.
#
# Compilation flags stored in a global property and are applied to source files.
# Note:
#	- Directory property name must follow a specific scheme.
#	- This is an internal function.
#	- This function only supports make and ninja generators.
#
# See: embedded_set_target_compile_flags()
#
#Examples:
#  _embedded_apply_compile_flags(my_app)
#
#INPUTS:
#   TARGET  - (mandatory) 			- The target to apply settings to.
#	Directory property - (optional) - Flags to apply.
#
#OUTPUTS
#    n/a
#
function(_embedded_apply_compile_flags TARGET)
	#Check if the parameter is a target.
  	if(NOT TARGET ${TARGET})
		message(FATAL_ERROR "_embedded_apply_compile_flags(): target '${TARGET}' is not defined.")
	endif()
	#Get list of enabled languages.
	get_property(_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
	foreach(_LNG ${_LANGUAGES})
		#Get the flags for this language.
		get_property(_FLAGS GLOBAL PROPERTY EMBEDDED_COMPILE_FLAGS_${TARGET}_${_LNG})

		#The generator expression below is only supported by the make and ninja
		#generators.
		assert_generator_is(_GENERATORS_OK "MSYS Makefiles" "MinGW Makefiles" "NMake Makefiles" "NMake Makefiles JOM" "Unix Makefiles" "Watcom WMake" "Ninja")
		target_compile_options(${TARGET} PRIVATE $<$<COMPILE_LANGUAGE:${_LNG}>:${_FLAGS}>)
	endforeach()
endfunction()

#Set compilation defines for the specified target.
#
# Store compilation defines for the specified target and language pair. Macros
# are stored in a global property and will
# be applied to source files in the current directory and sub-directories.
# Property name must follow a specific scheme (see outputs).
# See: _embedded_apply_compile_defines()
#
#Examples:
#  embedded_set_target_compile_defines(my_app C "FOO BAR=1")
#  embedded_set_target_compile_defines(my_lib CXX "FOO BAR=1")
#  embedded_set_target_compile_defines(my_app ASM "FOO BAR=1")
#
#INPUTS:
#   TARGET   - (mandatory) - The target to apply settings to.
#   LANGUAGE - (mandatory) - Programming language of source files settings shall
#							 be applied to.
#	DEFINES  - (mandatory) - List with the compiler flags.
#	APPEND   - (optional)  - Present if FLAGS shall be appended.
#
#OUTPUTS
#	Directory property EMBEDDED_COMPILE_DEFINES_TTT_LLL is set, where TTT is the
#	target name, and LLL is the language.
#
function (embedded_set_target_compile_defines)
	set( _OPTIONS_ARGS APPEND)		#Option (on/off) arguments (e.g. IGNORE_CASE)
	set( _ONE_VALUE_ARGS  TARGET LANGUAGE)	#Single option arguments (e.g. PATH "./foo/bar")
	set( _MULTI_VALUE_ARGS DEFINES)		#List arguments (e.g. LANGUAGES C ASM CXX)
	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )

	if (NOT DEFINED _MY_PARAMS_TARGET)
		message(FATAL_ERROR "embedded_set_target_compile_defines(): mandatory parameter 'TARGET' missing.")
	endif()

	if (NOT DEFINED _MY_PARAMS_LANGUAGE)
		message(FATAL_ERROR "embedded_set_target_compile_defines(): mandatory parameter 'LANGUAGE' missing.")
	endif()

	if (NOT DEFINED _MY_PARAMS_DEFINES)
		message(FATAL_ERROR "embedded_set_target_compile_defines(): mandatory parameter 'DEFINES' missing.")
	endif()

	if (_MY_PARAMS_APPEND)
		set_property(GLOBAL APPEND PROPERTY EMBEDDED_COMPILE_DEFINES_${_MY_PARAMS_TARGET}_${_MY_PARAMS_LANGUAGE} ${_MY_PARAMS_DEFINES})
	else()
		set_property(GLOBAL PROPERTY EMBEDDED_COMPILE_DEFINES_${_MY_PARAMS_TARGET}_${_MY_PARAMS_LANGUAGE} ${_MY_PARAMS_DEFINES})
	endif()
endfunction()

#Apply compilation defines for the specified target.
#
# Macro definitions are stored in a global property and are applied to
# source files.
#
# Note:
#	- Directory property name must follow a specific scheme.
#	- This is an internal function.
#	- This function only supports make and ninja generators.
#
# See: embedded_set_target_compile_defines()
#
#Examples:
#  _embedded_apply_compile_defines(my_app)
#
#INPUTS:
#   TARGET  - (mandatory) 			- The target to apply settings to.
#	Directory property - (optional) - Flags to apply.
#
#OUTPUTS
#    n/a
#
function(_embedded_apply_compile_defines TARGET)
	#Check if the parameter is a target.
  	if(NOT TARGET ${TARGET})
		message(FATAL_ERROR "_embedded_apply_compile_defines(): target '${TARGET}' is not defined.")
	endif()
	#Get list of enabled languages.
	get_property(_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
	foreach(_LNG ${_LANGUAGES})
		#Get the flags for this language.
		get_property(_FLAGS GLOBAL PROPERTY EMBEDDED_COMPILE_DEFINES_${TARGET}_${_LNG})
		#The generator expression below is only supported by the make and ninja
		#generators.
		assert_generator_is(_GENERATORS_OK "MSYS Makefiles" "MinGW Makefiles" "NMake Makefiles" "NMake Makefiles JOM" "Unix Makefiles" "Watcom WMake" "Ninja")
		target_compile_definitions(${TARGET} PRIVATE $<$<COMPILE_LANGUAGE:${_LNG}>:${_FLAGS}>)
	endforeach()
endfunction()

#Specify an include path for the compiler affecting a specific build target (all
# languages).
#
# Store include paths for the specified target. PATH is stored in a global
# property. The property name must follow a specific scheme (see outputs).
# See: _embedded_apply_include_directories()
#
#Examples:
#  embedded_target_include_directories(TARGET foo PATH "C:/fo/bar/include")
#  embedded_target_include_directories(TARGET foo PATH "C:/fo/bar/include" APPEND)
#  embedded_target_include_directories(TARGET foo PATH "C:/fo/bar/include" ABSOLUTE)
#
#INPUTS:
#    ABSOLUTE - (optional)- whether the path shall be converted to absolute
#	 APPEND - (optional)  - if set append path to existing values
#    PATH  - (mandatory)  - the include path to add
#	 TARGET - (mandatory) - name of target to apply settings to
#
#OUTPUTS
#	Directory property EMBEDDED_COMPILE_INCLUDES_TTT is set, where TTT is
#	the target name.
#
function (embedded_target_include_directories)
	#Parse our arguments
	set( _OPTIONS_ARGS ABSOLUTE APPEND)	#Option (on/off) arguments (e.g. IGNORE_CASE)
	set( _ONE_VALUE_ARGS  PATH TARGET) #Single option arguments (e.g. PATH "./foo/bar")
	set( _MULTI_VALUE_ARGS )		#List arguments (e.g. LANGUAGES C ASM CXX)
	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )

	#Check mandatory parameters
	if(NOT _MY_PARAMS_PATH)
		failure("embedded_target_include_directories(): Missing PATH parameter!")
	endif()

	if(NOT _MY_PARAMS_TARGET)
		failure("embedded_target_include_directories(): Missing TARGET parameter!")
	endif()

	if(DEFINED _MY_PARAMS_ABSOLUTE AND ${_MY_PARAMS_ABSOLUTE})
		get_filename_component(_MY_PARAMS_PATH ${_MY_PARAMS_PATH} ABSOLUTE)
	endif()

	if (_MY_PARAMS_APPEND)
		set_property(GLOBAL APPEND PROPERTY EMBEDDED_COMPILE_INCLUDES_${_MY_PARAMS_TARGET} ${_MY_PARAMS_PATH})
	else()
		set_property(GLOBAL PROPERTY EMBEDDED_COMPILE_INCLUDES_${_MY_PARAMS_TARGET} ${_MY_PARAMS_PATH})
	endif()
endfunction()

#Apply include path settings for the specified target.
#
# Include paths are stored in a global property and are applied to source files.
# Note:
#	- Directory property name must follow a specific scheme.
#	- This is an internal function.
#
# See: embedded_target_include_directories()
#
#Examples:
#  _embedded_apply_include_directories(my_app)
#
#INPUTS:
#   TARGET  - (mandatory) 			- The target to apply settings to.
#	Directory property - (optional) - Flags to apply.
#
#OUTPUTS
#    n/a
#
function(_embedded_apply_include_directories TARGET)
	#Check if the parameter is a target.
  	if(NOT TARGET ${TARGET})
		message(FATAL_ERROR "_embedded_apply_include_directories(): target '${TARGET}' is not defined.")
	endif()
	#Get the flags for this language.
	get_property(_FLAGS GLOBAL PROPERTY EMBEDDED_COMPILE_INCLUDES_${TARGET})
	#If we have flags to apply for this language.
	if (NOT _FLAGS STREQUAL "")
		target_include_directories(${TARGET} PRIVATE ${_FLAGS})
	endif()
endfunction()

#Set linker flags for the specified target.
#
# Store linker flags for the specified target in a global property.
# See: _embedded_apply_link_flags()
#
#Examples:
#  embedded_set_target_link_flags(my_app "-M my_map_file.map")
#
#INPUTS:
#   TARGET  - (mandatory) - The target to apply settings to.
#	FLAGS   - (mandatory) - List with the compiler flags.
#	APPEND  - (optional)  - True if FLAGS shall be appended.
#
#OUTPUTS
#	Directory property EMBEDDED_LINKER_FLAGS_TTT is set, where TTT is the
#	target name.
#
function(embedded_set_target_link_flags)
	set( _OPTIONS_ARGS APPEND)		#Option (on/off) arguments (e.g. IGNORE_CASE)
	set( _ONE_VALUE_ARGS  TARGET)	#Single option arguments (e.g. PATH "./foo/bar")
	set( _MULTI_VALUE_ARGS FLAGS)		#List arguments (e.g. LANGUAGES C ASM CXX)
	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )

	if (NOT DEFINED _MY_PARAMS_TARGET)
		message(FATAL_ERROR "embedded_set_target_link_flags(): mandatory parameter 'TARGET' missing.")
	endif()

	if (NOT DEFINED _MY_PARAMS_FLAGS)
		message(FATAL_ERROR "embedded_set_target_link_flags(): mandatory parameter 'FLAGS' missing.")
	endif()

	if (_MY_PARAMS_APPEND)
		set_property(GLOBAL APPEND PROPERTY EMBEDDED_LINKER_FLAGS_${_MY_PARAMS_TARGET} ${_MY_PARAMS_FLAGS})
	else()
		set_property(GLOBAL PROPERTY EMBEDDED_LINKER_FLAGS_${_MY_PARAMS_TARGET} ${_MY_PARAMS_FLAGS})
	endif()
endfunction()

#Apply linker flags for the specified target.
#
# Linker flags stored in a global property are applied.
#
# Note:
#	- Directory property name must follow a specific scheme.
#	- This is an internal function.
#
# See: embedded_set_target_link_flags()
#
#Examples:
#  _embedded_apply_link_flags(my_app)
#
#INPUTS:
#   TARGET  - (mandatory) 			- The target to apply settings to.
#	Directory property - (optional) - Flags to apply.
#
#OUTPUTS
#    n/a
#
function(_embedded_apply_link_flags TARGET)
	#Check if the parameter is a target.
	if(NOT TARGET ${TARGET})
		message(FATAL_ERROR "_embedded_apply_link_flags(): target '${TARGET}' is not defined.")
	endif()
	#Get the stored flags.
	get_property(_FLAGS GLOBAL PROPERTY EMBEDDED_LINKER_FLAGS_${TARGET})
	#Apply flags if defined.
	if (NOT _FLAGS STREQUAL "")
		list_to_string(_STR_FLAGS ${_FLAGS})
		set_property(TARGET ${TARGET} APPEND_STRING PROPERTY LINK_FLAGS  ${_STR_FLAGS})
	endif()
endfunction()

#Set linker command file for the specified target.
#
# Store path to linker command file for the specified target in a global
# property.
#
# See: _embedded_apply_linker_cmd_file_setting()
#
#Examples:
#  embedded_set_target_linker_file(TARGET my_app PATH "foo/my_linker_cmd.sct")
#
#INPUTS:
#  TARGET  - (mandatory) - The target to apply settings to.
#  PATH - (mandatory)    - Path to linker script.
#
#OUTPUTS
#  Directory property EMBEDDED_LINKER_CMD_FILE_TTT is set, where TTT is the
#  target name.
#
function(embedded_set_target_linker_file)
	set( _OPTIONS_ARGS )				#Option (on/off) arguments (e.g. IGNORE_CASE)
	set( _ONE_VALUE_ARGS  TARGET PATH)	#Single option arguments (e.g. PATH "./foo/bar")
	set( _MULTI_VALUE_ARGS )			#List arguments (e.g. LANGUAGES C ASM CXX)
	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )

	if (NOT DEFINED _MY_PARAMS_TARGET)
		message(FATAL_ERROR "embedded_set_target_linker_file(): mandatory parameter 'TARGET' missing.")
	endif()

	if (NOT DEFINED _MY_PARAMS_PATH)
		message(FATAL_ERROR "embedded_set_target_linker_file(): mandatory parameter 'PATH' missing.")
	endif()

	set_property(GLOBAL PROPERTY EMBEDDED_LINKER_CMD_FILE_${_MY_PARAMS_TARGET} ${_MY_PARAMS_PATH})
endfunction()

#Set pre-processor defines for the linker command file.
#
# Store preprocessor defines for the linker command file of the specified target
# in a global property.
#
# See: _embedded_apply_linker_cmd_file_setting()
#
#Examples:
#  embedded_set_target_link_defines(my_app "BL2=1" "USE_TLS=1")
#
#INPUTS:
#   TARGET  - (mandatory) - The target to apply settings to.
#   DEFINES - (mandatory) - List of macro value definitions.
#
#OUTPUTS
#   Directory property EMBEDDED_LINKER_DEFINES_TTT is set, where TTT is the
#   target name.
#
function(embedded_set_target_link_defines)
	set( _OPTIONS_ARGS )				#Option (on/off) arguments (e.g. IGNORE_CASE)
	set( _ONE_VALUE_ARGS  TARGET DEFINES)	#Single option arguments (e.g. PATH "./foo/bar")
	set( _MULTI_VALUE_ARGS )			#List arguments (e.g. LANGUAGES C ASM CXX)
	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )

	if (NOT DEFINED _MY_PARAMS_TARGET)
		message(FATAL_ERROR "embedded_set_target_link_defines(): mandatory parameter 'TARGET' missing.")
	endif()

	if (NOT DEFINED _MY_PARAMS_DEFINES)
		message(FATAL_ERROR "embedded_set_target_link_defines(): mandatory parameter 'DEFINES' missing.")
	endif()

	set_property(GLOBAL APPEND PROPERTY EMBEDDED_LINKER_DEFINES_${_MY_PARAMS_TARGET} ${_MY_PARAMS_DEFINES})
endfunction()


#Set pre-processor include paths for the linker command file.
#
# Store preprocessor include paths for the linker command file of the specified
# target in a global property.
#
# See: _embedded_apply_linker_cmd_file_setting()
#
#Examples:
#  embedded_set_target_link_includes(my_app "c:/foo" "../bar")
#
#INPUTS:
#   TARGET  - (mandatory)  - The target to apply settings to.
#   INCLUDES - (mandatory) - List of include paths.
#
#OUTPUTS
#   Directory property EMBEDDED_LINKER_INCLUDES_TTT is set, where TTT is the
#   target name.
#
function(embedded_set_target_link_includes)
	set( _OPTIONS_ARGS )				#Option (on/off) arguments (e.g. IGNORE_CASE)
	set( _ONE_VALUE_ARGS  TARGET)	#Single option arguments (e.g. PATH "./foo/bar")
	set( _MULTI_VALUE_ARGS INCLUDES)			#List arguments (e.g. LANGUAGES C ASM CXX)
	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )

	if (NOT DEFINED _MY_PARAMS_TARGET)
		message(FATAL_ERROR "embedded_set_target_link_includes(): mandatory parameter 'TARGET' missing.")
	endif()

	if (NOT DEFINED _MY_PARAMS_INCLUDES)
		message(FATAL_ERROR "embedded_set_target_link_includes(): mandatory parameter 'INCLUDES' missing.")
	endif()

	set_property(GLOBAL APPEND PROPERTY EMBEDDED_LINKER_INCLUDES_${_MY_PARAMS_TARGET} ${_MY_PARAMS_INCLUDES})
endfunction()


#Apply linker linker command file setting for the specified target.
#
# Path to linker command file, macro definitions and include paths stored in
# global properties are applied.
#
# Note:
#  - Directory property names must follow a specific scheme.
#  - This is an internal function.
#
# See: embedded_set_target_linker_file(), embedded_set_target_link_includes()
#      embedded_set_target_link_defines()
#
#Examples:
#  _embedded_apply_linker_cmd_file_setting(my_app)
#
#INPUTS:
#   TARGET  - (mandatory) - The target to apply settings to.
#   Directory properties
#
#OUTPUTS
#    n/a
#
function(_embedded_apply_linker_cmd_file_setting TARGET)
	#Check if the parameter is a target.
	if(NOT TARGET ${TARGET})
		message(FATAL_ERROR "_embedded_apply_linker_cmd_file_setting(): target '${TARGET}' is not defined.")
	endif()
	#Check if target is an executable.
	get_property(_TGT_TYPE TARGET ${TARGET} PROPERTY TYPE)
	if(NOT _TGT_TYPE STREQUAL "EXECUTABLE")
		message(FATAL_ERROR "_embedded_apply_linker_cmd_file_setting(): target '${TARGET}' is not an executable.")
	endif()

	#Check if executable has a linker command file set.
	get_property(_LINKER_CMD_FILE GLOBAL PROPERTY EMBEDDED_LINKER_CMD_FILE_${TARGET} SET)
	if (NOT _LINKER_CMD_FILE)
		message(FATAL_ERROR "_embedded_apply_linker_cmd_file_setting(): Please set linker command file for target '${TARGET}' using embedded_set_target_linker_file().")
	endif()
	#Get the path to the linker command file.
	get_property(_LINKER_CMD_FILE GLOBAL PROPERTY EMBEDDED_LINKER_CMD_FILE_${TARGET})

	#Get macro defines and include paths set for the target.
	get_property(_LINKER_DEFINES GLOBAL PROPERTY EMBEDDED_LINKER_DEFINES_${TARGET})
	get_property(_LINKER_INCLUDES GLOBAL PROPERTY EMBEDDED_LINKER_INCLUDES_${TARGET})
	compiler_set_linkercmdfile(TARGET ${TARGET} PATH ${_LINKER_CMD_FILE} DEFINES ${_LINKER_DEFINES} INCLUDES ${_LINKER_INCLUDES})
endfunction()
