# Build System v2

Attention

Build System v2 is currently available as a Technical Preview intended for testing and evaluation. Features, functionality, and performance are subject to change without notice, and production use is not recommended at this stage.

ESP-IDF CMake-based build system v2, referred to in this documentation simply as v2 or build system, is a successor to the original CMake-based Build System, referred to as v1. The v2 addresses limitations introduced in the previous version while trying to maintain backward compatibility for components written for v1. The most significant changes include the ability to use Kconfig variables to specify component dependencies, the removal of early component evaluation using CMake script mode, and support for writing components using the native CMake approach. While v2 aims to be as backward compatible with v1 as possible, meaning most components written for v1 should work without modification with v2, there are design differences between v1 and v2 that may require changes in v1 components to work with v2. The incompatibilities are described in Breaking Changes for v1 Components.

Example applications for Build System v2 are described in the Build System v2 examples README .

## Creating a New Project

Below is the minimal project's `CMakeLists.txt` file, which should suffice for most projects:

    cmake_minimum_required(VERSION 3.22)
    include($ENV{IDF_PATH}/tools/cmakev2/idf.cmake)
    project(myProject C CXX ASM)
    idf_project_default()

The order of commands in the project's `CMakeLists.txt` is important. First, the `cmake_minimum_required` command must be called to specify the minimum required version of CMake.

Next, include `idf.cmake`, which provides the build system functionality. It performs the necessary build system initialization tasks and toolchain configuration, which must be completed before invoking CMake’s `project()` command.

Once the build system is initialized, the `project()` command should be executed. It sets up project related variables, initializes the toolchain for the required languages, and performs other CMake specific initialization steps.

Finally, the idf_project_default function creates the default project executable based on the `main` component and its transitive dependencies. It also generates binary images and adds related targets such as `flash` and `menuconfig`.

## Creating a New Component

This section explains how to create a new component. The preferred and generally sufficient method is described in Creating a Component Compatible with v1 and v2. However, if you need more control or prefer a native CMake approach without concern for v1 compatibility, see Creating a Component for v2 Only.

Important

Always ensure that all variables used within the component's `CMakeLists.txt` are initialized before use. The component might be evaluated in the context of other component where some variables may already be set. A common example is appending to CMake's list without explicitly initializing the list variable first.

### Creating a Component Compatible with v1 and v2

The recommended method for creating a new component is to use the idf_component_register function. This function is provided in v2 to maintain backward compatibility with v1 and remains the preferred approach for creating components that work with both versions. The only difference between the v1 and v2 versions of this function is that the v2 version does not support the `KCONFIG` and `KCONFIG_PROJBUILD` options when using Kconfig filenames that do not follow the Standardized Kconfig and Kconfig.projbuild File Names. For components that require functionality beyond what idf_component_register provides, please refer to Breaking Changes for v1 Components.

Minimal Component CMakeLists.txt

    idf_component_register(SRCS "foo.c" "bar.c"
                           INCLUDE_DIRS "include"
                           REQUIRES mbedtls)

### Creating a Component for v2 Only

Important

This section describes how to create a component for v2, which is not compatible or usable with v1. To create a component that works with both versions, it is recommended to follow Creating a Component Compatible with v1 and v2.

This section demonstrates how to create a new component using a simple component named `esp_target_info` as an example. This component provides the `print_esp_target_info` function, which, when invoked, displays basic information about the target.

The layout of the component's directory is organized as follows:

    esp_target_info
    ├── include
    │   └── esp_target_info.h
    ├── srcs
    │   └── esp_target_info.c
    ├── CMakeLists.txt
    ├── esp_target_info.ld
    └── linker.lf

The `esp_target_info.h` header file provides the `print_esp_target_info` function declaration as its sole public interface, intended for use by other components.

include/esp_target_info.h

    #ifndef _ESP_TARGET_INFO_
    #define _ESP_TARGET_INFO_
    void print_esp_target_info(void);
    #endif

The `esp_target_info.c` file contains the definition for the `print_esp_target_info` function. This function outputs the target name based on the configuration `CONFIG_IDF_TARGET` variable, the number of CPU cores as obtained from the `esp_hw_support` component, the available free heap size from the `esp_system` component, and the flash size from the `spi_flash` component.

