blob: 77eff9affca16aa404b4635bf6e890a38d5ae893 [file] [log] [blame]
Gyorgy Szing49091802020-11-24 00:33:09 +01001#-------------------------------------------------------------------------------
Gyorgy Szing737a0bc2022-03-02 21:06:00 +00002# Copyright (c) 2019-2022, Arm Limited and Contributors. All rights reserved.
Gyorgy Szing49091802020-11-24 00:33:09 +01003#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6#-------------------------------------------------------------------------------
7
8#[===[.rst:
9Compiler abstraction for GCC
10----------------------------
11
12.. cmake:variable:: CROSS_COMPILE
13
14 A ';' separated GCC prefix triplets to use when searching for the cross-compiler.
15 (i.e. ``aarch64-none-elf;aarch64-elf``).
16 The variable can be set on the command line with ``-DCROSS_COMPILE=<value>`` or in the
17 environment. If both is specified, command line takes precedence.
18
Mark Dykese9db8172022-07-21 14:10:00 -050019.. cmake:variable:: LIBGCC_PATH
20
21 An absolute path to specify the location of the gcc specific library. The name
22 of the library is libgcc.a. Note that it must be the full path with library name.
23 The variable can be set on the command line with ``-DLIBGCC_PATH=<value>`` or in the
24 environment. If both is specified, command line takes precedence.
25
26.. cmake:variable:: LIBGCC_INCLUDE_DIRS
27
28 A semicolon separated list of absolute paths to specify the location of gcc specific header
29 files. The variable can be set on the command line with ``-DLIBGCC_INCLUDE_DIRS=<value>``
30 or in the environment. If both is specified, command line takes precedence.
31
32.. cmake:variable:: LIBGCC_LOCATE_CFLAGS
33
34 The compiler options used when searching for the gcc library (libgcc.a).
35 Setting the value is optional.
36 The variable can be set on the command line with ``-DLIBGCC_LOCATE_CFLAGS=<value>`` or
37 in the environment.
38
Gyorgy Szing49091802020-11-24 00:33:09 +010039#]===]
40
41include_guard(DIRECTORY)
42
43if(NOT CROSS_COMPILE AND NOT DEFINED ENV{CROSS_COMPILE})
Mark Dykese9db8172022-07-21 14:10:00 -050044 message(FATAL_ERROR "'CROSS_COMPILE' is not defined. Set it to the gcc prefix triplet, ie. cmake <..>-DCROSS_COMPILE=aarch64-elf-")
Gyorgy Szing49091802020-11-24 00:33:09 +010045endif()
46
47set(CROSS_COMPILE $ENV{CROSS_COMPILE} CACHE STRING "Prefix of the cross-compiler commands")
48
49#Generate a list of tool names to look for. Store the result in CMAKE_<lang>_COMPILER.
50function(gcc_find_tool NAME LANG)
51 string(REGEX REPLACE "([^;]+);" "\\1${NAME};\\1${NAME}.exe;" _gcc_names "${CROSS_COMPILE};")
52 find_program(_cross_compile_gcc NAMES ${_gcc_names} REQUIRED)
53 if (NOT _cross_compile_gcc)
54 string(REPLACE ";" " " _msg "${_gcc_names}")
55 message(FATAL_ERROR "Failed to find ${NAME} with the names: ${_msg}")
56 endif()
57 set(CMAKE_${LANG}_COMPILER ${_cross_compile_gcc} CACHE STRING "${LANG} compiler executable.")
58endfunction()
59
60gcc_find_tool(gcc C)
61gcc_find_tool(g++ CXX)
62
63#Official solution to disable compiler checks
64set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
65
Mark Dykese9db8172022-07-21 14:10:00 -050066#By default when INTERFACE_INCUDES of libraries linked to an exe are treated
Gyorgy Szing49091802020-11-24 00:33:09 +010067#as system includes. gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf (gcc 8.2.1) will
68#set C linkage o these files, which will result in compilation errors for C++ projects.
69#This setting fixes that.
70set(CMAKE_NO_SYSTEM_FROM_IMPORTED True)
71
72#[===[.rst:
73.. cmake:command:: compiler_preprocess_file
74
75 .. code-block:: cmake
76
Mark Dykese9db8172022-07-21 14:10:00 -050077 compiler_preprocess_file(SRC file.c DST file_pp.c)
78 compiler_preprocess_file(SRC file.c DST file_pp.c
79 DEFINES USE_LIB INCLUDES include/lib)
Gyorgy Szing49091802020-11-24 00:33:09 +010080
81 Run the preprocessor on a file and save the output to another file. Optionally
82 provide defines and include paths to the preprocessor.
83
84 Inputs:
85
86 ``SRC``
Mark Dykese9db8172022-07-21 14:10:00 -050087 Name of the source file to preprocess.
Gyorgy Szing49091802020-11-24 00:33:09 +010088
89 ``DST``
Mark Dykese9db8172022-07-21 14:10:00 -050090 Where to write the preprocessed output.
Gyorgy Szing49091802020-11-24 00:33:09 +010091
92 ``DEFINES`` (multi, optional)
Mark Dykese9db8172022-07-21 14:10:00 -050093 Definitions for the preprocessor.
Gyorgy Szing49091802020-11-24 00:33:09 +010094
95 ``INCLUDES`` (multi, optional)
Mark Dykese9db8172022-07-21 14:10:00 -050096 Include paths for the preprocessor.
Gyorgy Szing49091802020-11-24 00:33:09 +010097
98#]===]
99function(compiler_preprocess_file)
100 set(_OPTIONS_ARGS)
101 set(_ONE_VALUE_ARGS SRC DST)
102 set(_MULTI_VALUE_ARGS DEFINES INCLUDES)
103 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
104
105 check_args(compiler_preprocess_file SRC DST)
106
107 set(_flags "")
108 if(_MY_PARAMS_DEFINES)
109 list(TRANSFORM _MY_PARAMS_DEFINES PREPEND -D)
110 list(APPEND _flags ${_MY_PARAMS_DEFINES})
111 endif()
112 if(_MY_PARAMS_INCLUDES)
113 list(TRANSFORM _MY_PARAMS_INCLUDES PREPEND -I)
114 list(APPEND _flags ${_MY_PARAMS_INCLUDES})
115 endif()
116
117 add_custom_command(
118 DEPENDS ${_MY_PARAMS_SRC} OUTPUT ${_MY_PARAMS_DST}
119 COMMAND ${CMAKE_C_COMPILER} -E -P -x assembler-with-cpp ${_flags}
120 ${_MY_PARAMS_SRC} -o ${_MY_PARAMS_DST}
121 )
122endfunction()
123
124#[===[.rst:
125.. cmake:command:: compiler_set_linker_script
126
127 .. code-block:: cmake
128
Mark Dykese9db8172022-07-21 14:10:00 -0500129 compiler_set_linker_script(TARGET foo FILE foo.ld.S)
130 compiler_set_linker_script(TARGET foo FILE foo.ld.S DEF USE_LIB INC include/lib)
Gyorgy Szing49091802020-11-24 00:33:09 +0100131
132 Set linker script for a target. The function adds an LDFLAG using the
133 toolchain specific syntax to the TARGET_linker_script group, which is applied
134 onto the target by the caller function. FILE will be preprocessed, optionally
135 defines and/or includes can be provided using DEF/INC arguments.
136
137 Inputs:
138
139 ``TARGET``
Mark Dykese9db8172022-07-21 14:10:00 -0500140 Name of the target.
Gyorgy Szing49091802020-11-24 00:33:09 +0100141
142 ``FILE``
Mark Dykese9db8172022-07-21 14:10:00 -0500143 Linker script file for the target.
Gyorgy Szing49091802020-11-24 00:33:09 +0100144
145 ``DEF`` (multi, optional)
Mark Dykese9db8172022-07-21 14:10:00 -0500146 Defines for the linker script preprocessor.
Gyorgy Szing49091802020-11-24 00:33:09 +0100147
148 ``INC`` (multi, optional)
Mark Dykese9db8172022-07-21 14:10:00 -0500149 Include paths for the linker script preprocessor.
Gyorgy Szing49091802020-11-24 00:33:09 +0100150
151#]===]
152function(compiler_set_linker_script)
153 set(_OPTIONS_ARGS)
154 set(_ONE_VALUE_ARGS TARGET FILE)
155 set(_MULTI_VALUE_ARGS DEF INC)
156 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
157
158 check_args(compiler_set_linker_script TARGET FILE)
159
160 get_filename_component(_src "${_MY_PARAMS_FILE}" ABSOLUTE)
161 get_filename_component(_src_ext "${_MY_PARAMS_FILE}" EXT)
162 set(_dst "${CMAKE_BINARY_DIR}/${_MY_PARAMS_TARGET}.ld")
163
164 if(NOT ("${_src_ext}" STREQUAL ".ld" OR "${_src_ext}" STREQUAL ".ld.S"))
165 message(WARNING "compiler_set_linker_script(): extension mismatch '${_src}'")
166 endif()
167
168 compiler_preprocess_file(
169 SRC ${_src}
170 DST ${_dst}
171 DEFINES ${_MY_PARAMS_DEF} __LINKER__
172 INCLUDES ${_MY_PARAMS_INC}
173 )
174
175 add_custom_target("${_MY_PARAMS_TARGET}_ld" DEPENDS "${_dst}")
176 add_dependencies("${_MY_PARAMS_TARGET}" "${_MY_PARAMS_TARGET}_ld")
177
Imre Kise6d73412021-10-18 14:01:47 +0200178 target_link_options(${_MY_PARAMS_TARGET} PRIVATE "-Wl,--script=${_dst}")
Andrew Beggs97a00d42021-06-15 15:45:46 +0000179 set_target_properties(${_MY_PARAMS_TARGET} PROPERTIES LINK_DEPENDS "${_dst}")
Gyorgy Szing49091802020-11-24 00:33:09 +0100180endfunction()
181
182#[===[.rst:
183.. cmake:command:: compiler_generate_binary_output
184
185 .. code-block:: cmake
186
Mark Dykese9db8172022-07-21 14:10:00 -0500187 compiler_generate_binary_output(TARGET <name> RES <var>)
Gyorgy Szing49091802020-11-24 00:33:09 +0100188
189 Generate binary output for the target. The function converts the output
190 executable into bin file using toolchain specific syntax.
191
192 Inputs:
193
194 ``TARGET``
Mark Dykese9db8172022-07-21 14:10:00 -0500195 Name of the target.
Gyorgy Szing49091802020-11-24 00:33:09 +0100196
197 Outputs:
198
199 ``RES``
Mark Dykese9db8172022-07-21 14:10:00 -0500200 Full patch to output file.
Gyorgy Szing49091802020-11-24 00:33:09 +0100201
202#]===]
203function(compiler_generate_binary_output)
204 set(options)
Imre Kis2cfb2b42021-12-15 19:15:42 +0100205 set(oneValueArgs TARGET NAME RES)
Gyorgy Szing49091802020-11-24 00:33:09 +0100206 set(multiValueArgs)
207 cmake_parse_arguments(MY "${options}" "${oneValueArgs}"
208 "${multiValueArgs}" ${ARGN} )
209 add_custom_command(
210 TARGET ${MY_TARGET} POST_BUILD
211 COMMAND ${CMAKE_OBJCOPY} -O binary
212 $<TARGET_FILE:${MY_TARGET}>
Imre Kis2cfb2b42021-12-15 19:15:42 +0100213 $<TARGET_FILE_DIR:${MY_TARGET}>/${MY_NAME})
Gyorgy Szing49091802020-11-24 00:33:09 +0100214 if (MY_RES)
Imre Kis2cfb2b42021-12-15 19:15:42 +0100215 set(${MY_RES} $<TARGET_FILE_DIR:${MY_TARGET}>/${MY_NAME} PARENT_SCOPE)
Gyorgy Szing49091802020-11-24 00:33:09 +0100216 endif()
217
218endfunction()
219
220#[===[.rst:
221.. cmake:command:: compiler_generate_stripped_elf
222
223 .. code-block:: cmake
224
Mark Dykese9db8172022-07-21 14:10:00 -0500225 compiler_generate_stripped_elf(TARGET foo NAME foo.stripped.elf RES var)
Gyorgy Szing49091802020-11-24 00:33:09 +0100226
227 Strip all symbols that are not needed for relocation processing and return the location
228 of the result.
229
230 Inputs:
231
232 ``TARGET``
Mark Dykese9db8172022-07-21 14:10:00 -0500233 Name of the target.
Gyorgy Szing49091802020-11-24 00:33:09 +0100234
235 ``NAME``
Mark Dykese9db8172022-07-21 14:10:00 -0500236 Name of output file
Gyorgy Szing49091802020-11-24 00:33:09 +0100237
238 Outputs:
239
240 ``RES``
Mark Dykese9db8172022-07-21 14:10:00 -0500241 Name of variable to store the full path of the stripped executable.
Gyorgy Szing49091802020-11-24 00:33:09 +0100242
243#]===]
244
245function(compiler_generate_stripped_elf)
246 set(options)
247 set(oneValueArgs TARGET NAME RES)
248 set(multiValueArgs)
249 cmake_parse_arguments(MY "${options}" "${oneValueArgs}"
250 "${multiValueArgs}" ${ARGN} )
251
252 add_custom_command(
253 TARGET ${MY_TARGET} POST_BUILD
254 COMMAND ${CMAKE_OBJCOPY} --strip-unneeded
255 $<TARGET_FILE:${MY_TARGET}>
256 $<TARGET_FILE_DIR:${MY_TARGET}>/${MY_NAME})
257 if (MY_RES)
258 set(${MY_RES} $<TARGET_FILE_DIR:${MY_TARGET}>/${MY_NAME} PARENT_SCOPE)
259 endif()
260endfunction()
Andrew Beggs97a00d42021-06-15 15:45:46 +0000261
262#[===[.rst:
263.. cmake:command:: gcc_get_lib_location
264
265 .. code-block:: cmake
266
Mark Dykese9db8172022-07-21 14:10:00 -0500267 gcc_get_lib_location(TARGET foo NAME foo.stripped.elf RES var)
Andrew Beggs97a00d42021-06-15 15:45:46 +0000268
269 Query the location of a specific library part of the GCC binary release. Can
Mark Dykese9db8172022-07-21 14:10:00 -0500270 be used to find built in libraries like libgcc.a when i.w. -nostdlib option
Andrew Beggs97a00d42021-06-15 15:45:46 +0000271 is used.
272
Mark Dykese9db8172022-07-21 14:10:00 -0500273 The function uses the :variable:`LIBGCC_LOCATE_CFLAGS`.
274
Andrew Beggs97a00d42021-06-15 15:45:46 +0000275 Inputs:
276
277 ``LIBRARY_NAME``
Mark Dykese9db8172022-07-21 14:10:00 -0500278 Name of the library to search for.
Andrew Beggs97a00d42021-06-15 15:45:46 +0000279
280 Outputs:
281
282 ``RES``
Mark Dykese9db8172022-07-21 14:10:00 -0500283 Name of variable to store the full path of the library.
Andrew Beggs97a00d42021-06-15 15:45:46 +0000284
285#]===]
286function(gcc_get_lib_location)
287 set(options)
288 set(oneValueArgs LIBRARY_NAME RES)
289 set(multiValueArgs)
290 cmake_parse_arguments(MY "${options}" "${oneValueArgs}"
291 "${multiValueArgs}" ${ARGN} )
Mark Dykese9db8172022-07-21 14:10:00 -0500292
293 if (DEFINED ENV{LIBGCC_LOCATE_CFLAGS})
294 set(LIBGCC_LOCATE_CFLAGS $ENV{LIBGCC_LOCATE_CFLAGS} CACHE STRING "GCC library search options" )
295 endif()
296
Andrew Beggs97a00d42021-06-15 15:45:46 +0000297 execute_process(
Mark Dykese9db8172022-07-21 14:10:00 -0500298 COMMAND ${CMAKE_C_COMPILER} ${LIBGCC_LOCATE_CFLAGS} "--print-file-name=${MY_LIBRARY_NAME}"
Andrew Beggs97a00d42021-06-15 15:45:46 +0000299 OUTPUT_VARIABLE _RES
300 RESULT_VARIABLE _GCC_ERROR_CODE
301 OUTPUT_STRIP_TRAILING_WHITESPACE
302 )
303
304 if(_GCC_ERROR_CODE GREATER 0)
Gyorgy Szing737a0bc2022-03-02 21:06:00 +0000305 message(WARNING "GCC (${CMAKE_C_COMPILER}) invocation failed, cannot determine location of library \"${MY_LIBRARY_NAME}\".")
306 set(_RES "${LIBRARY_NAME}-NOTFOUND")
307 endif()
308
309 if (NOT IS_ABSOLUTE "${_RES}")
310 message(WARNING "GCC (${CMAKE_C_COMPILER}) failed to return the location of file \"${MY_LIBRARY_NAME}\".")
Andrew Beggs97a00d42021-06-15 15:45:46 +0000311 set(_RES "${LIBRARY_NAME}-NOTFOUND")
312 endif()
313
314 set(${MY_RES} ${_RES} PARENT_SCOPE)
315endfunction()
Mark Dykese9db8172022-07-21 14:10:00 -0500316
317
318#[===[.rst:
319.. cmake:command:: compiler_set_freestanding
320
321 .. code-block:: cmake
322
323 compiler_set_freestanding(TARGET foo)
324
325 Configure the target specified for "freestanging" compilation mode. Please see [1] for more information.
326 This will configure the target:
327 - to block access to the "built in" standard library and its headers
328 - link compiler specific libraries
329 - add include paths to compiler specific headers
330
331 All settings will be PUBLIC or INTERFACE (for imported targets) and thus will take effect on targets
332 depending on the configured one.
333
334 The function uses and manipulates the following CACHE variables:
335 - :variable:`LIBGCC_PATH`
336 - :variable:`LIBGCC_INCLUDE_DIRS`
337
338 CMake has a spacial behavior which needs a workaround. CMake is automatically filtering out built in compiler
339 include paths from the compiler command line.
340 As a workaround, compiler specific headers are copied to the build directory and set include path to the new
341 location.
342
343 Limitations:
344 - Inheritance of target settings may be problematic. Compiling components in "freestanding" mode may put
345 restrictions on reusing these. When such components are installed, the "freestanding" nature shall be
346 propagated to dependencies. This is not tested or implemented.
347
348 1: https://wiki.osdev.org/Implications_of_writing_a_freestanding_C_project
349 2: https://gitlab.kitware.com/cmake/cmake/-/issues/19227
350
351 Inputs:
352
353 ``TARGET``
354 Name of the target to configure.
355
356 Outputs:
357
358 N/A
359
360#]===]
361function(compiler_set_freestanding)
362 set(options)
363 set(oneValueArgs TARGET)
364 set(multiValueArgs)
365 cmake_parse_arguments(MY "${options}" "${oneValueArgs}"
366 "${multiValueArgs}" ${ARGN} )
367
368 # Validate parameters
369 if (NOT DEFINED MY_TARGET)
370 message(FATAL_ERROR "Mandatory parameter TARGET is missing!")
371 endif()
372
373 # Set INTERFACE options for imported targets and PUBLIC otherwise.
374 get_property(_is_imported_target TARGET ${MY_TARGET} PROPERTY IMPORTED SET)
375 if (_is_imported_target)
376 set(_option_type INTERFACE)
377 else()
378 set(_option_type PUBLIC)
379 endif()
380
381 ### Get the location of libgcc.a
382 # Copy values from environment if present. Note: if the value is already in the CACHE, this set will have no effect.
383 if(DEFINED ENV{LIBGCC_PATH})
384 set(LIBGCC_PATH $ENV{LIBGCC_PATH} CACHE PATH "location of libgcc.a")
385 endif()
386 if (NOT DEFINED LIBGCC_PATH)
387 gcc_get_lib_location(LIBRARY_NAME "libgcc.a" RES _TMP_VAR)
388
389 if (NOT _TMP_VAR)
390 message(FATAL_ERROR "Location of libgcc.a can not be determined. Please set LIBGCC_PATH on the command"
391 " line or in the environment.")
392 endif()
393 set(LIBGCC_PATH ${_TMP_VAR} CACHE PATH "location of libgcc.a")
394 unset(_TMP_VAR)
395 endif()
396
397 # Validate LIBGCC_PATH
398 if(NOT EXISTS "${LIBGCC_PATH}" OR IS_DIRECTORY "${LIBGCC_PATH}")
399 message(FATAL_ERROR "LIBGCC_PATH \"${LIBGCC_PATH}\" must be the full path of a library file."
400 " Either set LIBGCC_PATH on the command line (or in the environment), or fix the existing"
401 " value.")
402 endif()
403 message(STATUS "libgcc.a for target \"${MY_TARGET}\" is used from ${LIBGCC_PATH}")
404
405 ### Get the location of libgcc specific header files.
406 # Copy values from environment if present. Note: if the value is already in the CACHE, this set will have no effect.
407 if(DEFINED ENV{LIBGCC_INCLUDE_DIRS})
408 set(LIBGCC_INCLUDE_DIRS $ENV{LIBGCC_INCLUDE_DIRS} CACHE STRING "GCC specific include PATHs")
409 endif()
410 if(NOT DEFINED LIBGCC_INCLUDE_DIRS)
411 # We can get the correct path if we ask for a location without a library name
412 gcc_get_lib_location(LIBRARY_NAME "" RES _TMP_VAR)
413
414 if (NOT _TMP_VAR)
415 message(FATAL_ERROR "Location of GCC specific include PATHs can not be determined. Please set"
416 " LIBGCC_INCLUDE_DIRS on the command line or in the environment.")
417 endif()
418
419 set(LIBGCC_INCLUDE_DIRS
420 "${_TMP_VAR}/include"
421 "${_TMP_VAR}/include-fixed" CACHE STRING "GCC specific include PATHs")
422 unset(_TMP_VAR)
423 endif()
424
425 # There is no way to stop cmake from filtering out built in compiler include paths
426 # from compiler command line (see https://gitlab.kitware.com/cmake/cmake/-/issues/19227).
427 # As a workaround copy headers to build directory and set include path to the new
428 # location.
429 # Also validate locations.
430 if (NOT GCC_INCLUDES_MOVED)
431 foreach(_dir IN LISTS LIBGCC_INCLUDE_DIRS)
432 if(NOT IS_DIRECTORY "${_dir}")
433 message(FATAL_ERROR "GCC specific include PATH \"${_dir}\" does not exist.")
434 endif()
435
436 file(COPY "${_dir}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gcc-include")
437 get_filename_component(_TMP_VAR "${_dir}" NAME)
438 list(APPEND _gcc_include_dirs "${CMAKE_CURRENT_BINARY_DIR}/gcc-include/${_TMP_VAR}")
439 message(STATUS "Compiler specific include path \"${_dir}\" mirrored to"
440 " \"${CMAKE_CURRENT_BINARY_DIR}/gcc-include/${_TMP_VAR}\".")
441 endforeach()
442 unset(_TMP_VAR)
443 set(GCC_INCLUDES_MOVED True CACHE BOOL "GCC include files are already copied.")
444 mark_as_advanced(GCC_INCLUDES_MOVED)
445 # Fix the variable in the CACHE.
446 set(LIBGCC_INCLUDE_DIRS ${_gcc_include_dirs} CACHE STRING "GCC specific include PATHs" FORCE)
447 endif()
448
449 # Configure the target for freestanding mode.
450 target_compile_options(${MY_TARGET} ${_option_type} "-nostdinc")
451 target_include_directories(${MY_TARGET} SYSTEM ${_option_type} ${LIBGCC_INCLUDE_DIRS})
452 target_link_options(${MY_TARGET} ${_option_type} "-nostdlib" "-nostartfiles")
453 target_link_libraries(${MY_TARGET} ${_option_type} "${LIBGCC_PATH}")
454endfunction()