Enhance libgcc location discovery in cmake

List of changes:

1: The GCC binary built by Yocto needs a --sysroot parameter passed in
to be able to find it's libraries and related include paths. This change
introduces LIBGCC_LOCATE_CFLAGS for this purpose.

2: All libgcc discovery related build options LIBGCC_LOCATE_CFLAGS,
LIBGCC_PATH and LIBGCC_INCLUDE_DIRS are allowed to be set on the command
line with -D<variable>=<value> or in the environment (command line takes
precedence).

3: The interface of GCC.cmake was refactored to stop bleeding compiler
specifics to users of the interface:
  - moved GCC specific libgcc header copying from newlib.cmake to
    GCC.cmake
  - all "freestanding" mode related settings moved to GCC.cmake behind
    a single function call to hide compiler specific internals.
  - "freestanding" mode related settings are propagated project wide
    trough interface options of the std::c target.

4: Coding style issues fixed in modified cmake files.

Signed-off-by: Mark Dykes <mark.dykes@arm.com>
Signed-off-by: Gyorgy Szing <gyorgy.szing@arm.com>
Change-Id: Ic81b18fecd018b4a67d90a096c62b168a18167bb
diff --git a/tools/cmake/compiler/GCC.cmake b/tools/cmake/compiler/GCC.cmake
index 5a8fa59..77eff9a 100644
--- a/tools/cmake/compiler/GCC.cmake
+++ b/tools/cmake/compiler/GCC.cmake
@@ -16,12 +16,32 @@
 	The variable can be set on the command line with ``-DCROSS_COMPILE=<value>`` or in the
 	environment. If both is specified, command line takes precedence.
 
+.. cmake:variable:: LIBGCC_PATH
+
+	An absolute path to specify the location of the gcc specific library.  The name
+	of the library is libgcc.a.  Note that it must be the full path with library name.
+	The variable can be set on the command line with ``-DLIBGCC_PATH=<value>`` or in the
+	environment. If both is specified, command line takes precedence.
+
+.. cmake:variable:: LIBGCC_INCLUDE_DIRS
+
+	A semicolon separated list of absolute paths to specify the location of gcc specific header
+	files. The variable can be set on the command line with ``-DLIBGCC_INCLUDE_DIRS=<value>``
+	or in the environment. If both is specified, command line takes precedence.
+
+.. cmake:variable:: LIBGCC_LOCATE_CFLAGS
+
+	The compiler options used when searching for the gcc library (libgcc.a).
+	Setting the value is optional.
+	The variable can be set on the command line with ``-DLIBGCC_LOCATE_CFLAGS=<value>`` or
+	in the environment.
+
 #]===]
 
 include_guard(DIRECTORY)
 
 if(NOT CROSS_COMPILE AND NOT DEFINED ENV{CROSS_COMPILE})
-	message(FATAL_ERROR "'CROSS_COMPILE' is not defined. Set it to the gcc pferix triplet, ie. cmake <..>-DCROSS_COMPILE=aarch64-elf-")
+	message(FATAL_ERROR "'CROSS_COMPILE' is not defined. Set it to the gcc prefix triplet, ie. cmake <..>-DCROSS_COMPILE=aarch64-elf-")
 endif()
 
 set(CROSS_COMPILE $ENV{CROSS_COMPILE} CACHE STRING "Prefix of the cross-compiler commands")
@@ -43,7 +63,7 @@
 #Official solution to disable compiler checks
 set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
 
-#By default when INTERFACE_INCUDES of libraryes linked to an exe are treated
+#By default when INTERFACE_INCUDES of libraries linked to an exe are treated
 #as system includes. gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf (gcc 8.2.1) will
 #set C linkage o these files, which will result in compilation errors for C++ projects.
 #This setting fixes that.
@@ -54,9 +74,9 @@
 
   .. code-block:: cmake
 
