cmake: Delay computation of linker paths until needed

With inclusion of the optimization flag into the multilib selection
process, we cannot compute the compiler library path when the compiler's
target.cmake is processed as LINKER_OPTIMIZATION_FLAG is not computed until
much later.

Instead, add a function (compiler_file_path) which can be used to locate
the appropriate crtbegin.o and crtend.o files.

Delay computation of lib_include_dir and rt_library until after all
compiler flags have been computed by adding compiler_set_linker_properties
and calling that just before toolchain_linker_finalize is invoked.

Place default implementations of both of these functions in a new file,
cmake/compiler/target_template.cmake, where we assume the compiler works
like gcc or clang and handlers the --print-file-name and
--print-libgcc-file-name options. Compilers needing alternate
implementations can override these functions in their target.cmake files.

These implementations require that no generator expressions are necessary
for the compiler to compute the right library paths.

This mechanism is also used to take any additional compiler options by
adding a new (optional) linker function,
toolchain_linker_add_compiler_options, which maps compiler options to
equivalent linker options, discarding any that aren't applicable.

Signed-off-by: Keith Packard <keithp@keithp.com>
This commit is contained in:
Keith Packard
2025-08-11 11:51:02 -07:00
committed by Chris Friedt
parent d29cbf6563
commit d77b58aacb
11 changed files with 156 additions and 60 deletions

View File

@@ -307,6 +307,8 @@ zephyr_compile_options($<$<COMPILE_LANGUAGE:ASM>:${ASM_OPTIMIZATION_FLAG}>)
zephyr_compile_options($<$<COMPILE_LANGUAGE:C>:${COMPILER_OPTIMIZATION_FLAG}>)
zephyr_compile_options($<$<COMPILE_LANGUAGE:CXX>:${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($<TARGET_PROPERTY:compiler,optimization_lto>)
@@ -2401,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

View File

@@ -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}")

View File

@@ -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.

View File

@@ -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}")

View File

@@ -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()

View File

@@ -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.

View File

@@ -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 "<LINK_FLAGS> ${LIBGCC_DIR}/crtbegin.o ${link_libraries} ${LIBGCC_DIR}/crtend.o")
set(cpp_link "<LINK_FLAGS> ${CRTBEGIN_PATH} ${link_libraries} ${CRTEND_PATH}")
endif()
endif()
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> ${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)

View File

@@ -128,6 +128,14 @@ macro(toolchain_linker_finalize)
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> ${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)

View File

@@ -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()

View File

@@ -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
"<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> ${LIBGCC_DIR}/crtbegin.o <OBJECTS> -o <TARGET> <LINK_LIBRARIES> ${LIBGCC_DIR}/crtend.o")
"<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> ${CRTBEGIN_PATH} <OBJECTS> -o <TARGET> <LINK_LIBRARIES> ${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 "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> ${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)

View File

@@ -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)