CMake writing tutorial

sample project

A directory tree structure for an example project might look like this:

- myProject/
             - CMakeLists.txt
             - sdkconfig
             - components/ - component1/ - CMakeLists.txt
                                         - Kconfig
                                         - src1.c
                           - component2/ - CMakeLists.txt
                                         - Kconfig
                                         - src1.c
                                         - include/ - component2.h
             - main/       - src1.c
                           - src2.c
             - build/

The sample project  myproject contains the following components:

  • The top-level project CMakeLists.txt file, which is the main file used by CMake to learn how to build the project, can set the global CMake variables of the project in this file. The top-level project CMakeLists.txt file imports  the /tools/cmake/project.cmake  file, which implements the rest of the build system. The file ends with setting the name of the project and defines the project.

  • sdkconfig Project configuration file,  idf.py menuconfig this file will be created or updated during execution, and the configuration information of all components in the project (including ESP-IDF itself) is saved in the file. sdkconfig Files may or may not be added to the project's source control system.

  • The optional  component directory contains some custom components of the project. Not every project needs such custom components, but it helps to build reusable code or import third parties (not part of ESP-IDF) s component.

  • main A directory is a special one  伪组件that contains the source code for the project itself. main is the default name, CMake variables  COMPONENT_DIRS include this component by default, but you can modify this variable. Alternatively, you can also set variables in the top-level CMakeLists.txt  EXTRA_COMPONENT_DIRS to look for components at other specified locations. See  Renaming the main component for details . If there are many source files in the project, it is recommended to attribute them to the component, not all of them  main .

  • build The directory is where the build output is stored. If there is no such directory, idf.py it will be created automatically. CMake will configure the project and generate temporary build files in this directory. Later, during the run of the main build process, this directory also holds temporary object files, library files, and final output binaries. This directory is usually not added to the project's source control system, nor is it distributed with the project's source code.

Each component directory contains a  CMakeLists.txt file that defines variables to control the build process of that component and its integration with the overall project. See  the component CMakeLists file for more details .

Each component can also contain a  Kconfig file that defines  the configurationmenuconfig options  for the component to display   . Some components may also include   and   special files that  override some of the project's settings .Kconfig.projbuildproject_include.cmake

Project CMakeLists file

Every project has a top-level  CMakeLists.txt file that contains build settings for the entire project. By default, project CMakeLists files will be very small.

Example minimal CMakeLists file

Minimum item:

cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(myProject)

necessary part

Add the above three lines of code to each project in the order shown above:

  • cmake_minimum_required(VERSION 3.5) Must be placed on the first line of the CMakeLists.txt file, it will tell CMake the minimum version number required to build the project. ESP-IDF supports CMake 3.5 or higher.

  • include($ENV{IDF_PATH}/tools/cmake/project.cmake) The rest of CMake's functionality is imported to configure projects, retrieve components, and more.

  • project(myProject) The project itself is created, and a project name is specified. This name will be used as the name of the final output binary file, ie  myProject.elf and  myProject.bin. Only one project can be defined per CMakeLists file.

optional project variable

The following variables have default values ​​that users can override to customize build behavior. For more implementation details, please refer to  the /tools/cmake/project.cmake  file.

  • COMPONENT_DIRS: The search directory for components, the default is  ${IDF_PATH}/components, ${PROJECT_PATH}/components and  EXTRA_COMPONENT_DIRS. Override this variable if you do not want to search for components in these locations.

  • EXTRA_COMPONENT_DIRS: An optional list of additional directories to search for components. Paths can be relative to the project directory or absolute.

  • COMPONENTS: A list of component names to be built into the project, defaulting to  COMPONENT_DIRS all components retrieved under the directory. Use this variable to "thin" your project to improve build times. Note that if a component  COMPONENT_REQUIRES specifies another component it depends on, it will be automatically added to it  COMPONENTS , so  COMPONENTS the list can be very short.

  • COMPONENT_REQUIRES_COMMON: A list of common components required by each component that are automatically added to each component's  COMPONENT_PRIV_REQUIRES list as well as to the project's  COMPONENTS list. By default, this variable is set to the minimum set of core "system" components required by an ESP-IDF project. Usually you don't need to change this variable in your project.

The path in the above variables can be an absolute path, or a relative path relative to the project directory.

