Files
esp-idf/tools/cmake/toolchain_flags.cmake
T
Alexey Lapshin a217630b1e feat(build): propagate compiler flags from files to toolchain.cmake
This change improves build consistency across external projects integrated
through CMake by ensuring that compiler flags defined in configuration files
are passed correctly to the toolchain. It covers the majority of use cases,
as external projects are typically also CMake-based. For projects that use
a custom build system, users will still need to specify the required flags
manually.
2025-11-19 01:12:40 +07:00

158 lines
6.2 KiB
CMake

# Map option variable names to their corresponding file lists
set(_IDF_TOOLCHAIN_OPTION_MAPPINGS
"COMPILE_OPTIONS|cflags:cxxflags:asmflags"
"C_COMPILE_OPTIONS|cflags"
"CXX_COMPILE_OPTIONS|cxxflags"
"ASM_COMPILE_OPTIONS|asmflags"
"LINK_OPTIONS|ldflags"
)
# Extract keywords from mappings (drop substring after "|")
set(_IDF_TOOLCHAIN_MULTI_VALUE_KEYWORDS "")
foreach(mapping ${_IDF_TOOLCHAIN_OPTION_MAPPINGS})
string(REGEX REPLACE "\\|.*" "" keyword "${mapping}")
list(APPEND _IDF_TOOLCHAIN_MULTI_VALUE_KEYWORDS "${keyword}")
endforeach()
# Helper macro to process toolchain flag options based on mappings
# Uses macro instead of function to access parent scope variables directly
# operation: "add" or "remove"
macro(_process_toolchain_flag_options)
cmake_parse_arguments(PARSE_ARGV 0 "" "" "" "${_IDF_TOOLCHAIN_MULTI_VALUE_KEYWORDS}")
if(NOT EXISTS "${IDF_TOOLCHAIN_BUILD_DIR}")
message(FATAL_ERROR "Toolchain directory does not exist: ${IDF_TOOLCHAIN_BUILD_DIR}")
endif()
foreach(mapping ${_IDF_TOOLCHAIN_OPTION_MAPPINGS})
string(REPLACE "|" ";" mapping_list ${mapping})
# Extract option variable name and add underscore prefix
list(GET mapping_list 0 option_var)
set(option_var "_${option_var}")
# Extract file list for this option variable
list(GET mapping_list 1 files)
string(REPLACE ":" ";" files ${files})
# Process flags based on calling function: add or remove
if("${CMAKE_CURRENT_FUNCTION}" MATCHES "add_flags" AND DEFINED ${option_var})
_add_flags_to_files("${files}" "${${option_var}}")
endif()
if("${CMAKE_CURRENT_FUNCTION}" MATCHES "remove_flags" AND ${option_var})
_remove_flags_from_files("${files}" "${${option_var}}")
endif()
endforeach()
endmacro()
# Helper function to remove flags matching patterns from specific files
function(_remove_flags_from_files files patterns)
foreach(file ${files})
set(file_path "${IDF_TOOLCHAIN_BUILD_DIR}/${file}")
if(EXISTS "${file_path}")
file(STRINGS "${file_path}" existing_lines)
else()
set(existing_lines "")
endif()
file(WRITE "${file_path}" "")
foreach(line ${existing_lines})
set(should_keep TRUE)
foreach(pattern ${patterns})
if("${line}" MATCHES "${pattern}")
set(should_keep FALSE)
break()
endif()
endforeach()
if(should_keep)
file(APPEND "${file_path}" "${line}\n")
endif()
endforeach()
endforeach()
endfunction()
# Helper function to add flags to specific files
function(_add_flags_to_files files flags)
foreach(file ${files})
set(file_path "${IDF_TOOLCHAIN_BUILD_DIR}/${file}")
if(EXISTS "${file_path}")
file(STRINGS "${file_path}" existing_lines)
else()
set(existing_lines "")
endif()
# Split flags string using regex to respect quotes.
# Matches: double-quoted strings, single-quoted strings, or unquoted space-delimited tokens.
#
# TODO IDF-14467:
# Currently, paired linker flags are not properly handled when filtering.
# The regex-based splitting treats each flag independently, which breaks
# flag pairs that must be kept together. Future enhancement should support
# the following paired flag constructs:
# - --start-group ... --end-group
# - --push-state ... --pop-state
# - --whole-archive ... --no-whole-archive
# - --as-needed ... --no-as-needed
# - --push-section ... --pop-section
string(REGEX MATCHALL "([^\\s\"']*(\"[^\"]*\"|'[^']*')|\"[^\"]*\"|'[^']*'|[^ \"']+)" flags "${flags}")
file(WRITE "${file_path}" "")
foreach(line ${existing_lines})
set(should_keep TRUE)
foreach(flag ${flags})
if("${line}" STREQUAL "${flag}")
set(should_keep FALSE)
break()
endif()
endforeach()
if(should_keep)
file(APPEND "${file_path}" "${line}\n")
endif()
endforeach()
foreach(flag ${flags})
# Skip flags that contain IDF_TOOLCHAIN_BUILD_DIR substring
# to avoid recursion
string(FIND "${flag}" "${IDF_TOOLCHAIN_BUILD_DIR}" found_pos)
if(found_pos EQUAL -1)
file(APPEND "${file_path}" "${flag}\n")
endif()
endforeach()
endforeach()
endfunction()
# idf_toolchain_add_flags
#
# @brief Add compiler or linker flags to the toolchain configuration files.
#
# This function adds flags to the appropriate toolchain response files (cflags, cxxflags,
# asmflags, or ldflags) in the IDF_TOOLCHAIN_BUILD_DIR directory. Duplicate flags
# are automatically removed before adding new ones.
#
# @param[in, optional] COMPILE_OPTIONS (multivalue) flags to add to C, C++, and ASM
# compilation. Applied to cflags, cxxflags, and asmflags files.
# @param[in, optional] C_COMPILE_OPTIONS (multivalue) flags to add to C compilation only.
# Applied to cflags file.
# @param[in, optional] CXX_COMPILE_OPTIONS (multivalue) flags to add to C++ compilation only.
# Applied to cxxflags file.
# @param[in, optional] ASM_COMPILE_OPTIONS (multivalue) flags to add to ASM compilation only.
# Applied to asmflags file.
# @param[in, optional] LINK_OPTIONS (multivalue) flags to add to linking.
# Applied to ldflags file.
#
# @note Multiple keyword arguments can be specified in a single call.
# @note Flags are deduplicated - if a flag already exists in the file, it will be
# removed before adding the new one to ensure no duplicates.
#
# Example:
# idf_toolchain_add_flags(COMPILE_OPTIONS "-Wall" "-Wextra")
# idf_toolchain_add_flags(C_COMPILE_OPTIONS "-std=c99" LINK_OPTIONS "-Wl,--gc-sections")
function(idf_toolchain_add_flags)
_process_toolchain_flag_options()
endfunction()
function(idf_toolchain_remove_flags)
_process_toolchain_flag_options()
endfunction()