blob: 993bae0e47ce07ffbe62ef9683ed6e969b4bd253 [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
Gyorgy Szing49091802020-11-24 00:33:09 +010043#Generate a list of tool names to look for. Store the result in CMAKE_<lang>_COMPILER.
44function(gcc_find_tool NAME LANG)
45 string(REGEX REPLACE "([^;]+);" "\\1${NAME};\\1${NAME}.exe;" _gcc_names "${CROSS_COMPILE};")
46 find_program(_cross_compile_gcc NAMES ${_gcc_names} REQUIRED)
47 if (NOT _cross_compile_gcc)
48 string(REPLACE ";" " " _msg "${_gcc_names}")
49 message(FATAL_ERROR "Failed to find ${NAME} with the names: ${_msg}")
50 endif()
51 set(CMAKE_${LANG}_COMPILER ${_cross_compile_gcc} CACHE STRING "${LANG} compiler executable.")
52endfunction()
53
Gyorgy Szingf0be7ce2022-10-20 07:39:40 +020054if(CMAKE_CROSSCOMPILING)
55 if(NOT CROSS_COMPILE AND NOT DEFINED ENV{CROSS_COMPILE})
56 message(FATAL_ERROR "'CROSS_COMPILE' is not defined. Set it to the gcc prefix triplet, ie. cmake <..>-DCROSS_COMPILE=aarch64-elf-")
57 endif()
Gyorgy Szing49091802020-11-24 00:33:09 +010058
Gyorgy Szingf0be7ce2022-10-20 07:39:40 +020059 set(CROSS_COMPILE $ENV{CROSS_COMPILE} CACHE STRING "Prefix of the cross-compiler commands")
60
61 gcc_find_tool(gcc C)
62 gcc_find_tool(g++ CXX)
63
64 #Official solution to disable compiler checks
65 set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
66endif()
Gyorgy Szing49091802020-11-24 00:33:09 +010067
Mark Dykese9db8172022-07-21 14:10:00 -050068#By default when INTERFACE_INCUDES of libraries linked to an exe are treated
Gyorgy Szing49091802020-11-24 00:33:09 +010069#as system includes. gcc-arm-8.2-2019.01-i686-mingw32-aarch64-elf (gcc 8.2.1) will
70#set C linkage o these files, which will result in compilation errors for C++ projects.
71#This setting fixes that.
72set(CMAKE_NO_SYSTEM_FROM_IMPORTED True)
73
74#[===[.rst:
75.. cmake:command:: compiler_preprocess_file
76
77 .. code-block:: cmake
78
Mark Dykese9db8172022-07-21 14:10:00 -050079 compiler_preprocess_file(SRC file.c DST file_pp.c)
80 compiler_preprocess_file(SRC file.c DST file_pp.c
81 DEFINES USE_LIB INCLUDES include/lib)
Gyorgy Szing49091802020-11-24 00:33:09 +010082
83 Run the preprocessor on a file and save the output to another file. Optionally
84 provide defines and include paths to the preprocessor.
85
86 Inputs:
87
88 ``SRC``
Mark Dykese9db8172022-07-21 14:10:00 -050089 Name of the source file to preprocess.
Gyorgy Szing49091802020-11-24 00:33:09 +010090
91 ``DST``
Mark Dykese9db8172022-07-21 14:10:00 -050092 Where to write the preprocessed output.
Gyorgy Szing49091802020-11-24 00:33:09 +010093
94 ``DEFINES`` (multi, optional)
Mark Dykese9db8172022-07-21 14:10:00 -050095 Definitions for the preprocessor.
Gyorgy Szing49091802020-11-24 00:33:09 +010096
97 ``INCLUDES`` (multi, optional)
Mark Dykese9db8172022-07-21 14:10:00 -050098 Include paths for the preprocessor.
Gyorgy Szing49091802020-11-24 00:33:09 +010099
100#]===]
101function(compiler_preprocess_file)
102 set(_OPTIONS_ARGS)
103 set(_ONE_VALUE_ARGS SRC DST)
104 set(_MULTI_VALUE_ARGS DEFINES INCLUDES)
105 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
106
107 check_args(compiler_preprocess_file SRC DST)
108
109 set(_flags "")
110 if(_MY_PARAMS_DEFINES)
111 list(TRANSFORM _MY_PARAMS_DEFINES PREPEND -D)
112 list(APPEND _flags ${_MY_PARAMS_DEFINES})
113 endif()
114 if(_MY_PARAMS_INCLUDES)
115 list(TRANSFORM _MY_PARAMS_INCLUDES PREPEND -I)
116 list(APPEND _flags ${_MY_PARAMS_INCLUDES})
117 endif()
118
119 add_custom_command(
120 DEPENDS ${_MY_PARAMS_SRC} OUTPUT ${_MY_PARAMS_DST}
121 COMMAND ${CMAKE_C_COMPILER} -E -P -x assembler-with-cpp ${_flags}
122 ${_MY_PARAMS_SRC} -o ${_MY_PARAMS_DST}
123 )
124endfunction()
125
126#[===[.rst:
127.. cmake:command:: compiler_set_linker_script
128
129 .. code-block:: cmake
130
Mark Dykese9db8172022-07-21 14:10:00 -0500131 compiler_set_linker_script(TARGET foo FILE foo.ld.S)
132 compiler_set_linker_script(TARGET foo FILE foo.ld.S DEF USE_LIB INC include/lib)
Gyorgy Szing49091802020-11-24 00:33:09 +0100133
134 Set linker script for a target. The function adds an LDFLAG using the
135 toolchain specific syntax to the TARGET_linker_script group, which is applied
136 onto the target by the caller function. FILE will be preprocessed, optionally
137 defines and/or includes can be provided using DEF/INC arguments.
138
139 Inputs:
140
141 ``TARGET``
Mark Dykese9db8172022-07-21 14:10:00 -0500142 Name of the target.
Gyorgy Szing49091802020-11-24 00:33:09 +0100143
144 ``FILE``
Mark Dykese9db8172022-07-21 14:10:00 -0500145 Linker script file for the target.
Gyorgy Szing49091802020-11-24 00:33:09 +0100146
147 ``DEF`` (multi, optional)
Mark Dykese9db8172022-07-21 14:10:00 -0500148 Defines for the linker script preprocessor.
Gyorgy Szing49091802020-11-24 00:33:09 +0100149
150 ``INC`` (multi, optional)
Mark Dykese9db8172022-07-21 14:10:00 -0500151 Include paths for the linker script preprocessor.
Gyorgy Szing49091802020-11-24 00:33:09 +0100152
153#]===]
154function(compiler_set_linker_script)
155 set(_OPTIONS_ARGS)
156 set(_ONE_VALUE_ARGS TARGET FILE)
157 set(_MULTI_VALUE_ARGS DEF INC)
158 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
159
160 check_args(compiler_set_linker_script TARGET FILE)
161
162 get_filename_component(_src "${_MY_PARAMS_FILE}" ABSOLUTE)
163 get_filename_component(_src_ext "${_MY_PARAMS_FILE}" EXT)
164 set(_dst "${CMAKE_BINARY_DIR}/${_MY_PARAMS_TARGET}.ld")
165
166 if(NOT ("${_src_ext}" STREQUAL ".ld" OR "${_src_ext}" STREQUAL ".ld.S"))
167 message(WARNING "compiler_set_linker_script(): extension mismatch '${_src}'")
168 endif()
169
170 compiler_preprocess_file(
171 SRC ${_src}
172 DST ${_dst}
173 DEFINES ${_MY_PARAMS_DEF} __LINKER__
174 INCLUDES ${_MY_PARAMS_INC}
175 )
176
177 add_custom_target("${_MY_PARAMS_TARGET}_ld" DEPENDS "${_dst}")
178 add_dependencies("${_MY_PARAMS_TARGET}" "${_MY_PARAMS_TARGET}_ld")
179
Imre Kise6d73412021-10-18 14:01:47 +0200180 target_link_options(${_MY_PARAMS_TARGET} PRIVATE "-Wl,--script=${_dst}")
Andrew Beggs97a00d42021-06-15 15:45:46 +0000181 set_target_properties(${_MY_PARAMS_TARGET} PROPERTIES LINK_DEPENDS "${_dst}")
Gyorgy Szing49091802020-11-24 00:33:09 +0100182endfunction()
183
184#[===[.rst:
185.. cmake:command:: compiler_generate_binary_output
186
187 .. code-block:: cmake
188
Mark Dykese9db8172022-07-21 14:10:00 -0500189 compiler_generate_binary_output(TARGET <name> RES <var>)
Gyorgy Szing49091802020-11-24 00:33:09 +0100190
191 Generate binary output for the target. The function converts the output
192 executable into bin file using toolchain specific syntax.
193
194 Inputs:
195
196 ``TARGET``
Mark Dykese9db8172022-07-21 14:10:00 -0500197 Name of the target.
Gyorgy Szing49091802020-11-24 00:33:09 +0100198
199 Outputs:
200
201 ``RES``
Mark Dykese9db8172022-07-21 14:10:00 -0500202 Full patch to output file.
Gyorgy Szing49091802020-11-24 00:33:09 +0100203
204#]===]
205function(compiler_generate_binary_output)
206 set(options)
Imre Kis2cfb2b42021-12-15 19:15:42 +0100207 set(oneValueArgs TARGET NAME RES)
Gyorgy Szing49091802020-11-24 00:33:09 +0100208 set(multiValueArgs)
209 cmake_parse_arguments(MY "${options}" "${oneValueArgs}"
210 "${multiValueArgs}" ${ARGN} )
211 add_custom_command(
212 TARGET ${MY_TARGET} POST_BUILD
213 COMMAND ${CMAKE_OBJCOPY} -O binary
214 $<TARGET_FILE:${MY_TARGET}>
Imre Kis2cfb2b42021-12-15 19:15:42 +0100215 $<TARGET_FILE_DIR:${MY_TARGET}>/${MY_NAME})
Gyorgy Szing49091802020-11-24 00:33:09 +0100216 if (MY_RES)
Imre Kis2cfb2b42021-12-15 19:15:42 +0100217 set(${MY_RES} $<TARGET_FILE_DIR:${MY_TARGET}>/${MY_NAME} PARENT_SCOPE)
Gyorgy Szing49091802020-11-24 00:33:09 +0100218 endif()
219
220endfunction()
221
222#[===[.rst:
223.. cmake:command:: compiler_generate_stripped_elf
224
225 .. code-block:: cmake
226
Mark Dykese9db8172022-07-21 14:10:00 -0500227 compiler_generate_stripped_elf(TARGET foo NAME foo.stripped.elf RES var)
Gyorgy Szing49091802020-11-24 00:33:09 +0100228
229 Strip all symbols that are not needed for relocation processing and return the location
230 of the result.
231
232 Inputs:
233
234 ``TARGET``
Mark Dykese9db8172022-07-21 14:10:00 -0500235 Name of the target.
Gyorgy Szing49091802020-11-24 00:33:09 +0100236
237 ``NAME``
Mark Dykese9db8172022-07-21 14:10:00 -0500238 Name of output file
Gyorgy Szing49091802020-11-24 00:33:09 +0100239
240 Outputs:
241
242 ``RES``
Mark Dykese9db8172022-07-21 14:10:00 -0500243 Name of variable to store the full path of the stripped executable.
Gyorgy Szing49091802020-11-24 00:33:09 +0100244
245#]===]
246
247function(compiler_generate_stripped_elf)
248 set(options)
249 set(oneValueArgs TARGET NAME RES)
250 set(multiValueArgs)
251 cmake_parse_arguments(MY "${options}" "${oneValueArgs}"
252 "${multiValueArgs}" ${ARGN} )
253
254 add_custom_command(
255 TARGET ${MY_TARGET} POST_BUILD
256 COMMAND ${CMAKE_OBJCOPY} --strip-unneeded
257 $<TARGET_FILE:${MY_TARGET}>
258 $<TARGET_FILE_DIR:${MY_TARGET}>/${MY_NAME})
259 if (MY_RES)
260 set(${MY_RES} $<TARGET_FILE_DIR:${MY_TARGET}>/${MY_NAME} PARENT_SCOPE)
261 endif()
262endfunction()
Andrew Beggs97a00d42021-06-15 15:45:46 +0000263
264#[===[.rst:
265.. cmake:command:: gcc_get_lib_location
266
267 .. code-block:: cmake
268
Mark Dykese9db8172022-07-21 14:10:00 -0500269 gcc_get_lib_location(TARGET foo NAME foo.stripped.elf RES var)
Andrew Beggs97a00d42021-06-15 15:45:46 +0000270
271 Query the location of a specific library part of the GCC binary release. Can
Mark Dykese9db8172022-07-21 14:10:00 -0500272 be used to find built in libraries like libgcc.a when i.w. -nostdlib option
Andrew Beggs97a00d42021-06-15 15:45:46 +0000273 is used.
274
Mark Dykese9db8172022-07-21 14:10:00 -0500275 The function uses the :variable:`LIBGCC_LOCATE_CFLAGS`.
276
Andrew Beggs97a00d42021-06-15 15:45:46 +0000277 Inputs:
278
279 ``LIBRARY_NAME``
Mark Dykese9db8172022-07-21 14:10:00 -0500280 Name of the library to search for.
Andrew Beggs97a00d42021-06-15 15:45:46 +0000281
282 Outputs:
283
284 ``RES``
Mark Dykese9db8172022-07-21 14:10:00 -0500285 Name of variable to store the full path of the library.
Andrew Beggs97a00d42021-06-15 15:45:46 +0000286
287#]===]
288function(gcc_get_lib_location)
289 set(options)
290 set(oneValueArgs LIBRARY_NAME RES)
291 set(multiValueArgs)
292 cmake_parse_arguments(MY "${options}" "${oneValueArgs}"
293 "${multiValueArgs}" ${ARGN} )
Mark Dykese9db8172022-07-21 14:10:00 -0500294
295 if (DEFINED ENV{LIBGCC_LOCATE_CFLAGS})
296 set(LIBGCC_LOCATE_CFLAGS $ENV{LIBGCC_LOCATE_CFLAGS} CACHE STRING "GCC library search options" )
297 endif()
298
Andrew Beggs97a00d42021-06-15 15:45:46 +0000299 execute_process(
Mark Dykese9db8172022-07-21 14:10:00 -0500300 COMMAND ${CMAKE_C_COMPILER} ${LIBGCC_LOCATE_CFLAGS} "--print-file-name=${MY_LIBRARY_NAME}"
Andrew Beggs97a00d42021-06-15 15:45:46 +0000301 OUTPUT_VARIABLE _RES
302 RESULT_VARIABLE _GCC_ERROR_CODE
303 OUTPUT_STRIP_TRAILING_WHITESPACE
304 )
305
306 if(_GCC_ERROR_CODE GREATER 0)
Gyorgy Szing737a0bc2022-03-02 21:06:00 +0000307 message(WARNING "GCC (${CMAKE_C_COMPILER}) invocation failed, cannot determine location of library \"${MY_LIBRARY_NAME}\".")
308 set(_RES "${LIBRARY_NAME}-NOTFOUND")
309 endif()
310
311 if (NOT IS_ABSOLUTE "${_RES}")
312 message(WARNING "GCC (${CMAKE_C_COMPILER}) failed to return the location of file \"${MY_LIBRARY_NAME}\".")
Andrew Beggs97a00d42021-06-15 15:45:46 +0000313 set(_RES "${LIBRARY_NAME}-NOTFOUND")
314 endif()
315
316 set(${MY_RES} ${_RES} PARENT_SCOPE)
317endfunction()
Mark Dykese9db8172022-07-21 14:10:00 -0500318
319
320#[===[.rst:
321.. cmake:command:: compiler_set_freestanding
322
323 .. code-block:: cmake
324
325 compiler_set_freestanding(TARGET foo)
326
327 Configure the target specified for "freestanging" compilation mode. Please see [1] for more information.
328 This will configure the target:
329 - to block access to the "built in" standard library and its headers
330 - link compiler specific libraries
331 - add include paths to compiler specific headers
332
333 All settings will be PUBLIC or INTERFACE (for imported targets) and thus will take effect on targets
334 depending on the configured one.
335
336 The function uses and manipulates the following CACHE variables:
337 - :variable:`LIBGCC_PATH`
338 - :variable:`LIBGCC_INCLUDE_DIRS`
339
340 CMake has a spacial behavior which needs a workaround. CMake is automatically filtering out built in compiler
341 include paths from the compiler command line.
342 As a workaround, compiler specific headers are copied to the build directory and set include path to the new
343 location.
344
345 Limitations:
346 - Inheritance of target settings may be problematic. Compiling components in "freestanding" mode may put
347 restrictions on reusing these. When such components are installed, the "freestanding" nature shall be
348 propagated to dependencies. This is not tested or implemented.
349
350 1: https://wiki.osdev.org/Implications_of_writing_a_freestanding_C_project
351 2: https://gitlab.kitware.com/cmake/cmake/-/issues/19227
352
353 Inputs:
354
355 ``TARGET``
356 Name of the target to configure.
357
358 Outputs:
359
360 N/A
361
362#]===]
363function(compiler_set_freestanding)
364 set(options)
365 set(oneValueArgs TARGET)
366 set(multiValueArgs)
367 cmake_parse_arguments(MY "${options}" "${oneValueArgs}"
368 "${multiValueArgs}" ${ARGN} )
369
370 # Validate parameters
371 if (NOT DEFINED MY_TARGET)
372 message(FATAL_ERROR "Mandatory parameter TARGET is missing!")
373 endif()
374
375 # Set INTERFACE options for imported targets and PUBLIC otherwise.
376 get_property(_is_imported_target TARGET ${MY_TARGET} PROPERTY IMPORTED SET)
377 if (_is_imported_target)
378 set(_option_type INTERFACE)
379 else()
380 set(_option_type PUBLIC)
381 endif()
382
383 ### Get the location of libgcc.a
384 # Copy values from environment if present. Note: if the value is already in the CACHE, this set will have no effect.
385 if(DEFINED ENV{LIBGCC_PATH})
386 set(LIBGCC_PATH $ENV{LIBGCC_PATH} CACHE PATH "location of libgcc.a")
387 endif()
388 if (NOT DEFINED LIBGCC_PATH)
389 gcc_get_lib_location(LIBRARY_NAME "libgcc.a" RES _TMP_VAR)
390
391 if (NOT _TMP_VAR)
392 message(FATAL_ERROR "Location of libgcc.a can not be determined. Please set LIBGCC_PATH on the command"
393 " line or in the environment.")
394 endif()
395 set(LIBGCC_PATH ${_TMP_VAR} CACHE PATH "location of libgcc.a")
396 unset(_TMP_VAR)
397 endif()
398
399 # Validate LIBGCC_PATH
400 if(NOT EXISTS "${LIBGCC_PATH}" OR IS_DIRECTORY "${LIBGCC_PATH}")
401 message(FATAL_ERROR "LIBGCC_PATH \"${LIBGCC_PATH}\" must be the full path of a library file."
402 " Either set LIBGCC_PATH on the command line (or in the environment), or fix the existing"
403 " value.")
404 endif()
405 message(STATUS "libgcc.a for target \"${MY_TARGET}\" is used from ${LIBGCC_PATH}")
406
407 ### Get the location of libgcc specific header files.
408 # Copy values from environment if present. Note: if the value is already in the CACHE, this set will have no effect.
409 if(DEFINED ENV{LIBGCC_INCLUDE_DIRS})
410 set(LIBGCC_INCLUDE_DIRS $ENV{LIBGCC_INCLUDE_DIRS} CACHE STRING "GCC specific include PATHs")
411 endif()
412 if(NOT DEFINED LIBGCC_INCLUDE_DIRS)
413 # We can get the correct path if we ask for a location without a library name
414 gcc_get_lib_location(LIBRARY_NAME "" RES _TMP_VAR)
415
416 if (NOT _TMP_VAR)
417 message(FATAL_ERROR "Location of GCC specific include PATHs can not be determined. Please set"
418 " LIBGCC_INCLUDE_DIRS on the command line or in the environment.")
419 endif()
420
421 set(LIBGCC_INCLUDE_DIRS
422 "${_TMP_VAR}/include"
423 "${_TMP_VAR}/include-fixed" CACHE STRING "GCC specific include PATHs")
424 unset(_TMP_VAR)
425 endif()
426
427 # There is no way to stop cmake from filtering out built in compiler include paths
428 # from compiler command line (see https://gitlab.kitware.com/cmake/cmake/-/issues/19227).
429 # As a workaround copy headers to build directory and set include path to the new
430 # location.
431 # Also validate locations.
432 if (NOT GCC_INCLUDES_MOVED)
433 foreach(_dir IN LISTS LIBGCC_INCLUDE_DIRS)
434 if(NOT IS_DIRECTORY "${_dir}")
435 message(FATAL_ERROR "GCC specific include PATH \"${_dir}\" does not exist.")
436 endif()
437
438 file(COPY "${_dir}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gcc-include")
439 get_filename_component(_TMP_VAR "${_dir}" NAME)
440 list(APPEND _gcc_include_dirs "${CMAKE_CURRENT_BINARY_DIR}/gcc-include/${_TMP_VAR}")
441 message(STATUS "Compiler specific include path \"${_dir}\" mirrored to"
442 " \"${CMAKE_CURRENT_BINARY_DIR}/gcc-include/${_TMP_VAR}\".")
443 endforeach()
444 unset(_TMP_VAR)
445 set(GCC_INCLUDES_MOVED True CACHE BOOL "GCC include files are already copied.")
446 mark_as_advanced(GCC_INCLUDES_MOVED)
447 # Fix the variable in the CACHE.
448 set(LIBGCC_INCLUDE_DIRS ${_gcc_include_dirs} CACHE STRING "GCC specific include PATHs" FORCE)
449 endif()
450
451 # Configure the target for freestanding mode.
452 target_compile_options(${MY_TARGET} ${_option_type} "-nostdinc")
453 target_include_directories(${MY_TARGET} SYSTEM ${_option_type} ${LIBGCC_INCLUDE_DIRS})
454 target_link_options(${MY_TARGET} ${_option_type} "-nostdlib" "-nostartfiles")
455 target_link_libraries(${MY_TARGET} ${_option_type} "${LIBGCC_PATH}")
456endfunction()