Please use  the set command in cmake  to set these variables, ie  set(VARIABLE "VALUE"). Please note that set() the command needs to be placed  include(...) before and cmake_minimum(...) after.

rename  main component

main Components are handled specially by the build system  . Provided  main the component is in the expected location (i.e. ${PROJECT_PATH}/main), then it is automatically added to the build system. Other components are also added to the build system as their dependencies, which frees users from having to deal with dependencies and provides out-of-the-box build functionality. Renaming  main components alleviates this behind-the-scenes work, but requires the user to specify the location of the renamed component and manually add dependencies to it. The steps to rename  main a component are as follows:

  1. Rename  main the directory.

  2. Set in the project CMakeLists.txt file  EXTRA_COMPONENT_DIRSand add the renamed  main directory.

  3. COMPONENT_REQUIRES Set or  COMPONENT_PRIV_REQUIRES specify dependencies in the component's CMakeLists.txt file  .

Component CMakeLists file

Each project contains one or more components, which are either part of ESP-IDF, part of the project's own component directory, or added from a custom component directory (see above  ) .

A component is   any directory that contains files COMPONENT_DIRS in the list  .CMakeLists.txt

search component

COMPONENT_DIRS A list of directories in which to search for components of a project, either the component itself (i.e. the directory containing the CMakeLists.txt file), or the top-level directory of the component as a subdirectory

When CMake runs the project configuration, it records the list of components included in this build, which can be used to debug the addition/exclusion of certain components.

Component with the same name

When ESP-IDF searches for all components to build, it  COMPONENT_DIRS does so in the specified order, which means that by default, ESP-IDF internal components are searched first, then project components, and finally components  EXTRA_COMPONENT_DIRS in . If two or more of these directories contain components with the same name, the component in the last location searched is used. This allows components to be copied into the project directory and then modified to overwrite the ESP-IDF components, if used this way, the ESP-IDF directory itself can remain unchanged.

Minimal component CMakeLists file

The content of the minimal component  CMakeLists.txt file is as follows:

set(COMPONENT_SRCS "foo.c")
set(COMPONENT_ADD_INCLUDEDIRS "include")
register_component()
  • COMPONENT_SRCS is a space-separated list of source files ( *.c, *.cpp, *.cc, *.S), all of which will be compiled into the component library.

  • COMPONENT_ADD_INCLUDEDIRS is a space-separated list of directories that will be added to the global include search path of all components that require this component (including the main component).

  • register_component() The component is added to the build system with the variables set above, and the build produces a library with the same name as the component, which is finally linked into the application.  If this step is skipped because of using  an if command in CMake or similar, the component will not be added to the build system.

The above directory is usually set as  CMakeLists.txt a relative path relative to the file, of course, it can also be set as an absolute path.

For a more complete  CMakeLists.txt example, see  the component CMakeLists example .

Default component variables

The following component-specific variables can be used in component CMakeLists, but modification is not recommended:

  • COMPONENT_PATH: The component directory, that is,  CMakeLists.txt the absolute path of the included file. It is  CMAKE_CURRENT_SOURCE_DIR the same as a variable, and the path cannot contain spaces.

  • COMPONENT_NAME: Component name, which is the same as the component directory name.

  • COMPONENT_TARGET: The name of the library target, which is created internally by the build system for the component.

The following variables are set at the project level, but are available in component CMakeLists:

  • PROJECT_NAME: Project name, set in the project CMakeLists.txt file.

  • PROJECT_PATH: Absolute path to the project directory (containing the project CMakeLists file),  CMAKE_SOURCE_DIR same as the variable.

  • COMPONENTS: The names of all components included in this build, in the form of a semicolon-separated CMake list.

  • CONFIG_*: Each value in the project configuration corresponds to a  CONFIG_ variable starting with cmake in cmake. See Kconfig for more details  .

  • IDF_VER: The git version number of ESP-IDF,  git describe generated by the command.

  • IDF_VERSION_MAJORIDF_VERSION_MINORIDF_VERSION_PATCH: A component version of ESP-IDF that can be used in conditional expressions. Please note that the accuracy of these information is not as good as   the  IDF_VER variable, version number  v4.0-dev-*v4.0-beta1and  the  corresponding   variable value is the same, but   the value of is different.v4.0-rc1v4.0IDF_VERSION_*IDF_VER

  • IDF_TARGET: The hardware target name of the project.

  • PROJECT_VER: Project version number.

    • If the variable is set in the project CMakeLists.txt file  PROJECT_VER , the variable value can be used.

    • Or, if  ${PROJECT_PATH}/version.txt the file exists, its contents are used  PROJECT_VER as the value.

    • Or, if the project is in some Git repository, use  git describe the output of the command as  PROJECT_VER the value of .

    • Otherwise, PROJECT_VER the value is null.

