fix(build): don't call enable_language() before project()

For the Linux target, we currently attempt to fallback to older C/CXX
lagnuage standards in the __build_set_lang_version() function. The
language standard support is checked using CMake's language-specific
functions, such as check_c_compiler_flag(). These functions require the
language to be enabled[1] in CMake beforehand, which is done by calling
project() or by enabling the languages later with enable_language(). At
present, we use enable_language() to enable C and CXX languages in
CMake, allowing us to set the standard early, before invoking project().
However, newer CMake versions (>3.29) issue a warning[2] if
enable_language() is called before project(), as noted in CMP0165[3].

It should generally be acceptable to call __build_set_lang_version()
after __project(), but doing so would alter the behavior of the
COMPILE_OPTIONS also for non-Linux targets. Currently, users can
add to COMPILE_OPTIONS even before calling project() in the project's
CMakeLists.txt and the options will be in the desired order. In other
words, appending to COMPILE_OPTIONS can occur either before or after
calling project() in the project's CMakeLists.txt, with the outcome
remaining consistent. This means the user's settings will appear later
and take priority. However, if __build_set_lang_version() is called
after __project(), the user's COMPILE_OPTIONS settings would be
overridden if set before calling project(). Our documentation[4] explicitly
states that COMPILE_OPTIONS and similar properties should be modified
using idf_build_set_property() after calling project() to prevent
default values from overwriting them.

Even with this guidance, some existing components that modify
COMPILE_OPTIONS before invoking project() might be impacted by this
change. Therefore, separate the language standard settings for non-Linux
and Linux targets. For non-Linux targets, these settings are applied in
__build_set_default_build_specifications(), maintaining the current
behavior. For the Linux target, the language standard is set with
__linux_build_set_lang_version() after calling __project(), ensuring the
languages are already enabled in CMake and no warning is issued. Since the Linux
target is still in preview, this approach should be acceptable,
especially with the existing documentation[4].

Closes https://github.com/espressif/esp-idf/issues/15488

[1] https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#languages
[2] https://gitlab.kitware.com/cmake/cmake/-/merge_requests/9396
[3] https://cmake.org/cmake/help/latest/policy/CMP0165.html#policy:CMP0165
[4] https://docs.espressif.com/projects/esp-idf/en/v5.4/esp32/api-guides/
    build-system.html#overriding-default-build-specifications

Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
This commit is contained in:
Frantisek Hrbata
2025-02-28 18:59:09 +01:00
parent 648dc329f6
commit 70407df8c2
2 changed files with 50 additions and 39 deletions

View File

@ -148,6 +148,16 @@ function(__build_set_default_build_specifications)
# always generate debug symbols (even in release mode, these don't
# go into the final binary so have no impact on size
"-ggdb")
if(NOT IDF_TARGET STREQUAL "linux")
# Building for chip targets: we use a known version of the toolchain.
# Use latest supported versions.
# For Linux target -std settings, refer to the __linux_build_set_lang_version
# function, which must be called after project().
# Please update docs/en/api-guides/c.rst, docs/en/api-guides/cplusplus.rst and
# tools/test_apps/system/cxx_build_test/main/test_cxx_standard.cpp when changing this.
list(APPEND c_compile_options "-std=gnu17")
list(APPEND cxx_compile_options "-std=gnu++2b")
endif()
idf_build_set_property(COMPILE_DEFINITIONS "${compile_definitions}" APPEND)
idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND)
@ -155,47 +165,48 @@ function(__build_set_default_build_specifications)
idf_build_set_property(CXX_COMPILE_OPTIONS "${cxx_compile_options}" APPEND)
endfunction()
function(__build_set_lang_version)
function(__linux_build_set_lang_version)
# This must be called after the project() function when languages are
# enabled in CMake. Refer to
# https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#languages
# for more information. We use language-specific functions such as
# check_c_compiler_flag here. Remember, we cannot use enable_language()
# because it must not be invoked before the first call to project().
# Refer to policy CMP0165 for more details.
if(NOT IDF_TARGET STREQUAL "linux")
# Building for chip targets: we use a known version of the toolchain.
# Use latest supported versions.
# Please update docs/en/api-guides/c.rst, docs/en/api-guides/cplusplus.rst and
# tools/test_apps/system/cxx_build_test/main/test_cxx_standard.cpp when changing this.
set(c_std gnu17)
set(cxx_std gnu++2b)
else()
enable_language(C CXX)
# Building for Linux target, fall back to an older version of the standard
# if the preferred one is not supported by the compiler.
set(preferred_c_versions gnu17 gnu11 gnu99)
set(ver_found FALSE)
foreach(c_version ${preferred_c_versions})
check_c_compiler_flag("-std=${c_version}" ver_${c_version}_supported)
if(ver_${c_version}_supported)
set(c_std ${c_version})
set(ver_found TRUE)
break()
endif()
endforeach()
if(NOT ver_found)
message(FATAL_ERROR "Failed to set C language standard to one of the supported versions: "
"${preferred_c_versions}. Please upgrade the host compiler.")
endif()
return()
endif()
set(preferred_cxx_versions gnu++2b gnu++20 gnu++2a gnu++17 gnu++14)
set(ver_found FALSE)
foreach(cxx_version ${preferred_cxx_versions})
check_cxx_compiler_flag("-std=${cxx_version}" ver_${cxx_version}_supported)
if(ver_${cxx_version}_supported)
set(cxx_std ${cxx_version})
set(ver_found TRUE)
break()
endif()
endforeach()
if(NOT ver_found)
message(FATAL_ERROR "Failed to set C++ language standard to one of the supported versions: "
"${preferred_cxx_versions}. Please upgrade the host compiler.")
# Building for Linux target, fall back to an older version of the standard
# if the preferred one is not supported by the compiler.
set(preferred_c_versions gnu99 gnu17 gnu11 gnu99)
set(ver_found FALSE)
foreach(c_version ${preferred_c_versions})
check_c_compiler_flag("-std=${c_version}" ver_${c_version}_supported)
if(ver_${c_version}_supported)
set(c_std ${c_version})
set(ver_found TRUE)
break()
endif()
endforeach()
if(NOT ver_found)
message(FATAL_ERROR "Failed to set C language standard to one of the supported versions: "
"${preferred_c_versions}. Please upgrade the host compiler.")
endif()
set(preferred_cxx_versions gnu++2b gnu++20 gnu++2a gnu++17 gnu++14)
set(ver_found FALSE)
foreach(cxx_version ${preferred_cxx_versions})
check_cxx_compiler_flag("-std=${cxx_version}" ver_${cxx_version}_supported)
if(ver_${cxx_version}_supported)
set(cxx_std ${cxx_version})
set(ver_found TRUE)
break()
endif()
endforeach()
if(NOT ver_found)
message(FATAL_ERROR "Failed to set C++ language standard to one of the supported versions: "
"${preferred_cxx_versions}. Please upgrade the host compiler.")
endif()
idf_build_set_property(C_COMPILE_OPTIONS "-std=${c_std}" APPEND)
@ -241,7 +252,6 @@ function(__build_init idf_path)
idf_build_set_property(IDF_COMPONENT_MANAGER 0)
__build_set_default_build_specifications()
__build_set_lang_version()
# Add internal components to the build
idf_build_get_property(idf_path IDF_PATH)

View File

@ -587,6 +587,7 @@ macro(project project_name)
# The actual call to project()
__project(${project_name} C CXX ASM)
__linux_build_set_lang_version()
# Generate compile_commands.json (needs to come after project call).
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)