srcs/esp_target_info.c

    #include <stdio.h>
    #include <inttypes.h>
    #include "esp_chip_info.h"
    #include "esp_flash.h"
    #include "esp_system.h"

    void esp_target_chip_info(esp_chip_info_t*);

    void print_esp_target_info(void)
    {
        esp_chip_info_t chip_info;
        uint32_t flash_size = 0;

        esp_target_chip_info(&chip_info);
        esp_flash_get_size(NULL, &flash_size);

        printf("target: %s\n", CONFIG_IDF_TARGET);
        printf("cpu cores: %d\n", chip_info.cores);
        printf("free heap size: %" PRIu32 "\n", esp_get_minimum_free_heap_size());
        printf("flash size: %" PRIu32 "B\n", flash_size);
    }

The `esp_target_info.ld` linker script defines an additional symbol, `esp_target_chip_info`, which serves as an alias for the `esp_chip_info` function. This alias is used in `esp_target_info.c` to obtain chip information instead of directly calling the `esp_chip_info` function. This serves no real purpose and is merely an example of a working linker script used in the demonstration.

esp_target_info.ld

    esp_target_chip_info = esp_chip_info;

The `linker.lf` is a linker fragment that determines the placement of `print_esp_target_info` in IRAM instead of flash. This is merely an example for demonstration purposes. For more details on linker fragments, please refer to Linker Script Generation.

linker.lf

    [mapping:esp_target_info]
    archive: libesp_target_info.a
    entries:
        print_esp_target_info (noflash)

Now, let's describe how the aforementioned sources can be integrated as a component for the build system by defining the component's `CMakeLists.txt` file.

Create a static library target with the target name stored in the `COMPONENT_TARGET` variable for the `srcs/esp_target_info.c` source file, and add the `include` directory as the component's directory with public header files.

CMakeLists.txt

     1add_library(${COMPONENT_TARGET} STATIC
     2    "srcs/esp_target_info.c"
     3)
     4
     5target_include_directories(${COMPONENT_TARGET} PUBLIC
     6    ${CMAKE_CURRENT_LIST_DIR}/include
     7)
     8
     9idf_component_include(esp_hw_support)
    10idf_component_include(spi_flash)
    11idf_component_include(esp_system)
    12
    13target_link_libraries(${COMPONENT_TARGET} PRIVATE
    14    idf::esp_hw_support
    15    idf::spi_flash
    16    idf::esp_system
    17)
    18
    19idf_component_set_property(${COMPONENT_TARGET} WHOLE_ARCHIVE TRUE)
    20idf_component_set_property(${COMPONENT_TARGET} LDFRAGMENTS linker.lf APPEND)
    21idf_component_set_property(${COMPONENT_TARGET} LINKER_SCRIPTS esp_target_info.ld APPEND)
    22
    23target_link_options(${COMPONENT_TARGET} INTERFACE "SHELL:-u esp_chip_info")

When a component is evaluated by the build system, the `COMPONENT_TARGET` variable is assigned a target name that the component is responsible for creating. This target represents the component within the build and is linked to the component interface target managed by the build system. The component interface target is what other components use to declare dependencies on this component. Other components can obtain a component’s interface target using the idf_component_include function and link to it as needed.

Include all required components and link them to the component's target.

CMakeLists.txt

     1add_library(${COMPONENT_TARGET} STATIC
     2    "srcs/esp_target_info.c"
     3)
     4
     5target_include_directories(${COMPONENT_TARGET} PUBLIC
     6    ${CMAKE_CURRENT_LIST_DIR}/include
     7)
     8
     9idf_component_include(esp_hw_support)
    10idf_component_include(spi_flash)
    11idf_component_include(esp_system)
    12
    13target_link_libraries(${COMPONENT_TARGET} PRIVATE
    14    idf::esp_hw_support
    15    idf::spi_flash
    16    idf::esp_system
    17)
    18
    19idf_component_set_property(${COMPONENT_TARGET} WHOLE_ARCHIVE TRUE)
    20idf_component_set_property(${COMPONENT_TARGET} LDFRAGMENTS linker.lf APPEND)
    21idf_component_set_property(${COMPONENT_TARGET} LINKER_SCRIPTS esp_target_info.ld APPEND)
    22
    23target_link_options(${COMPONENT_TARGET} INTERFACE "SHELL:-u esp_chip_info")

Note