CMakeLists.txt If you modify the above variables in a component  , it will not affect the construction of other components, but it may make the component difficult to build or debug.

  • COMPONENT_ADD_INCLUDEDIRS: The relative path relative to the component directory, which is added to the global include search path of all other components that need this component. If an include path is only required when compiling the current component, please add it to  COMPONENT_PRIV_INCLUDEDIRS .

  • COMPONENT_REQUIRES is a space-separated list of components, listing other components that the current component depends on. If the current component has a header file located  COMPONENT_ADD_INCLUDEDIRS in the directory, and the header file includes another component's header file, then the dependent component needs to be  COMPONENT_REQUIRES pointed out in . This dependency can be recursive.

    COMPONENT_REQUIRES Can be empty, because all components need some common components (such as libc library provided by newlib component, RTOS function provided by freertos component), these common components have been set in project-level variables  COMPONENT_REQUIRES_COMMON .

    If a component only requires the header files of additional components to compile its source files (rather than importing their header files globally), then these dependent components need to be indicated in  COMPONENT_PRIV_REQUIRES .

    See  Component Dependencies for details.

Optional component specific variables

The following variables can  CMakeLists.txt be set in to control the build behavior of this component:

  • COMPONENT_PRIV_INCLUDEDIRS: The relative path relative to the component directory will only be added to the include search path of this component.

  • COMPONENT_PRIV_REQUIRES: A space-separated list of components to compile or link the source files for the current component. The header file path for these components is not passed to the rest of the components that need it, it is only used to compile the source code of the current component. See Component Dependencies for more details  .

  • COMPONENT_SRCS: The path of the source files to be compiled into the current component. It is recommended to use this method to add source files to the build system.

  • COMPONENT_SRCDIRS: Source file directory path relative to the component directory, used to search for source files ( *.cpp, *.c, *.S). The source file that matches successfully will replace  COMPONENT_SRCS the source file specified in , and then be compiled into the component. That is, the setting  COMPONENT_SRCDIRS will cause  COMPONENT_SRCS it to be ignored. This approach makes it easy to import source files into components in their entirety, but is not recommended (see  File Wildcards & Incremental Builds for details ).

  • COMPONENT_SRCEXCLUDE: The source file path that needs to be excluded from the component. When there are a large number of source files in a directory that need to be imported into the component, but at the same time there are individual files that do not need to be imported, they can be set  COMPONENT_SRCDIRS together with variables. Paths can be relative to the component directory or absolute.

  • COMPONENT_ADD_LDFRAGMENTS: The path to the linker fragment file used by the component to automatically generate the linker script file. See Linker Script Generation Mechanisms for details  .

annotation

If  COMPONENT_SRCDIRS or  is not set COMPONENT_SRCS, the component will not be compiled into a library file, but can still be added to the include path for use when compiling other components.

Component compilation control

When compiling source files for a specific component,  target_compile_options compiler options can be passed using the command:

target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-variable)

This command encapsulates CMake's  target_compile_options  command.

To specify compiler flags for individual source files, use CMake's  set_source_files_properties  command:

set_source_files_properties(mysrc.c
    PROPERTIES COMPILE_FLAGS
    -Wno-unused-variable
)

This may be useful if the upstream code emits warnings when compiling.

Note that the above two commands can only  register_component() be called after the commands in the component CMakeLists file.

component configuration

Each component can contain a  Kconfig file and  CMakeLists.txt be placed in the same directory. Kconfig The file contains information about some configuration settings to be added to the component's configuration menu.

Component Settings These settings can be found under the menu bar when you run menuconfig  .

The easiest way to create a component's Kconfig file is to use the existing Kconfig file in ESP-IDF as a template and modify it on this basis.

See  Adding Conditional Configuration for an example .

preprocessor definition

