diff --git a/CMakeLists.txt b/CMakeLists.txt index f7383e0bd582a..7fb0f6d9870d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -226,6 +226,12 @@ get_property(ASM_OPTIMIZE_FOR_SPEED_FLAG TARGET asm PROPERTY optimization_speed) get_property(ASM_OPTIMIZE_FOR_SIZE_FLAG TARGET asm PROPERTY optimization_size) get_property(ASM_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG TARGET asm PROPERTY optimization_size_aggressive) +get_property(LINKER_OPTIMIZE_FOR_NO_OPTIMIZATIONS_FLAG TARGET linker PROPERTY no_optimization) +get_property(LINKER_OPTIMIZE_FOR_DEBUG_FLAG TARGET linker PROPERTY optimization_debug) +get_property(LINKER_OPTIMIZE_FOR_SPEED_FLAG TARGET linker PROPERTY optimization_speed) +get_property(LINKER_OPTIMIZE_FOR_SIZE_FLAG TARGET linker PROPERTY optimization_size) +get_property(LINKER_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG TARGET linker PROPERTY optimization_size_aggressive) + # Let the assembler inherit the optimization flags of the compiler if it is # not set explicitly. if(NOT ASM_OPTIMIZE_FOR_NO_OPTIMIZATIONS_FLAG) @@ -244,23 +250,46 @@ if(NOT ASM_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG) set(ASM_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG ${COMPILER_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG}) endif() +# Let the linker inherit the optimization flags of the compiler if it is +# not set explicitly. +if(NOT LINKER_OPTIMIZE_FOR_NO_OPTIMIZATIONS_FLAG) + set(LINKER_OPTIMIZE_FOR_NO_OPTIMIZATIONS_FLAG ${COMPILER_OPTIMIZE_FOR_NO_OPTIMIZATIONS_FLAG}) +endif() +if(NOT LINKER_OPTIMIZE_FOR_DEBUG_FLAG) + set(LINKER_OPTIMIZE_FOR_DEBUG_FLAG ${COMPILER_OPTIMIZE_FOR_DEBUG_FLAG}) +endif() +if(NOT LINKER_OPTIMIZE_FOR_SPEED_FLAG) + set(LINKER_OPTIMIZE_FOR_SPEED_FLAG ${COMPILER_OPTIMIZE_FOR_SPEED_FLAG}) +endif() +if(NOT LINKER_OPTIMIZE_FOR_SIZE_FLAG) + set(LINKER_OPTIMIZE_FOR_SIZE_FLAG ${COMPILER_OPTIMIZE_FOR_SIZE_FLAG}) +endif() +if(NOT LINKER_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG) + set(LINKER_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG ${COMPILER_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG}) +endif() + # From kconfig choice, pick the actual OPTIMIZATION_FLAG to use. # Kconfig choice ensures only one of these CONFIG_*_OPTIMIZATIONS is set. if(CONFIG_NO_OPTIMIZATIONS) set(COMPILER_OPTIMIZATION_FLAG ${COMPILER_OPTIMIZE_FOR_NO_OPTIMIZATIONS_FLAG}) set(ASM_OPTIMIZATION_FLAG ${ASM_OPTIMIZE_FOR_NO_OPTIMIZATIONS_FLAG}) + set(LINKER_OPTIMIZATION_FLAG ${LINKER_OPTIMIZE_FOR_NO_OPTIMIZATIONS_FLAG}) elseif(CONFIG_DEBUG_OPTIMIZATIONS) set(COMPILER_OPTIMIZATION_FLAG ${COMPILER_OPTIMIZE_FOR_DEBUG_FLAG}) set(ASM_OPTIMIZATION_FLAG ${ASM_OPTIMIZE_FOR_DEBUG_FLAG}) + set(LINKER_OPTIMIZATION_FLAG ${LINKER_OPTIMIZE_FOR_DEBUG_FLAG}) elseif(CONFIG_SPEED_OPTIMIZATIONS) set(COMPILER_OPTIMIZATION_FLAG ${COMPILER_OPTIMIZE_FOR_SPEED_FLAG}) set(ASM_OPTIMIZATION_FLAG ${ASM_OPTIMIZE_FOR_SPEED_FLAG}) + set(LINKER_OPTIMIZATION_FLAG ${LINKER_OPTIMIZE_FOR_SPEED_FLAG}) elseif(CONFIG_SIZE_OPTIMIZATIONS) set(COMPILER_OPTIMIZATION_FLAG ${COMPILER_OPTIMIZE_FOR_SIZE_FLAG}) # Default in kconfig set(ASM_OPTIMIZATION_FLAG ${ASM_OPTIMIZE_FOR_SIZE_FLAG}) + set(LINKER_OPTIMIZATION_FLAG ${LINKER_OPTIMIZE_FOR_SIZE_FLAG}) elseif(CONFIG_SIZE_OPTIMIZATIONS_AGGRESSIVE) set(COMPILER_OPTIMIZATION_FLAG ${COMPILER_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG}) set(ASM_OPTIMIZATION_FLAG ${ASM_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG}) + set(LINKER_OPTIMIZATION_FLAG ${LINKER_OPTIMIZE_FOR_SIZE_AGGRESSIVE_FLAG}) else() message(FATAL_ERROR "Unreachable code. Expected optimization level to have been chosen. See Kconfig.zephyr") @@ -277,6 +306,9 @@ endif() zephyr_compile_options($<$:${ASM_OPTIMIZATION_FLAG}>) zephyr_compile_options($<$:${COMPILER_OPTIMIZATION_FLAG}>) zephyr_compile_options($<$:${COMPILER_OPTIMIZATION_FLAG}>) +add_link_options(${LINKER_OPTIMIZATION_FLAG}) +compiler_simple_options(simple_options) +toolchain_linker_add_compiler_options(${simple_options}) if(CONFIG_LTO) zephyr_compile_options($) @@ -2371,6 +2403,9 @@ add_subdirectory_ifdef( cmake/makefile_exports ) +# Ask the compiler to set the lib_include_dir and rt_library properties +compiler_set_linker_properties() + toolchain_linker_finalize() # export build information diff --git a/cmake/compiler/clang/target.cmake b/cmake/compiler/clang/target.cmake index e3d4982726725..85eaa5a4d5481 100644 --- a/cmake/compiler/clang/target.cmake +++ b/cmake/compiler/clang/target.cmake @@ -100,21 +100,6 @@ if(NOT "${ARCH}" STREQUAL "posix") endif() endif() - # This libgcc code is partially duplicated in compiler/*/target.cmake - execute_process( - COMMAND ${CMAKE_C_COMPILER} ${clang_target_flag} ${TOOLCHAIN_C_FLAGS} - --print-libgcc-file-name - OUTPUT_VARIABLE RTLIB_FILE_NAME - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - get_filename_component(RTLIB_DIR ${RTLIB_FILE_NAME} DIRECTORY) - get_filename_component(RTLIB_NAME_WITH_PREFIX ${RTLIB_FILE_NAME} NAME_WLE) - string(REPLACE lib "" RTLIB_NAME ${RTLIB_NAME_WITH_PREFIX}) - - set_property(TARGET linker PROPERTY lib_include_dir "-L${RTLIB_DIR}") - set_property(TARGET linker PROPERTY rt_library "-l${RTLIB_NAME}") - list(APPEND CMAKE_REQUIRED_FLAGS -nostartfiles -nostdlib ${isystem_include_flags}) string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") diff --git a/cmake/compiler/gcc/target.cmake b/cmake/compiler/gcc/target.cmake index 65e37d4bc5ec4..4b20e398981c6 100644 --- a/cmake/compiler/gcc/target.cmake +++ b/cmake/compiler/gcc/target.cmake @@ -98,21 +98,6 @@ if(SYSROOT_DIR) set(LIBC_LIBRARY_DIR "\"${SYSROOT_DIR}\"/lib/${NEWLIB_DIR}") endif() -# This libgcc code is partially duplicated in compiler/*/target.cmake -execute_process( - COMMAND ${CMAKE_C_COMPILER} ${TOOLCHAIN_C_FLAGS} --print-libgcc-file-name - OUTPUT_VARIABLE LIBGCC_FILE_NAME - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - -assert_exists(LIBGCC_FILE_NAME) - -get_filename_component(LIBGCC_DIR ${LIBGCC_FILE_NAME} DIRECTORY) - -assert_exists(LIBGCC_DIR) - -set_linker_property(PROPERTY lib_include_dir "-L\"${LIBGCC_DIR}\"") - # For CMake to be able to test if a compiler flag is supported by the # toolchain we need to give CMake the necessary flags to compile and # link a dummy C file. diff --git a/cmake/compiler/icx/target.cmake b/cmake/compiler/icx/target.cmake index cd389497a5d38..9f1387e4758bb 100644 --- a/cmake/compiler/icx/target.cmake +++ b/cmake/compiler/icx/target.cmake @@ -45,21 +45,6 @@ else() list(APPEND TOOLCHAIN_C_FLAGS "-m32") endif() - -# This libgcc code is partially duplicated in compiler/*/target.cmake -execute_process( - COMMAND ${CMAKE_C_COMPILER} ${TOOLCHAIN_C_FLAGS} --print-libgcc-file-name - OUTPUT_VARIABLE LIBGCC_FILE_NAME - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - -get_filename_component(LIBGCC_DIR ${LIBGCC_FILE_NAME} DIRECTORY) - -list(APPEND LIB_INCLUDE_DIR "-L\"${LIBGCC_DIR}\"") -if(LIBGCC_DIR) - list(APPEND TOOLCHAIN_LIBS gcc) -endif() - set(CMAKE_REQUIRED_FLAGS -nostartfiles -nostdlib ${isystem_include_flags}) string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") diff --git a/cmake/compiler/target_template.cmake b/cmake/compiler/target_template.cmake new file mode 100644 index 0000000000000..0c83868beba3e --- /dev/null +++ b/cmake/compiler/target_template.cmake @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2025, Nordic Semiconductor ASA + +# Template file for optional Zephyr compiler functions. +# +# This file will define optional compiler functions for toolchains that are not +# defining these functions themselves. + +# Extract all of the compiler options which don't involve generator +# expressions. We hope that none of the flags required to compute compiler +# support library paths depend upon those. + +function(compiler_simple_options simple_options_out) + + get_property(flags TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_OPTIONS) + + set(simple_options "") + + foreach(flag ${flags}) + + # Include this flag if GENEX_STRIP has no effect, + # otherwise skip the whole thing + + string(GENEX_STRIP "${flag}" sflag) + if(flag STREQUAL sflag) + if(flag MATCHES "^SHELL:[ ]*(.*)") + separate_arguments(flag UNIX_COMMAND ${CMAKE_MATCH_1}) + endif() + list(APPEND simple_options ${flag}) + endif() + + endforeach() + + set(${simple_options_out} "${simple_options}" PARENT_SCOPE) +endfunction() + +if(NOT COMMAND compiler_file_path) + + # Search for filename in default compiler library path using the + # --print-file-name option which is common to gcc and clang. If the + # file is not found, filepath_out will be set to an empty string. + # + # This only works if none of the compiler flags used to compute + # the library path involve generator expressions as we cannot + # evaluate those in this function. + # + # Compilers needing a different implementation should provide this + # function in their target.cmake file + + function(compiler_file_path filename filepath_out) + + compiler_simple_options(simple_options) + + execute_process( + COMMAND ${CMAKE_C_COMPILER} ${TOOLCHAIN_C_FLAGS} ${COMPILER_OPTIMIZATION_FLAG} ${simple_options} + --print-file-name ${filename} + OUTPUT_VARIABLE filepath + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(${filepath} STREQUAL ${filename}) + set(filepath "") + endif() + set(${filepath_out} "${filepath}" PARENT_SCOPE) + endfunction() + +endif() + +if(NOT COMMAND compiler_set_linker_properties) + + # Set the lib_include_dir and rt_library linker properties + # by searching for the runtime library in the compiler default + # library search path. If no runtime library is found, these + # properties will remain unset + # + # Compilers needing a different implementation should provide this + # function in their target.cmake file + + function(compiler_set_linker_properties) + + compiler_simple_options(simple_options) + + # Compute complete path to the runtime library using the + # --print-libgcc-file-name compiler flag + execute_process( + COMMAND ${CMAKE_C_COMPILER} ${TOOLCHAIN_C_FLAGS} ${COMPILER_OPTIMIZATION_FLAG} ${simple_options} + --print-libgcc-file-name + OUTPUT_VARIABLE library_path + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + # Compute the library directory name + + get_filename_component(library_dir ${library_path} DIRECTORY) + set_linker_property(PROPERTY lib_include_dir "-L${library_dir}") + + # Compute the linker option for this library + + get_filename_component(library_basename ${library_path} NAME_WLE) + + # Remove the leading 'lib' prefix to leave a value suitable for use with + # the linker -l flag + string(REPLACE lib "" library_name ${library_basename}) + + set_linker_property(PROPERTY rt_library "-l${library_name}") + endfunction() + +endif() diff --git a/cmake/compiler/xcc/target.cmake b/cmake/compiler/xcc/target.cmake index 56d15451d3d5f..32e5e2288f32f 100644 --- a/cmake/compiler/xcc/target.cmake +++ b/cmake/compiler/xcc/target.cmake @@ -55,17 +55,6 @@ foreach(file_name include/stddef.h include-fixed/limits.h) endif() endforeach() -# This libgcc code is partially duplicated in compiler/*/target.cmake -execute_process( - COMMAND ${CMAKE_C_COMPILER} ${TOOLCHAIN_C_FLAGS} --print-libgcc-file-name - OUTPUT_VARIABLE LIBGCC_FILE_NAME - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - -get_filename_component(LIBGCC_DIR ${LIBGCC_FILE_NAME} DIRECTORY) - -list(APPEND LIB_INCLUDE_DIR "-L\"${LIBGCC_DIR}\"") - # For CMake to be able to test if a compiler flag is supported by the # toolchain we need to give CMake the necessary flags to compile and # link a dummy C file. diff --git a/cmake/linker/ld/target.cmake b/cmake/linker/ld/target.cmake index 1b3ce9ec9d816..592596576d111 100644 --- a/cmake/linker/ld/target.cmake +++ b/cmake/linker/ld/target.cmake @@ -159,15 +159,25 @@ macro(toolchain_linker_finalize) set(cpp_link "${common_link}") if(NOT "${ZEPHYR_TOOLCHAIN_VARIANT}" STREQUAL "host") - if(CONFIG_CPP_EXCEPTIONS AND LIBGCC_DIR) + compiler_file_path(crtbegin.o CRTBEGIN_PATH) + compiler_file_path(crtend.o CRTEND_PATH) + if(CONFIG_CPP_EXCEPTIONS AND CRTBEGIN_PATH AND CRTEND_PATH) # When building with C++ Exceptions, it is important that crtbegin and crtend # are linked at specific locations. - set(cpp_link " ${LIBGCC_DIR}/crtbegin.o ${link_libraries} ${LIBGCC_DIR}/crtend.o") + set(cpp_link " ${CRTBEGIN_PATH} ${link_libraries} ${CRTEND_PATH}") endif() endif() set(CMAKE_CXX_LINK_EXECUTABLE " ${cpp_link}") endmacro() +# Function to map compiler flags into suitable linker flags +# When using the compiler driver to run the linker, just pass +# them all through + +function(toolchain_linker_add_compiler_options) + add_link_options(${ARGV}) +endfunction() + # Load toolchain_ld-family macros include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_relocation.cmake) include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_configure.cmake) diff --git a/cmake/linker/lld/target.cmake b/cmake/linker/lld/target.cmake index b6b96525e7062..96df1c123796a 100644 --- a/cmake/linker/lld/target.cmake +++ b/cmake/linker/lld/target.cmake @@ -128,6 +128,14 @@ macro(toolchain_linker_finalize) set(CMAKE_CXX_LINK_EXECUTABLE " ${common_link}") endmacro() +# Function to map compiler flags into suitable linker flags +# When using the compiler driver to run the linker, just pass +# them all through + +function(toolchain_linker_add_compiler_options) + add_link_options(${ARGV}) +endfunction() + # Load toolchain_ld-family macros include(${ZEPHYR_BASE}/cmake/linker/ld/target_relocation.cmake) include(${ZEPHYR_BASE}/cmake/linker/ld/target_configure.cmake) diff --git a/cmake/linker/target_template.cmake b/cmake/linker/target_template.cmake index efa27de6fb4fa..59e9d8d84f0bc 100644 --- a/cmake/linker/target_template.cmake +++ b/cmake/linker/target_template.cmake @@ -11,3 +11,13 @@ if(NOT COMMAND toolchain_linker_finalize) macro(toolchain_linker_finalize) endmacro() endif() + +if(NOT COMMAND toolchain_linker_add_compiler_options) + + # If the linker doesn't provide a method for mapping compiler options + # to linker options, then assume we can't. This matters when the linker + # is using additional flags when computing toolchain library paths. + + function(toolchain_linker_add_compiler_options) + endfunction() +endif() diff --git a/cmake/linker/xt-ld/target.cmake b/cmake/linker/xt-ld/target.cmake index 9a6b76cda621e..bdaf9829fcdd2 100644 --- a/cmake/linker/xt-ld/target.cmake +++ b/cmake/linker/xt-ld/target.cmake @@ -11,14 +11,16 @@ find_program(CMAKE_LINKER xt-ld ${LD_SEARCH_PATH}) set_ifndef(LINKERFLAGPREFIX -Wl) -if(CONFIG_CPP_EXCEPTIONS) +compiler_file_path(crtbegin.o CRTBEGIN_PATH) +compiler_file_path(crtend.o CRTEND_PATH) +if(CONFIG_CPP_EXCEPTIONS AND CRTBEGIN_PATH AND CRTEND_PATH) # When building with C++ Exceptions, it is important that crtbegin and crtend # are linked at specific locations. # The location is so important that we cannot let this be controlled by normal # link libraries, instead we must control the link command specifically as # part of toolchain. set(CMAKE_CXX_LINK_EXECUTABLE - " ${LIBGCC_DIR}/crtbegin.o -o ${LIBGCC_DIR}/crtend.o") + " ${CRTBEGIN_PATH} -o ${CRTEND_PATH}") endif() # Run $LINKER_SCRIPT file through the C preprocessor, producing ${linker_script_gen} @@ -156,6 +158,14 @@ macro(toolchain_linker_finalize) set(CMAKE_CXX_LINK_EXECUTABLE " ${common_link}") endmacro() +# Function to map compiler flags into suitable linker flags +# When using the compiler driver to run the linker, just pass +# them all through + +function(toolchain_linker_add_compiler_options) + add_link_options(${ARGV}) +endfunction() + # xt-ld is Xtensa's own version of binutils' ld. # So we can reuse most of the ld configurations. include(${ZEPHYR_BASE}/cmake/linker/ld/target_relocation.cmake) diff --git a/cmake/modules/FindTargetTools.cmake b/cmake/modules/FindTargetTools.cmake index e35f577d69793..d5236aa8268cb 100644 --- a/cmake/modules/FindTargetTools.cmake +++ b/cmake/modules/FindTargetTools.cmake @@ -106,6 +106,7 @@ include(${ZEPHYR_BASE}/cmake/bintools/bintools_template.cmake) include(${TOOLCHAIN_ROOT}/cmake/bintools/${BINTOOLS}/target.cmake OPTIONAL) include(${TOOLCHAIN_ROOT}/cmake/linker/target_template.cmake) +include(${TOOLCHAIN_ROOT}/cmake/compiler/target_template.cmake) set(TargetTools_FOUND TRUE) set(TARGETTOOLS_FOUND TRUE)