The v1 automatically adds component dependencies in certain situations. For instance, if the `main` component does not specify its dependencies, all components identified by the build system are added as dependencies to the `main` component. Additionally, component dependencies specified in `idf_component.yaml` for the component manager are also added as dependencies to the component. In contrast, the v2 does not automatically add any dependencies, all component dependencies must be explicitly stated.

The `esp_target_info` component uses functionality from other components, namely `esp_hw_support`, `esp_system`, and `spi_flash`, to collect basic information about the target. Therefore, it must declare its dependencies on these components. The component acquires the required component interface targets using the idf_component_include function.

Each component is evaluated exactly once by the idf_component_include function. This function essentially invokes CMake’s `add_subdirectory` for the specified component directory and links the `COMPONENT_TARGET` created by that component with its interface target.

The interface target can then be used by other components to declare dependencies. It can be referenced directly via a predefined alias named `idf::<component name>`, which provides a convenient and literal way to reference a component’s interface target, as shown in the example above.

The idf_component_include function also supports an `INTERFACE` option that allows storing the component’s interface target name in a variable. This approach is useful in more complex cases where the target needs to be passed to other CMake functions, stored in lists, or referenced programmatically.

Declaring component dependencies using interface target variable

    idf_component_include(spi_flash INTERFACE spi_flash_iface)

    target_link_libraries(${COMPONENT_TARGET} PRIVATE
        ${spi_flash_iface}
    )

Component dependencies can be conditionally expressed based on the configuration. When a component's `CMakeLists.txt` is evaluated by the build system, the configuration for all discovered components is available. If a component needs to declare a conditional dependency based on a configuration variable, it must ensure that the component is included in the build system first before using it by including the component with the idf_component_include function. For example, if a component has a conditional dependency on the `vfs` component based on the `CONFIG_VFS_SUPPORT_IO` configuration option being set, it needs to include the `vfs` component before linking it to the component target.

Declaring optional component dependency

    if(CONFIG_VFS_SUPPORT_IO)
        idf_component_include(vfs)
        target_link_libraries(${COMPONENT_TARGET} PRIVATE
            idf::vfs
        )
    endif()

Important

Always ensure that the required component is included before using it. The presence of certain component configuration options does not guarantee that the component will be included in the project build.

Set miscellaneous component properties. This primarily serves as an example to demonstrate other commonly used features that the component may configure.

CMakeLists.txt

     1add_library(${COMPONENT_TARGET} STATIC
     2    "srcs/esp_target_info.c"
     3)
     4
     5target_include_directories(${COMPONENT_TARGET} PUBLIC
     6    ${CMAKE_CURRENT_LIST_DIR}/include
     7)
     8
     9idf_component_include(esp_hw_support)
    10idf_component_include(spi_flash)
    11idf_component_include(esp_system)
    12
    13target_link_libraries(${COMPONENT_TARGET} PRIVATE
    14    idf::esp_hw_support
    15    idf::spi_flash
    16    idf::esp_system
    17)
    18
    19idf_component_set_property(${COMPONENT_TARGET} WHOLE_ARCHIVE TRUE)
    20idf_component_set_property(${COMPONENT_TARGET} LDFRAGMENTS linker.lf APPEND)
    21idf_component_set_property(${COMPONENT_TARGET} LINKER_SCRIPTS esp_target_info.ld APPEND)
    22
    23target_link_options(${COMPONENT_TARGET} INTERFACE "SHELL:-u esp_chip_info")

WHOLE_ARCHIVE:

  Include all object files from the component's static library when linked.

LDFRAGMENTS:

  Include files from this property to be processed with the Linker Script Generation.

LINKER_SCRIPTS:

  Include files from this property in the link command line as linker scripts.

Also ensure that the `esp_chip_info` function is retained in the final binary even when section garbage collection, `--gc-sections`, is enabled. This is required because `esp_target_info.ld` defines `esp_target_chip_info` as an alias for `esp_chip_info`, and without forcing the linker to include it, the underlying `esp_chip_info` function could be discarded as unused.

## Build Event Callback Framework

The build system allows components to register callbacks that are invoked at specific points in the build lifecycle. This provides a generic way for components to run custom steps (for example, running a tool on the executable or adding dependencies) without relying on internal build targets or properties.