The ESP-IDF build system adds the following C preprocessor definitions to the command line:

  • ESP_PLATFORM: Can be used to detect build behavior within ESP-IDF.

  • IDF_VER: Define the git version string, for example: v2.0 it is used to mark the released version, v1.0-275-g0efaa4f and it is used to mark any commit record.

  • PROJECT_VER: Project version number, for details, please refer to  the preset component variables .

  • PROJECT_NAME: Project name, defined in the project CMakeLists.txt file.

Component dependencies

As each component is compiled, the ESP-IDF system recursively evaluates its components.

The source files for each component are compiled with the header files in the following paths:

  • COMPONENT_ADD_INCLUDEDIRS The sum  of the current components  COMPONENT_PRIV_INCLUDEDIRS.

  • The current component's  COMPONENT_REQUIRES and  COMPONENT_PRIV_REQUIRES other components specified by the variable (that is, all public and private dependencies of the current component)  COMPONENT_ADD_INCLUDEDIRS.

  • COMPONENT_REQUIRES The recursive operation of all components  , that is, all the public dependencies of the component after recursive operation.

Write components

  • COMPONENT_REQUIRES Need to include all components where the header files are #included by the current component's public header file.

  • COMPONENT_PRIV_REQUIRES Need to include the component whose header file is #included by the current component's source file (unless it is already set in  COMPONENT_PRIV_REQUIRES ). Or a component that must be linked to the current component to work properly.

  • COMPONENT_REQUIRES, COMPONENT_PRIV_REQUIRES need to be set before calling  register_component() .

  • COMPONENT_REQUIRES The value of and  COMPONENT_PRIV_REQUIRES cannot depend on any configuration options ( CONFIG_xxx), because the dependencies are expanded before the configuration is loaded. Other component variables (such as  COMPONENT_SRCS and  COMPONENT_ADD_INCLUDEDIRS) can depend on configuration choices.

  • If the current component  COMPONENT_REQUIRES_COMMON does not depend on other components except for the common components set in (such as RTOS, libc, etc.), then the above two  REQUIRES variables can be empty.

If a component only supports certain hardware targets (ie depends on specific ones  IDF_TARGET),  require_idf_targets(NAMES...) a CMake function can be called to declare this requirement. In this case, an error will be raised if the build system imports a component that does not support the current hardware target.

create project

  • Each component is included in the build system by default.

  • If  COMPONENTS the variable is set to the minimal list of components used directly by the project, the build system imports:

    • COMPONENTS Components explicitly mentioned in .

    • Dependencies of these components (and components recursively).

    • Common components that every component depends on.

  • Will  COMPONENTS be set to a minimal list of required components, which can significantly reduce your project's build time.

Implementation details of dependency handling in the build system

  • Scripts are run early in the CMake configuration process  expand_requirements.cmake . The script will perform local operations on the CMakeLists.txt files of all components to obtain a component dependency graph (this graph may have a closed loop). This image is used to generate files in the build directory  component_depends.cmake .

  • BUILD_COMPONENTS The main CMake process imports this file and uses it to determine the list of components ( variables used internally) to include in the build system  . BUILD_COMPONENTS Variables are sorted, dependent components come first. Since there may be closed loops in the component dependency graph, there is no guarantee that every component will satisfy this ordering. Given the same set of components and dependencies, then the final ranking should be deterministic.

  • CMake will  BUILD_COMPONENTS print out the value of 'Component names:'.

  • Configuration of each component included in the build system is then performed.

  • Each component is included in the build system normally, and then the CMakeLists.txt file is executed again to add the component library to the build system.

Component dependency order

BUILD_COMPONENTS The order of the components in the variable determines the rest of the order in the build process, including:

  • The order in which the project imports  the project_include.cmake  file.

  • -I The order in which the list of header file paths is generated for compilation (via  arguments). Note that for a given component's source files, the compiler only needs to be told the path to the header files of that component's dependent components.

build internal process

For more information about  CMake  and CMake commands, please refer to  the official CMake v3.5 documentation  .

Contents of project.cmake

 Some useful modules and global variables are defined when the project CMakeLists file imports  project.cmake the file . It also automatically sets  the variable project.cmakeif it is not set in the system environment   .IDF_PATHIDF_PATH