-    compiler_preprocess_file(SRC file.c DST file_pp.c)
-    compiler_preprocess_file(SRC file.c DST file_pp.c
-                             DEFINES USE_LIB INCLUDES include/lib)
+	compiler_preprocess_file(SRC file.c DST file_pp.c)
+	compiler_preprocess_file(SRC file.c DST file_pp.c
+							 DEFINES USE_LIB INCLUDES include/lib)
 
   Run the preprocessor on a file and save the output to another file. Optionally
   provide defines and include paths to the preprocessor.
@@ -64,16 +84,16 @@
   Inputs:
 
   ``SRC``
-    Name of the source file to preprocess.
+	Name of the source file to preprocess.
 
   ``DST``
-    Where to write the preprocessed output.
+	Where to write the preprocessed output.
 
   ``DEFINES`` (multi, optional)
-    Definitions for the preprocessor.
+	Definitions for the preprocessor.
 
   ``INCLUDES`` (multi, optional)
-    Include paths for the preprocessor.
+	Include paths for the preprocessor.
 
 #]===]
 function(compiler_preprocess_file)
@@ -106,8 +126,8 @@
 
   .. code-block:: cmake
 
-    compiler_set_linker_script(TARGET foo FILE foo.ld.S)
-    compiler_set_linker_script(TARGET foo FILE foo.ld.S DEF USE_LIB INC include/lib)
+	compiler_set_linker_script(TARGET foo FILE foo.ld.S)
+	compiler_set_linker_script(TARGET foo FILE foo.ld.S DEF USE_LIB INC include/lib)
 
   Set linker script for a target. The function adds an LDFLAG using the
   toolchain specific syntax to the TARGET_linker_script group, which is applied
@@ -117,16 +137,16 @@
   Inputs:
 
   ``TARGET``
-    Name of the target.
+	Name of the target.
 
   ``FILE``
-    Linker script file for the target.
+	Linker script file for the target.
 
   ``DEF`` (multi, optional)
-    Defines for the linker script preprocessor.
+	Defines for the linker script preprocessor.
 
   ``INC`` (multi, optional)
-    Include paths for the linker script preprocessor.
+	Include paths for the linker script preprocessor.
 
 #]===]
 function(compiler_set_linker_script)
@@ -164,7 +184,7 @@
 
   .. code-block:: cmake
 
-    compiler_generate_binary_output(TARGET <name> RES <var>)
+	compiler_generate_binary_output(TARGET <name> RES <var>)
 
   Generate binary output for the target. The function converts the output
   executable into bin file using toolchain specific syntax.
@@ -172,12 +192,12 @@
   Inputs:
 
   ``TARGET``
-    Name of the target.
+	Name of the target.
 
   Outputs:
 
   ``RES``
-    Full patch to output file.
+	Full patch to output file.
 
 #]===]
 function(compiler_generate_binary_output)
@@ -202,7 +222,7 @@
 
   .. code-block:: cmake
 
-    compiler_generate_stripped_elf(TARGET foo NAME foo.stripped.elf RES var)
+	compiler_generate_stripped_elf(TARGET foo NAME foo.stripped.elf RES var)
 
   Strip all symbols that are not needed for relocation processing and return the location
   of the result.
@@ -210,15 +230,15 @@
   Inputs:
 
   ``TARGET``
-    Name of the target.
+	Name of the target.
 
   ``NAME``
-    Name of output file
+	Name of output file
 
   Outputs:
 
   ``RES``
-    Name of variable to store the full path of the stripped executable.
+	Name of variable to store the full path of the stripped executable.
 
 #]===]
 
@@ -244,21 +264,23 @@
 
   .. code-block:: cmake
 
-    gcc_get_lib_location(TARGET foo NAME foo.stripped.elf RES var)
+	gcc_get_lib_location(TARGET foo NAME foo.stripped.elf RES var)
 
   Query the location of a specific library part of the GCC binary release. Can
-  be used to find built in libraryes like libgcc.a when i.w. -nostdlib option
+  be used to find built in libraries like libgcc.a when i.w. -nostdlib option
   is used.
 
+  The function uses the :variable:`LIBGCC_LOCATE_CFLAGS`.
+
   Inputs:
 
   ``LIBRARY_NAME``
-    Name of the library to search for.
+	Name of the library to search for.
 
   Outputs:
 
   ``RES``