Components register a callback in their `project_include.cmake` using idf_component_register_build_event_callback. The callback must be a CMake function defined in the same file. At the specified event, the build system invokes the callback and passes the relevant CMake target as the first argument (for example, the executable target for `POST_ELF`).

Currently supported events:

- POST_ELF — Fired after the executable target is created and linked, but before the binary (`.bin`) image is generated. The callback receives the executable target name. Use this to perform actions on the ELF by attaching a `POST_BUILD` command to the executable with `add_custom_command(TARGET ... POST_BUILD ...)`.

Example: perform actions on the ELF after linking:

    # In project_include.cmake
    function(my_post_elf_hook target)
        add_custom_command(TARGET ${target} POST_BUILD
            COMMAND my_tool "$<TARGET_FILE:${target}>"
            COMMENT "Running my_tool on the executable")
    endfunction()

    idf_component_register_build_event_callback(EVENT POST_ELF CALLBACK my_post_elf_hook)

Additional build events may be added in future when required.

## Breaking Changes for v1 Components

While most of the v1 components should be usable without any modifications in v2, there are some differences between v1 and v2 that might require changes to v1 components. This section outlines the breaking changes that may occur if a v1 component is used with v2 and explains how the component can be modified to work with both v1 and v2.

### Updating Component for v2 Compatibility

If a component needs to be evaluated with both v1 and v2, and requires modifications to function with v2, the `IDF_BUILD_V2` variable can be used by the component to determine whether it is being evaluated under v1 or v2, and adjust its behavior accordingly. The `IDF_BUILD_V2` is set when the component is evaluated with v2. This variable can be used to conditionally adjust only small parts of the existing v1 component, or a completely new `CMakeLists.txt` can be created for v2 and conditionally included in the v1 component's `CMakeLists.txt`. For more information on how to write a v2 component, please see Creating a New Component.

Below is the `hello_world` example component with its `CMakeLists.txt` adjusted to include and evaluate the separate v2 component `CMakeLists_v2.txt` if evaluated under v2.

Note

The existing v1 `hello_world` component should function without modification in v2. This is just a simple example of how `IDF_BUILD_V2` can be used to evaluate different component's `CMakeLists.txt` files for v1 and v2.

Adjusted original v1 hello_world example `CMakeLists.txt`.

    if(IDF_BUILD_V2)
        # Include component CMake code for v2 and return.
        include(CMakeLists_v2.txt)
        return()
    endif()

    # Here follows the original component CMake code for v1.
    idf_component_register(SRCS "hello_world_main.c"
                           PRIV_REQUIRES spi_flash
                           INCLUDE_DIRS "")

The hello_world example `CMakeLists_v2.txt` for v2.

    idf_component_include(spi_flash)

    add_library(${COMPONENT_TARGET} STATIC
        "hello_world_main.c"
    )

    target_include_directories(${COMPONENT_TARGET} PUBLIC
        "${CMAKE_CURRENT_LIST_DIR}"
    )

    target_link_libraries(${COMPONENT_TARGET} PRIVATE
        idf::spi_flash
    )

### `idf_build_add_post_elf_dependency` and `idf_build_get_post_elf_dependencies` are Unavailable

In v1, components that need to run a step after the executable is linked but before the binary image is generated use `idf_build_add_post_elf_dependency` to register a dependency and `idf_build_get_post_elf_dependencies` to retrieve the list of such dependencies (see the build system API). These functions are not available in v2.

In v2, use the Build Event Callback Framework instead. Register a POST_ELF callback with idf_component_register_build_event_callback in your component's `project_include.cmake`. The callback receives the executable target name; use it to attach a `POST_BUILD` command (e.g. with `add_custom_command(TARGET ... POST_BUILD ...)`) or to add custom targets that depend on the executable. This achieves the same ordering (run after ELF, before binary) without relying on internal build properties.

### The `BUILD_COMPONENTS` Build Property is Unavailable

