Merge branch 'fix/cmakev2_linux_target' into 'master'

fix(cmakev2): linux target fixes

Closes IDFGH-17121

See merge request espressif/esp-idf!45315
This commit is contained in:
Frantisek Hrbata
2026-02-09 14:09:36 +01:00
14 changed files with 103 additions and 48 deletions

View File

@@ -486,6 +486,7 @@ pytest_buildv2_system:
--junitxml ${CI_PROJECT_DIR}/XUNIT_RESULT.xml
--ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME}
--
buildv2
test_non_default_target.py
test_component_manager.py
test_build.py

View File

@@ -165,6 +165,7 @@ pytest_buildv2_system_win:
--junitxml=${CI_PROJECT_DIR}\XUNIT_RESULT.xml
--ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME}
--
buildv2
test_non_default_target.py
test_component_manager.py
test_build.py

View File

@@ -66,7 +66,7 @@ menu "Bluetooth"
menu "Controller Options"
depends on BT_CONTROLLER_ENABLED
source "$IDF_PATH/components/bt/controller/$IDF_TARGET/Kconfig.in"
osource "$IDF_PATH/components/bt/controller/$IDF_TARGET/Kconfig.in"
endmenu
config BT_RELEASE_IRAM

View File

@@ -197,6 +197,11 @@ if(arch STREQUAL "linux")
PROPERTIES COMPILE_OPTIONS
"-Wno-strict-prototypes"
)
if(APPLE)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u _app_main")
else()
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u app_main")
endif()
else()
idf_component_get_property(COMPONENT_DIR freertos COMPONENT_DIR)

View File

@@ -3,6 +3,7 @@ if(NOT "${target}" STREQUAL "linux")
return()
endif()
set(srcs)
set(includes "include")
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
list(APPEND srcs getrandom.c assert_func.c)

View File