project.cmake The file also overrides  CMake  built-in  project functions to add all ESP-IDF project-specific functionality.

project function

The custom  project() function performs the following steps:

  • Determine the hardware target (  IDF_TARGET set by environment variables), and save it in the CMake cache. If the hardware target set in the environment variable does not match the one in the CMake cache, it will report an error and exit.

  • Computes component dependencies and constructs  BUILD_COMPONENTS the variable, which is a list of all components that need to be imported into the build system ( see above for details ).

  • Find all components in the project (search  COMPONENT_DIRS, and  COMPONENTS filter by (if this variable is set).

  • sdkconfig Load project configuration information  from  a file, build sdkconfig.cmake and  sdkconfig.h file, used to define configuration items in CMake and C/C++ respectively. If the project configuration changes, CMake will automatically re-run, regenerate the above two files, and then reconfigure the project.

  • Depending on the value of the hardware target ( IDF_TARGET), set  the CMAKE_TOOLCHAIN_FILE  variable to the corresponding toolchain file.

  • Call  CMake's project function  to declare the actual CMake-level project.

  • Load the git version number. Some tricks are used to re-run CMake if a new version is checked out in git. For details, refer to  File Wildcards & Incremental Builds .

  • Import this file from the component that includes  the project_include.cmake  file.

  • Each component is added to the build system. Each component's CMakeLists file will call  register_component functions, which will call CMake's  add_library  function to add a library, then add source files, compile options, etc.

  • Add the final application executable to the build system.

  • Go back and specify dependencies between components (adding each component's public header directory to other components).

See /tools/cmake/project.cmake  file for more details  .

CMake debugging

Some tips for debugging the ESP-IDF CMake build system:

  • When CMake runs, it prints a lot of diagnostic information, including component list and component path.

  • Run  cmake -DDEBUG=1, the IDF build system produces more verbose diagnostic output.

  • Runtime  cmake specifications  --trace or  --trace-expand options provide a great deal of information about the flow of control. Please refer to  the CMake command line documentation for details .

warn about undefined variables

By default, CMake  is called with  idf.py flags  passed to it, and CMake  will print a warning  if undefined variables are referenced during the build process . This is very useful for finding buggy CMake files.--warn-uninitialized

If you don't want to enable this feature, you can  idf.py pass  --no-warnings a flag to .

Override some of the project's settings

project_include.cmake

project_include.cmake If some build behavior of the component needs to be executed before the component CMakeLists file, you can create a file named in the component directory  , and project.cmake this CMake file will be imported during operation.

project_include.cmake The file is used internally by ESP-IDF to define project-wide build functionality, such as  esptool.py command-line arguments  bootloader for this particular application.

Different from component  CMakeLists.txt files, when importing the ``project_include.cmake`` file, the current source file directory (ie  CMAKE_CURRENT_SOURCE_DIR) and the working directory are the project directory. If you want to get the absolute path of the current component, you can use  COMPONENT_PATH variables.

Note that project_include.cmake it is not required for most common components. For example, adding include search directories to the project, adding options to the final link step,  LDFLAGS etc. can  CMakeLists.txt be customized through files. See  Optional project variables for details .

project_include.cmake Files are  BUILD_COMPONENTS imported sequentially in the order of the components in the variable (as recorded by CMake). That is, only  project_include.cmake after the files of all dependent components of the current component are imported,  project_include.cmake the files of the current component will be imported, unless the two components are in the same dependency closed loop. This is especially the case if a  project_include.cmake file depends on variables set by another component. See  Implementation Details of Dependency Handling in Build Systems for more details .

Be extra careful when setting variables or targets in  project_include.cmake files, these values ​​are included in the project's top-level CMake file, so they can affect or break the functionality of all components.

KConfig.projbuild

Similar  project_include.cmake to , you can also define a KConfig file for a component to achieve global  component configuration . If you want to add configuration options at the top level of menuconfig, rather than in the "Component Configuration" submenu, you can  CMakeLists.txt define these options in the KConfig.projbuild file in the same directory as the file.

Be careful when adding configurations in this file as they will be included in the overall project configuration. Where possible, create KConfig files for  component configuration  .

Only configuration components

A configuration-only component is a special type of component that does not contain a source file, but only a ,  Kconfig.projbuild, KConfig and  CMakeLists.txt file, which  CMakeLists.txt has only one line of code that calls  register_config_only_component() a function. This function will import the component into the project build, but will not build any libraries, and will not add header files to any include search paths.

If the CMakeLists.txt file does not call  register_component() or  register_config_only_component(), then the file will be excluded from the project build. Depending on the configuration of your project, this may sometimes be necessary.

Component CMakeLists example

Because the build environment tries to set sensible defaults that work most of the time, component  CMakeLists.txt files can be very small or even empty, see  the minimal component CMakeLists file . But some functions often need to override  the preset component variables  to achieve.

The following is a more advanced example of a component CMakeLists file.

Add conditional configuration

The configuration system can be used to conditionally compile certain files based on options selected in the project configuration.

Kconfig:

config FOO_ENABLE_BAR
    bool "Enable the BAR feature."
    help
        This enables the BAR feature of the FOO component.

CMakeLists.txt:

set(COMPONENT_SRCS "foo.c" "more_foo.c")

if(CONFIG_FOO_ENABLE_BAR)
    list(APPEND COMPONENT_SRCS "bar.c")
endif()

The above example uses CMake's  if  function and  list APPEND  function.

Can also be used to select or remove an implementation, as follows:

Kconfig:

config ENABLE_LCD_OUTPUT
    bool "Enable LCD output."
    help
        Select this if your board has a LCD.

config ENABLE_LCD_CONSOLE
    bool "Output console text to LCD"
    depends on ENABLE_LCD_OUTPUT
    help
        Select this to output debugging output to the lcd

config ENABLE_LCD_PLOT
    bool "Output temperature plots to LCD"
    depends on ENABLE_LCD_OUTPUT
    help
        Select this to output temperature plots

CMakeLists.txt:

if(CONFIG_ENABLE_LCD_OUTPUT) 
    set(COMPONENT_SRCS lcd-real.c lcd-spi.c) 
else() 
    set(COMPONENT_SRCS lcd-dummy.c) 
endif() 

# If the console or drawing function is enabled, you need to add font 
if(CONFIG_ENABLE_LCD_CONSOLE OR CONFIG_ENABLE_LCD_PLOT) 
    list(APPEND COMPONENT_SRCS "font.c") 
endif()

Conditional judgment of hardware targets

CMake files can use  IDF_TARGET variables to get the current hardware target.

Additionally, if the current hardware target is  xyz``(即 ``IDF_TARGET=xyz), then the Kconfig variable  CONFIG_IDF_TARGET_XYZ will also be set.

Note that components can depend on  IDF_TARGET variables, but not this Kconfig variable. It is also not  include possible to use Kconfig variables in statements in CMake files, which are allowed in this context  IDF_TARGET.

generate source code

Some component source files may not be provided by the component itself, but must be generated from another file. Assuming a component requires a header file consisting of binary data converted from a BMP file (using the bmp2h tool), the header file is then included in a file named graphics_lib.c:

add_custom_command(OUTPUT logo.h
    COMMAND bmp2h -i ${COMPONENT_DIR}/logo.bmp -o log.h
    DEPENDS ${COMPONENT_DIR}/logo.bmp
    VERBATIM)

add_custom_target(logo DEPENDS logo.h)
add_dependencies(${COMPONENT_LIB} logo)

set_property(DIRECTORY "${COMPONENT_DIR}" APPEND PROPERTY
    ADDITIONAL_MAKE_CLEAN_FILES logo.h)

This example was adapted from  a CMake FAQ , which also includes some examples for the ESP-IDF build system as well.

This example generates a logo.h file in the current directory (the build directory), and a logo.bmp is provided with the component in the component directory. Since logo.h is a newly generated file, it should also be cleared whenever the project needs to be cleaned up. Therefore, add this file to  the ADDITIONAL_MAKE_CLEAN_FILES  property.

annotation

If you need to makefiles as part of the project CMakeLists.txt instead of as part of the component CMakeLists.txt, you need to use  ${PROJECT_PATH} overrides  ${COMPONENT_DIR}, use  ${PROJECT_NAME}.elf overrides  ${COMPONENT_LIB}.

If a source file is generated from another component and contains  logo.h files, it needs to be called  add_dependenciesto add a dependency between the two components to ensure that the component source files are compiled in the correct order.

Embed binary data

Sometimes your component expects to use a binary or text file, but you don't want to reformat them as C source files. In this case, you can add  COMPONENT_EMBED_FILES variables to the component CMakeLists to specify the file names to embed (space-separated ):

set(COMPONENT_EMBED_FILES server_root_cert.der)

Or, if the file is a string, you can set  COMPONENT_EMBED_TXTFILES the variable to convert the contents of the file into a null-terminated string for embedding:

set(COMPONENT_EMBED_TXTFILES server_root_cert.pem)

The content of the file will be added to the .rodata section of Flash, and the user can access it by symbolic name, as follows:

extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
extern const uint8_t server_root_cert_pem_end[]   asm("_binary_server_root_cert_pem_end");

Symbolic names are generated from the full file name, as  COMPONENT_EMBED_FILES shown in , characters  /, . etc. are replaced by underscores. The _binary prefix in symbol names is added by the objcopy command, for both text and binary files.

If you want to embed the file in the project, not in the component, you can call  target_add_binary_data the function:

target_add_binary_data(myproject.elf "main/data.bin" TEXT)

And put this line of code  project() after the command of the project CMakeLists.txt, and modify it  myproject.elf to your own project name. TEXTThe build system embeds the null-terminated string if the  last parameter is  set to , BINARYand embeds the file contents as-is if the last parameter is set to .

See  protocols/https_request for an example of using this technique , the contents of the certificate file are loaded from the .pem file at compile time.

code and data storage

ESP-IDF also supports automatic generation of linker scripts, which allow components to define where their code and data reside in memory by linking fragment files. The build system processes these linker fragment files and augments the resulting linker scripts to direct the linking process of the application binary. For more details and a quick start guide, see  Linker Script Generation Mechanism .

Complete coverage of the component's build process

Of course, in some cases, the methods mentioned above may not be enough. If the component encapsulates another third-party component, and this third-party component does not work directly in the ESP-IDF build system, in this case, you need to abandon the ESP-IDF build system and use CMake's ExternalProject  instead  Function. An example of component CMakeLists is as follows:

# External build process for quirc, run in the source directory and generate libquirc.a 
externalproject_add(quirc_build 
    PREFIX ${COMPONENT_DIR} 
    SOURCE_DIR ${COMPONENT_DIR}/quirc 
    CONFIGURE_COMMAND "" 
    BUILD_IN_SOURCE 1 
    BUILD_COMMAND make CC=${CMAKE_C_COMPILER} libquirc.a 
    INSTALL_COMMAND "" 
    ) 

# Add libquirc.a to the build system 
add_library(quirc STATIC IMPORTED GLOBAL) 
add_dependencies(quirc quirc_build) 

set_target_properties(quirc PROPERTIES IMPORTED_LOCATION 
    ${COMPONENT_DIR}/quirc/libquirc.a) 
set_target _properties(quirc PROPERTIES INTERFACE_INCLUDE_DIRECTORIES 
    ${COMPONENT_DIR }/quirc/lib)
 
set_directory_properties( PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
    "${COMPONENT_DIR}/quirc/libquirc.a")

(The above CMakeLists.txt can be used to create  a component named , which builds the quircquirc project  with its own Makefile   .)

  • externalproject_add Defines an external build system.

    • Set  SOURCE_DIR, CONFIGURE_COMMAND, BUILD_COMMAND and  INSTALL_COMMAND. CONFIGURE_COMMAND Can be set to an empty string if the external build system does not configure this step  . In ESP-IDF's build system,  INSTALL_COMMAND variables are generally set to empty.

    • Setup  BUILD_IN_SOURCE, i.e. the build directory is the same as the source directory. Otherwise, you can also set  BUILD_DIR variables.

    • externalproject_add() See  ExternalProject for details on  the command .

  • The second set of commands adds a target library that points to the library files generated by the external build system. In order to add the include directory and tell CMake where the file is, some more properties need to be set.

  • Finally, the generated library is added to  ADDITIONAL_MAKE_CLEAN_FILES  . make clean That is, the library will be deleted after execution  . Note that other object files in the build system are not removed.

url: https://docs.espressif.com/projects/esp-idf/zh_CN/release-v4.2/esp32/api-guides/build-system.html?highlight=cmake#component-directories

Guess you like

Origin blog.csdn.net/Horsdy123/article/details/110195971