The v1, by design, collects components built by the project in the `BUILD_COMPONENTS` build property. This is achieved by evaluating components early using the CMake script mode and collecting components based on their dependencies as provided in idf_component_register. Later, the `BUILD_COMPONENTS` are evaluated again using CMake's `add_subdirectory`. This approach imposes several restrictions. First, components cannot express their dependencies based on Kconfig variables because Kconfig variables are not known during the early evaluation. Second, the CMake script mode does not allow CMake commands that define build targets or actions, which caused confusion about which commands can be used in a component and when they are actually evaluated. The v2 removes this two-stage component evaluation, and hence the `BUILD_COMPONENTS` build property doesn't exist in v2. Any attempt to obtain the `BUILD_COMPONENTS` build property with idf_build_get_property will result in an error. The `BUILD_COMPONENTS` build property was primarily used in v1 by components to discover which other components were being built by the project and to adjust their behavior, for example, by adding additional source files. In v2, the functionality of the `BUILD_COMPONENTS` build property can be replaced with the `$<TARGET_EXISTS:tgt>` generator expression.

For example, the `esp_eth` component is compiled with the additional `esp_eth_netif_glue.c` file if the `esp_netif` component is also being built in v1 with:

    idf_build_get_property(components_to_build BUILD_COMPONENTS)
    if(esp_netif IN_LIST components_to_build)
        list(APPEND srcs "src/esp_eth_netif_glue.c")
    endif()

To use the `esp_eth` component with v2, the usage of `BUILD_COMPONENTS` has to be replaced with:

    if(IDF_BUILD_V2)
        target_sources(${COMPONENT_TARGET} PRIVATE "$<$<TARGET_EXISTS:idf::esp_netif>:src/esp_eth_netif_glue.c>")
    else()
        idf_build_get_property(components_to_build BUILD_COMPONENTS)
        if(esp_netif IN_LIST components_to_build)
            list(APPEND srcs "src/esp_eth_netif_glue.c")
        endif()
    endif()

Note

The `$<TARGET_EXISTS:tgt>` generator expression should work for both v1 and v2. The `IDF_BUILD_V2` is used to demonstrate the different approaches in v1 and v2.

### Standardized `Kconfig` and `Kconfig.projbuild` File Names

The v1 allows the use of `Kconfig` and `Kconfig.projbuild` with custom file names in idf_component_register. This is possible because, in v1, the components participating in the project build are collected during early component evaluation, allowing for the specification of custom names for the Kconfig files. The v2 does not perform early evaluation, and the Kconfig files are collected based on the fixed `Kconfig` and `Kconfig.projbuild` file names, which must be present in the component's root directory. This can be resolved by renaming the Kconfig files that do not conform to this naming convention or by including them in Kconfig files that comply with this convention.

### Component Configuration Visibility

In v1, the `sdkconfig` is generated based on Kconfig files provided by components that are part of the project build. This means that if a component is not included in the project build, for instance, if it is not required as a dependency of another component, its configuration will not be visible or available when components are evaluated. In v2, the `sdkconfig` is generated based on Kconfig files for all available components. This means the presence of some configuration options does not guarantee that the component providing this configuration option is participating in the build, unlike in v1.

For example, in v1, the following condition in the `esp_vfs_console` component links the `vfs` component to the `esp_vfs_console` component based on the `CONFIG_VFS_SUPPORT_IO` configuration option being set. This works in v1 because the `CONFIG_VFS_SUPPORT_IO` configuration option is not available if the `vfs` component is not participating in the project build.

    if(CONFIG_VFS_SUPPORT_IO)
        target_link_libraries(${COMPONENT_LIB} PUBLIC idf::vfs)
        # Make sure esp_vfs_console_register gets called at startup stage
        target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_vfs_include_console_register")
    endif()

In contrast, in v2, configuration for all discovered components is available. This means that even if a component does not participate in the project build, its configuration will still be accessible. If the `CONFIG_VFS_SUPPORT_IO` configuration option is set by default, the condition will evaluate as true in v2, even if the `vfs` component is not participating in the project build and the `idf::vfs` target does not exist. The `$<TARGET_EXISTS:tgt>` generator expression can be used to express such dependencies in v2.

    if(IDF_BUILD_V2)
        target_link_libraries(${COMPONENT_LIB} PUBLIC "$<$<TARGET_EXISTS:idf::vfs>:idf::vfs>")
        # Make sure esp_vfs_console_register gets called at startup stage
        target_link_libraries(${COMPONENT_LIB} INTERFACE "$<$<TARGET_EXISTS:idf::vfs>:-u esp_vfs_include_console_register>")
    else()
        if(CONFIG_VFS_SUPPORT_IO)
            target_link_libraries(${COMPONENT_LIB} PUBLIC idf::vfs)
            # Make sure esp_vfs_console_register gets called at startup stage
            target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_vfs_include_console_register")
        endif()
    endif()