@@ -2,6 +2,8 @@ idf_build_get_property(target IDF_TARGET)
set(target_folder "${target}")
set(srcs)
# On Linux the soc component is a simple wrapper, without much functionality
if(NOT ${target} STREQUAL "linux")
set(srcs "lldesc.c"

View File

@@ -715,35 +715,55 @@ endfunction()
.. code-block:: cmake
idf_build_generate_metadata(<binary>
idf_build_generate_metadata([BINARY <binary>]
[EXECUTABLE <executable>]
[OUTPUT_FILE <file>])
*binary[in]*
*BINARY[in,opt]*
Binary target for which to generate a metadata file.
*EXECUTABLE[in,opt]*
Executable target for which to generate a metadata file.
*OUTPUT_FILE[in,opt]*
Optional output file path for storing the metadata. If not provided,
the default path ``<build>/project_description.json`` is used.
Generate metadata for the specified ``binary`` and store it in the
specified ``OUTPUT_FILE``. If no ``OUTPUT_FILE`` is provided, the default
location ``<build>/project_description.json`` will be used.
Generate metadata for the specified ``binary`` or ``executable`` target and
store it in the specified ``OUTPUT_FILE``. If no ``OUTPUT_FILE`` is
provided, the default location ``<build>/project_description.json`` will be
used.
#]]
function(idf_build_generate_metadata binary)
function(idf_build_generate_metadata)
set(options)
set(one_value OUTPUT_FILE)
set(one_value OUTPUT_FILE BINARY EXECUTABLE)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
# The EXECUTABLE_TARGET property is set by the idf_build_binary or
# the idf_sign_binary function.
get_target_property(executable "${binary}" EXECUTABLE_TARGET)
if(NOT executable)
idf_die("Binary target '${binary}' is missing 'EXECUTABLE_TARGET' property.")
if(NOT DEFINED ARG_BINARY AND NOT DEFINED ARG_EXECUTABLE)
idf_die("BINARY or EXECUTABLE option is required")
endif()
__get_executable_library_or_die(TARGET "${executable}" OUTPUT library)
if(DEFINED ARG_BINARY)
# The EXECUTABLE_TARGET property is set by the idf_build_binary or
# the idf_sign_binary function.
get_target_property(ARG_EXECUTABLE "${ARG_BINARY}" EXECUTABLE_TARGET)
if(NOT ARG_EXECUTABLE)
idf_die("Binary target '${ARG_BINARY}' is missing 'EXECUTABLE_TARGET' property.")
endif()
# The BINARY_PATH property is set by the idf_build_binary or
# the idf_sign_binary function.
get_target_property(binary_path ${ARG_BINARY} BINARY_PATH)
if(NOT binary_path)
idf_die("Binary target '${ARG_BINARY}' is missing 'BINARY_PATH' property.")
endif()
get_filename_component(PROJECT_BIN "${binary_path}" NAME)
endif()
__get_executable_library_or_die(TARGET "${ARG_EXECUTABLE}" OUTPUT library)
idf_build_get_property(PROJECT_NAME PROJECT_NAME)
idf_build_get_property(PROJECT_VER PROJECT_VER)
@@ -752,14 +772,7 @@ function(idf_build_generate_metadata binary)
idf_build_get_property(BUILD_DIR BUILD_DIR)
idf_build_get_property(SDKCONFIG SDKCONFIG)
idf_build_get_property(SDKCONFIG_DEFAULTS SDKCONFIG_DEFAULTS)
set(PROJECT_EXECUTABLE "$<TARGET_FILE_NAME:${executable}>")
# The BINARY_PATH property is set by the idf_build_binary or
# the idf_sign_binary function.
get_target_property(binary_path ${binary} BINARY_PATH)
if(NOT binary_path)
idf_die("Binary target '${binary}' is missing 'BINARY_PATH' property.")
endif()
get_filename_component(PROJECT_BIN "${binary_path}" NAME)
set(PROJECT_EXECUTABLE "$<TARGET_FILE_NAME:${ARG_EXECUTABLE}>")
if(NOT PROJECT_BIN)
set(PROJECT_BIN "")
endif()

View File

@@ -885,12 +885,12 @@ function(idf_component_include name)
# helps in detecting and reporting circular dependencies, such as
# C1->C2->C1. In this scenario, C2 can still use the C1 interface target,
# but C1 will only be fully evaluated after C2 has been evaluated.
if("${component_name}" IN_LIST __DEPENDENCY_CHAIN)
idf_dbg("Component '${name}' in circular dependency chain '${__DEPENDENCY_CHAIN}'")
if("${component_interface}" IN_LIST __DEPENDENCY_CHAIN)
idf_dbg("Component '${component_interface}' in circular dependency chain '${__DEPENDENCY_CHAIN}'")
return()
endif()
list(APPEND __DEPENDENCY_CHAIN "${name}")
list(APPEND __DEPENDENCY_CHAIN "${component_interface}")
# Evaluate the CMakeLists.txt file of the component.
idf_component_get_property(component_build_dir "${component_name}" COMPONENT_BUILD_DIR)
add_subdirectory("${component_directory}" "${component_build_dir}")

View File

@@ -266,16 +266,15 @@ endfunction()
#[[
__init_toolchain()
Determine the IDF_TOOLCHAIN value from the IDF_TOOLCHAIN environment
variable or the CMake cache variable. If none of these are set, use the
default gcc toolchain. Ensure there are no inconsistencies in the
IDF_TOOLCHAIN values set in different locations. Also ensure that the
Determine the toolchain file, set IDF_TOOLCHAIN_FILE build property and
global CMAKE_TOOLCHAIN_FILE CMake variable. Also ensure that the
CMAKE_TOOLCHAIN_FILE is set to the correct file according to the current
IDF_TARGET.
Set the IDF_TOOLCHAIN and IDF_TOOLCHAIN_FILE build properties. Also,
configure the IDF_TOOLCHAIN CMake cache variable and set the
CMAKE_TOOLCHAIN_FILE global variable.
Note: The IDF_TOOLCHAIN build property is set after the toolchain
configuration in ``idf_project_init``. The ``tools/cmake/toolchain.cmake``
is included in the toolchain file and it sets the IDF_TOOLCHAIN variable in
CMake's cache.
#]]
function(__init_toolchain)
set(cache_toolchain $CACHE{IDF_TOOLCHAIN})
@@ -318,9 +317,7 @@ function(__init_toolchain)
idf_die("Toolchain file ${toolchain_file} not found")
endif()
set(IDF_TOOLCHAIN ${toolchain} CACHE STRING "IDF Build Toolchain Type")
set(CMAKE_TOOLCHAIN_FILE "${toolchain_file}" PARENT_SCOPE)
idf_build_set_property(IDF_TOOLCHAIN "${toolchain}")
idf_build_set_property(IDF_TOOLCHAIN_FILE "${toolchain_file}")
endfunction()

View File

@@ -407,9 +407,10 @@ function(__create_executable_config_env_file executable)
foreach(component_interface IN LISTS component_interfaces)
__idf_component_get_property_unchecked(component_kconfig "${component_interface}" __KCONFIG)
__idf_component_get_property_unchecked(component_projbuild "${component_interface}" __KCONFIG_PROJBUILD)
__idf_component_get_property_unchecked(component_real_target "${component_interface}" COMPONENT_REAL_TARGET)
if(component_kconfig)
if("${component_interface}" IN_LIST component_interfaces_linked)
if("${component_interface}" IN_LIST component_interfaces_linked AND component_real_target)
list(APPEND kconfigs "${component_kconfig}")
else()
list(APPEND kconfigs_excluded "${component_kconfig}")
@@ -417,7 +418,7 @@ function(__create_executable_config_env_file executable)
endif()
if(component_projbuild)
if("${component_interface}" IN_LIST component_interfaces_linked)
if("${component_interface}" IN_LIST component_interfaces_linked AND component_real_target)
list(APPEND kconfigs_projbuild "${component_projbuild}")
else()
list(APPEND kconfigs_projbuild_excluded "${component_projbuild}")

View File

@@ -560,6 +560,11 @@ macro(idf_project_init)
# Ensure this function is executed only once throughout the entire
# project.
# The IDF_TOOLCHAIN variable is established as a CMake cache variable
# during the toolchain initialization process in
# ``tools/cmake/toolchain.cmake``.
idf_build_set_property(IDF_TOOLCHAIN "${IDF_TOOLCHAIN}")
# Warn about the use of deprecated variables.
deprecate_variable(COMPONENTS)
deprecate_variable(EXCLUDE_COMPONENTS)
@@ -707,7 +712,7 @@ function(__project_default)
COMPONENTS main
MAPFILE_TARGET "${executable}_mapfile")
if(CONFIG_APP_BUILD_GENERATE_BINARIES)
if(CONFIG_APP_BUILD_GENERATE_BINARIES AND TARGET idf::esptool_py)
# Is it possible to have a configuration where
# CONFIG_APP_BUILD_GENERATE_BINARIES is not set?
@@ -726,7 +731,7 @@ function(__project_default)
TARGET app-flash
NAME "app"
FLASH)
idf_build_generate_metadata("${executable}_binary_signed")
idf_build_generate_metadata(BINARY "${executable}_binary_signed")
else()
idf_build_binary("${executable}"
OUTPUT_FILE "${build_dir}/${executable}.bin"
@@ -743,10 +748,12 @@ function(__project_default)
idf_create_dfu("${executable}_binary"
TARGET dfu)
idf_build_generate_metadata("${executable}_binary")
idf_build_generate_metadata(BINARY "${executable}_binary")
endif()
idf_build_generate_flasher_args()
else()
idf_build_generate_metadata(EXECUTABLE "${executable}")
endif()
idf_create_menuconfig("${executable}"

View File

@@ -232,7 +232,7 @@ function(test_executable)
NAME fatfs_example)
idf_create_menuconfig(fatfs_example
TARGET menuconfig-fatfs)
idf_build_generate_metadata(fatfs_example_bin
idf_build_generate_metadata(BINARY fatfs_example_bin
OUTPUT_FILE project_description_fatfs.json)
idf_build_generate_depgraph(fatfs_example
OUTPUT_FILE component_deps_fatfs.dot)
@@ -257,7 +257,7 @@ function(test_executable)
NAME hello_world_example)
idf_create_menuconfig(hello_world_example
TARGET menuconfig-hello_world)
idf_build_generate_metadata(hello_world_example_bin
idf_build_generate_metadata(BINARY hello_world_example_bin
OUTPUT_FILE project_description_hello_world.json)
idf_build_generate_depgraph(hello_world_example
OUTPUT_FILE component_deps_hello_world.dot)

View File

@@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import logging
import sys
import pytest
from test_build_system_helpers import IdfPyFunc
@pytest.mark.skipif(sys.platform == 'win32', reason='Unix test')
@pytest.mark.usefixtures('test_app_copy')
def test_linux_target_build(idf_py: IdfPyFunc) -> None:
logging.info('Can build for Linux target')
idf_py('--preview', '-DIDF_TARGET=linux', 'build')

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import datetime
import logging
@@ -241,10 +241,23 @@ def idf_py(default_idf_env: EnvDict) -> IdfPyFunc:
def pytest_collection_modifyitems(session: Session, config: Config, items: list[Item]) -> None:
if not config.getoption('--buildv2', False):
return
buildv2_dir = Path(__file__).parent / 'buildv2'
is_buildv2 = config.getoption('--buildv2', False)
for item in items:
marker = item.get_closest_marker('buildv2_skip')
if marker:
reason = marker.args[0] if marker.args else 'Skipped as this test is specific to build system v1.'
item.add_marker(pytest.mark.skip(reason=reason))
if is_buildv2:
marker = item.get_closest_marker('buildv2_skip')
if marker:
reason = marker.args[0] if marker.args else 'Skipped as this test is specific to build system v1.'
item.add_marker(pytest.mark.skip(reason=reason))
else:
if buildv2_dir in item.path.parents or item.path == buildv2_dir:
item.add_marker(pytest.mark.skip(reason='Skipped as build system v2 tests are disabled.'))
def pytest_report_header(config: Config) -> str:
"""Add a clear header to the terminal output whether buildv1 or buildv2 testing is in progress."""
if config.getoption('--buildv2'):
return 'Testing ESP-IDF CMake-based build system v2'
else:
return 'Testing ESP-IDF CMake-based build system v1'