-    Name of variable to store the full path of the library.
+	Name of variable to store the full path of the library.
 
 #]===]
 function(gcc_get_lib_location)
@@ -267,8 +289,13 @@
 	set(multiValueArgs)
 	cmake_parse_arguments(MY "${options}" "${oneValueArgs}"
 						"${multiValueArgs}" ${ARGN} )
+
+	if (DEFINED ENV{LIBGCC_LOCATE_CFLAGS})
+		set(LIBGCC_LOCATE_CFLAGS $ENV{LIBGCC_LOCATE_CFLAGS} CACHE STRING "GCC library search options" )
+	endif()
+
 	execute_process(
-		COMMAND ${CMAKE_C_COMPILER} "--print-file-name=${MY_LIBRARY_NAME}"
+		COMMAND ${CMAKE_C_COMPILER} ${LIBGCC_LOCATE_CFLAGS} "--print-file-name=${MY_LIBRARY_NAME}"
 		OUTPUT_VARIABLE _RES
 		RESULT_VARIABLE _GCC_ERROR_CODE
 		OUTPUT_STRIP_TRAILING_WHITESPACE
@@ -286,3 +313,142 @@
 
 	set(${MY_RES} ${_RES} PARENT_SCOPE)
 endfunction()
+
+
+#[===[.rst:
+.. cmake:command:: compiler_set_freestanding
+
+  .. code-block:: cmake
+
+	compiler_set_freestanding(TARGET foo)
+
+  	Configure the target specified for "freestanging" compilation mode. Please see [1] for more information.
+	This will configure the target:
+	  - to block access to the "built in" standard library and its headers
+	  - link compiler specific libraries
+	  - add include paths to compiler specific headers
+
+	All settings will be PUBLIC or INTERFACE (for imported targets) and thus will take effect on targets
+	depending on the configured one.
+
+	The function uses and manipulates the following CACHE variables:
+	  - :variable:`LIBGCC_PATH`
+	  - :variable:`LIBGCC_INCLUDE_DIRS`
+
+	CMake has a spacial behavior which needs a workaround. CMake is automatically filtering out built in compiler
+	include paths from the compiler command line.
+	As a workaround, compiler specific headers are copied to the build directory and set include path to the new
+	location.
+
+	Limitations:
+	  - Inheritance of target settings may be problematic. Compiling components in "freestanding" mode may put
+	    restrictions on reusing these. When such components are installed, the "freestanding" nature shall be
+		propagated to dependencies. This is not tested or implemented.
+
+	1: https://wiki.osdev.org/Implications_of_writing_a_freestanding_C_project
+	2: https://gitlab.kitware.com/cmake/cmake/-/issues/19227
+
+  Inputs:
+
+  ``TARGET``
+	Name of the target to configure.
+
+  Outputs:
+
+  N/A
+
+#]===]
+function(compiler_set_freestanding)
+	set(options)
+	set(oneValueArgs TARGET)
+	set(multiValueArgs)
+	cmake_parse_arguments(MY "${options}" "${oneValueArgs}"
+						"${multiValueArgs}" ${ARGN} )
+
+	# Validate parameters
+	if (NOT DEFINED MY_TARGET)
+		message(FATAL_ERROR "Mandatory parameter TARGET is missing!")
+	endif()
+
+	# Set INTERFACE options for imported targets and PUBLIC otherwise.
+	get_property(_is_imported_target TARGET ${MY_TARGET} PROPERTY IMPORTED SET)
+	if (_is_imported_target)
+		set(_option_type INTERFACE)
+	else()
+		set(_option_type PUBLIC)
+	endif()
+
+	### Get the location of libgcc.a
+	# Copy values from environment if present. Note: if the value is already in the CACHE, this set will have no effect.
+	if(DEFINED ENV{LIBGCC_PATH})
+		set(LIBGCC_PATH $ENV{LIBGCC_PATH} CACHE PATH "location of libgcc.a")
+	endif()
+	if (NOT DEFINED LIBGCC_PATH)
+		gcc_get_lib_location(LIBRARY_NAME "libgcc.a" RES _TMP_VAR)
+
+		if (NOT _TMP_VAR)
+			message(FATAL_ERROR "Location of libgcc.a can not be determined. Please set LIBGCC_PATH on the command"
+					" line or in the environment.")
+		endif()
+		set(LIBGCC_PATH ${_TMP_VAR} CACHE PATH "location of libgcc.a")
+		unset(_TMP_VAR)
+	endif()
+
+	# Validate LIBGCC_PATH
+	if(NOT EXISTS "${LIBGCC_PATH}" OR IS_DIRECTORY "${LIBGCC_PATH}")
+		message(FATAL_ERROR "LIBGCC_PATH \"${LIBGCC_PATH}\" must be the full path of a library file."
+							" Either set LIBGCC_PATH on the command line (or in the environment), or fix the existing"
+							" value.")
+	endif()
+	message(STATUS "libgcc.a for target \"${MY_TARGET}\" is used from ${LIBGCC_PATH}")
+
+	### Get the location of libgcc specific header files.
+	# Copy values from environment if present. Note: if the value is already in the CACHE, this set will have no effect.
+	if(DEFINED ENV{LIBGCC_INCLUDE_DIRS})
+		set(LIBGCC_INCLUDE_DIRS $ENV{LIBGCC_INCLUDE_DIRS} CACHE STRING "GCC specific include PATHs")
+	endif()
+	if(NOT DEFINED LIBGCC_INCLUDE_DIRS)
+		# We can get the correct path if we ask for a location without a library name
+		gcc_get_lib_location(LIBRARY_NAME "" RES _TMP_VAR)
+
+		if (NOT _TMP_VAR)
+			message(FATAL_ERROR "Location of GCC specific include PATHs can not be determined. Please set"
+					" LIBGCC_INCLUDE_DIRS on the command line or in the environment.")
+		endif()
+
+		set(LIBGCC_INCLUDE_DIRS
+			"${_TMP_VAR}/include"
+			"${_TMP_VAR}/include-fixed" CACHE STRING "GCC specific include PATHs")
+		unset(_TMP_VAR)
+	endif()
+
+	# There is no way to stop cmake from filtering out built in compiler include paths
+	# from compiler command line (see https://gitlab.kitware.com/cmake/cmake/-/issues/19227).
+	# As a workaround copy headers to build directory and set include path to the new
+	# location.
+	# Also validate locations.
+	if (NOT GCC_INCLUDES_MOVED)
+		foreach(_dir IN LISTS LIBGCC_INCLUDE_DIRS)
+			if(NOT IS_DIRECTORY "${_dir}")
+				message(FATAL_ERROR "GCC specific include PATH \"${_dir}\" does not exist.")
+			endif()
+
+			file(COPY "${_dir}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gcc-include")
+			get_filename_component(_TMP_VAR "${_dir}" NAME)
+			list(APPEND _gcc_include_dirs "${CMAKE_CURRENT_BINARY_DIR}/gcc-include/${_TMP_VAR}")
+			message(STATUS "Compiler specific include path \"${_dir}\" mirrored to"
+							"  \"${CMAKE_CURRENT_BINARY_DIR}/gcc-include/${_TMP_VAR}\".")
+		endforeach()
+		unset(_TMP_VAR)
+		set(GCC_INCLUDES_MOVED True CACHE BOOL "GCC include files are already copied.")
+		mark_as_advanced(GCC_INCLUDES_MOVED)
+		# Fix the variable in the CACHE.
+		set(LIBGCC_INCLUDE_DIRS ${_gcc_include_dirs} CACHE STRING "GCC specific include PATHs" FORCE)
+	endif()
+
+	# Configure the target for freestanding mode.
+	target_compile_options(${MY_TARGET} ${_option_type} "-nostdinc")
+	target_include_directories(${MY_TARGET} SYSTEM ${_option_type} ${LIBGCC_INCLUDE_DIRS})
+	target_link_options(${MY_TARGET} ${_option_type} "-nostdlib" "-nostartfiles")
+	target_link_libraries(${MY_TARGET} ${_option_type} "${LIBGCC_PATH}")
+endfunction()