Note

The `$<TARGET_EXISTS:tgt>` generator expression should work for both v1 and v2. The `IDF_BUILD_V2` is used to demonstrate the different approaches in v1 and v2.

Important

This change applies not only to a component's `CMakeLists.txt` but also to its source files. The component's code can no longer assume that the functionality of other components is available simply because their Kconfig variables are set. For example, if the `CONFIG_VFS_SUPPORT_IO` configuration variable is set and the component's code depends on the functionality of the `vfs` component, it cannot merely check `CONFIG_VFS_SUPPORT_IO` in the source code. It must ensure that the `vfs` component is included in the project build and that the component has a declared dependency on the `vfs` component in its `CMakeLists.txt`.

### Recursive Evaluation of Components

In v1, the components participating in the project build are collected in the `BUILD_COMPONENTS` build property during the early evaluation phase, prior to their evaluation with `add_subdirectory`. This means that v1 is aware of all components participating in the project build and their dependencies before CMake evaluates them with the `add_subdirectory` call. This allows v1 to evaluate components in a non-recursive manner and within a relatively predictable environment with minimal variables set. In contrast, v2 does not perform early component evaluation and components are added based on the requirements observed during their evaluation. This means a component can be evaluated recursively within the scope of another component's variables if it is a dependency of that component. In other words, if component A requires component B, then when B is evaluated, it can access the variables of A. Hence, it's important to ensure that all variables used by the component are properly initialized before usage. A typical problem involves CMake lists and the `APPEND` operation.

    # Wrong
    list(APPEND srcs main.c)

    # Correct
    set(srcs)
    list(APPEND srcs main.c)

### The `project_include.cmake` Files are Included in a Non-Specific Order

In v1, the `project_include.cmake` files from components are included in the order specified by the `BUILD_COMPONENTS` build property. The components in `BUILD_COMPONENTS` are sorted based on their requirements, as determined during early evaluation. This means a given component's `project_include.cmake` is included only after all `project_include.cmake` files for its dependencies have been included. This holds true as long as there is no cyclic dependency between components. In contrast, v2 does not perform early evaluation of components, and `BUILD_COMPONENTS` does not exist. Therefore, in v2, the `project_include.cmake` files are included for all discovered components, not just those participating in the project build, and they are included in the order in which the components are discovered by v2. As a result, cross-file functionality at the global scope between `project_include.cmake` files can no longer be relied upon in v2.

Note

It is still possible to call functions or macros defined in another `project_include.cmake` file, provided they are invoked within a CMake function or another non-global scope. Only global-scope interactions are unreliable in v2.

### Strict Component Precedence

The v2 strictly adheres to the component precedence for components with the same name, as described in Multiple Components with the Same Name. While v1 allows components discovered in directories specified with the `EXTRA_COMPONENT_DIRS` variable to be overridden by Local Directory Dependencies specified in the `idf_component.yml` manifest file, this is no longer possible in v2.

### The Behavior of `idf_component_optional_requires` has Changed

In v1, the `idf_component_optional_requires` function adds a dependency on a specified component only if that component is already included in the build (for instance, if it is already required by another component). To achieve this, v1 examines the `BUILD_COMPONENTS` build property, which is generated during the early evaluation phase and lists all components involved in the build.

In v2, there is no early collection phase and `BUILD_COMPONENTS` does not exist. The build system discovers components as it evaluates dependencies. So v2 cannot use the same "only if already in the build" check; it has to choose a different rule.

The build system supports two behaviors, controlled by the `IDF_COMPONENT_OPTIONAL_REQUIRES_MODE` build property:

- IMMEDIATE (default) — When a component calls `idf_component_optional_requires(type req_component)`, the build system includes `req_component` and links it to the caller if it is recognized (discovered). No check is made whether the rest of the project actually needs that component. This is safe for multi-binary projects (multiple executables or binaries), but it can pull in more components than necessary and increase build time.

- DEFERRED — The build system does not include or link immediately. It records the request and resolves it later in `idf_build_library`: the optional component is linked only if it ends up in that library's dependency graph. This matches v1 semantics and keeps the number of linked components minimal. It must not be used when building more than one library (see below).

A multi-binary project is one that creates more than one executable or binary (for example, several application executables built from the same tree). Such a project calls `idf_build_library` or `idf_build_executable` more than once. In v2, component targets are shared globally across all libraries. If `IDF_COMPONENT_OPTIONAL_REQUIRES_MODE` is set to DEFERRED, the build system resolves optional requirements when it processes each library. When it processes the second or a later library, it may add new links to component targets that are already used by the first library. The first library's metadata (such as the list of linker fragments or linked components) was already computed when that library was processed and is not updated. As a result, linker script generation and section placement for the first library can be incorrect or stale. For this reason, DEFERRED mode is not allowed when more than one library is built; the build fails with an error in that case. IMMEDIATE mode does not have this problem, because optional requirements are applied during component evaluation, before any per-library metadata is computed. The side effect of the IMMEDIATE mode is that it can pull in more components than necessary and increase build time.

idf_project_default (the usual entry point for a single-executable project) sets `IDF_COMPONENT_OPTIONAL_REQUIRES_MODE` to DEFERRED before building the default executable when no libraries have been created yet. So if your project uses `idf_project_default()` and builds only one executable, you get DEFERRED behavior automatically and do not need to do anything.

If you do not use idf_project_default and instead call `idf_project_init` and then the lower-level API (`idf_build_executable`, `idf_build_library`) yourself, the default mode is IMMEDIATE. If you build only one library/executable and want the same efficient, v1-like behavior as `idf_project_default`, you must set the mode to DEFERRED yourself after project init:

    idf_project_init()
    idf_build_set_property(IDF_COMPONENT_OPTIONAL_REQUIRES_MODE DEFERRED)
    idf_build_executable(my_app COMPONENTS main ...)
    # ... rest of your project ...

Do not set `IDF_COMPONENT_OPTIONAL_REQUIRES_MODE` to `DEFERRED` if you build multiple libraries; the build will error. Keep the default IMMEDIATE in that case.

## API Reference

This section contains an automatically generated API reference from the build system v2 CMake source code.

### Variables

### Functions

idf_build_get_property

      idf_build_get_property(<var> <property> [GENERATOR_EXPRESSION])

  variable[out]

  > Variable to store the value in.

  property[in]

  > Property name to get the value of.

  GENERATOR_EXPRESSION[in]

  > Obtain the generator expression for the property rather than the actual value.

  Get the value of the specified property related to the ESP-IDF build.

idf_build_set_property

      idf_build_set_property(<property> <value> [APPEND])

  property[in]

  > Property name.

  value[in]

  > Property value.

  APPEND

  > Append the value to the property's current value instead of replacing it.

  Set the value of the specified property related to the ESP-IDF build. The property is also added to the internal list of build properties if it isn't already there.

idf_component_get_property

      idf_component_get_property(<variable> <component> <property> [GENERATOR_EXPRESSION])

  variable[out]

  > Variable to store the value in.

  component[in]

  > Component name, target, target alias or interface.

  property[in]

  > Property name to get the value of.

  GENERATOR_EXPRESSION

  > Obtain the generator expression for the property rather than the actual value.

  Retrieve the value of the specified component property.

idf_component_include

      idf_component_include(<name>
                            [INTERFACE <variable>])

  name[in]

  > Component name.

  INTERFACE[out,opt]

  > Optional variable where the name of the target interface for the included component will be stored.

  This is a core function of the build system, responsible for including the specified component, as indicated by the `name` option, into the build process. It does this by evaluating the component through the `add_subdirectory` call for the component's directory. The component is responsible for creating its own CMake target, with the target name provided via the `COMPONENT_TARGET` variable. This target is linked to the component's interface target, which is managed internally by the build system. The component's interface target is used by other components to establish their dependencies.

  When the `INTERFACE` variable is provided, the name of the included component interface target will be stored in it.

idf_component_optional_requires

      idf_component_optional_requires(<type> <component>...)

  type[in]

  > Type of dependency: PRIVATE, PUBLIC or INTERFACE

  component[in]

  > The component name that should be added as a dependency to the evaluated component. It may be provided multiple times.

  Add a dependency on a specific component only if the component is recognized by the build system. The behavior is controlled by the `IDF_COMPONENT_OPTIONAL_REQUIRES_MODE` build property:

  - IMMEDIATE (default): Include the component and link it to the caller if it is discovered. Safe for multi-library projects but may pull in more components than strictly needed.

  - DEFERRED: Do not include or link immediately; record the request and resolve it in `idf_build_library` so that the component is linked only when it is part of the library's dependency graph. Matches v1 semantics and reduces unnecessary components, but must not be used when building multiple libraries (see docs).

  Note

  This function should be avoided in cmakev2, where dependencies should be added based on configuration options. This is purely for backward compatibility with cmakev1.

  Warning

  In multi-library projects with DEFERRED mode, optional requires resolved when processing a later library apply globally to shared component targets. Earlier libraries then link that optional component too, but their per-library metadata (e.g. linker fragments) was already computed and is not updated. DEFERRED mode is therefore disallowed when more than one library is built.

idf_component_register

      idf_component_register([SRCS <file>..]
                             [SRC_DIRS <dir>...]
                             [EXCLUDE_SRCS <file>...]
                             [INCLUDE_DIRS <dir>...]
                             [PRIV_INCLUDE_DIRS <dir>...]
                             [LDFRAGMENTS <file>...]
                             [REQUIRES <component>...]
                             [PRIV_REQUIRES <component>...]
                             [REQUIRED_IDF_TARGETS <target>...]
                             [EMBED_FILES <file>...]
                             [EMBED_TXTFILES <file>...]
                             [WHOLE_ARCHIVE])

  SRCS[in,opt]

  > List of source files for the component.

  SRC_DIRS[in,opt]

  > List of source directories to search for source files (.c, .cpp, .S); ignored when SRCS is specified.

  EXCLUDE_SRCS[in,opt]

  > List of source files to exclude from the designated source directories.

  INCLUDE_DIRS[in,opt]

  > List of public include directories for the created component library.

  PRIV_INCLUDE_DIRS[in,opt]

  > List of private include directories for the newly created component library.

  LDFRAGMENTS[in,opt]

  > List of linker script fragments for the component.

  REQUIRES[in,opt]

  > List of publicly required components based on usage requirements.

  PRIV_REQUIRES[in,opt]

  > List of privately required components based on usage requirements.

  REQUIRED_IDF_TARGETS[in,opt]

  > List of IDF build targets supported exclusively by the component.

  EMBED_FILES[in,opt]

  > List of binary files to embed with the component.

  EMBED_TXTFILES[in,opt]

  > List of text files to embed with the component.

  WHOLE_ARCHIVE[in,opt]

  > Link the component as --whole-archive.

  Register a new component with the build system using the provided options. This function also automatically links all commonly required and managed components to the component's target.

idf_component_register_build_event_callback

      idf_component_register_build_event_callback(EVENT <event> CALLBACK <function-name>)

  EVENT[in]

  > Build lifecycle event at which the callback will be invoked. Currently only `POST_ELF` is supported.

  CALLBACK[in]

  > Name of a CMake function defined in the component's project_include.cmake file. The build system calls this function with the primary CMake target as its argument at the specified event point. For `POST_ELF` this is the executable target.

  Example:

      # project_include.cmake
      function(my_component_post_elf_hook target)
          add_custom_command(TARGET ${target} POST_BUILD
              COMMAND my_tool "$<TARGET_FILE:${target}>")
      endfunction()

      idf_component_register_build_event_callback(
          EVENT    POST_ELF
          CALLBACK my_component_post_elf_hook)

idf_component_set_property

      idf_component_set_property(<component> <property> <value> [APPEND])

  component[in]

  > Component name, target, target alias or interface.

  property[in]

  > Property name.

  value[in]

  > Property value.

  APPEND

  > Append the value to the property's current value instead of replacing it.

  Set the value of the specified component property. The property is also added to the internal list of component properties if it isn't already there.

### Macros

idf_project_default

      idf_project_default()

  Create a default project executable based on the main component and its transitive dependencies. The executable name is derived from the `PROJECT_NAME` variable, which by default uses the `CMAKE_PROJECT_NAME` value specified in the CMake's `project()` call.

  Generate the binary image for the executable, signed or unsigned based on the configuration, and add flash targets for it.

Was this page helpful?

- Thank you! We received your feedback.
- If you have any comments, fill in Espressif Documentation Feedback Form.

- We value your feedback.
- Let us know how we can improve this page by filling in Espressif Documentation Feedback Form.