深入理解ESP-IDF构建系统

本系列文章主要介绍ESP-IDF构建系统的实现原理,以及组件等相关概念。

概述

一个 ESP-IDF 项目可以看作是多个不同组件的集合,例如一个显示当前湿度的网页服务器会包含以下组件:

  • ESP-IDF 基础库,包括 libc、ROM bindings 等
  • Wi-Fi 驱动
  • TCP/IP 协议栈
  • FreeRTOS 操作系统
  • 网页服务器
  • 湿度传感器的驱动
  • 负责将上述组件整合到一起的主程序

ESP-IDF 可以显式地指定和配置每个组件。在构建项目的时候,构建系统会前往 ESP-IDF 目录、项目目录和用户自定义组件目录(可选)中查找所有组件,允许用户通过文本菜单系统配置 ESP-IDF 项目中用到的每个组件。在所有组件配置结束后,构建系统开始编译整个项目。

概念

项目

特指一个目录,其中包含了构建可执行应用程序所需的全部文件和配置,以及其他支持型文件,例如分区表、数据/文件系统分区和引导程序。

项目配置

保存在项目根目录下名为 sdkconfig 的文件中,可以通过 idf.py menuconfig 进行修改,且一个项目只能包含一个项目配置。

应用程序

是由 ESP-IDF 构建得到的可执行文件。一个项目通常会构建两个应用程序:项目应用程序(可执行的主文件,即用户自定义的固件)和引导程序(启动并初始化项目应用程序)。

组件

是模块化且独立的代码,会被编译成静态库(.a 文件)并链接到应用程序。部分组件由 ESP-IDF 官方提供,其他组件则来源于其它开源项目。

目标

特指运行构建后应用程序的硬件设备。运行 idf.py –list-targets 可以查看当前 ESP-IDF 版本中支持目标的完整列表。

请注意,以下内容并不属于项目的组成部分:

ESP-IDF 并不是项目的一部分,它独立于项目,通过 IDF_PATH 环境变量(保存 esp-idf 目录的路径)链接到项目,从而将 IDF 框架与项目分离。

交叉编译工具链并不是项目的组成部分,它应该被安装在系统 PATH 环境变量中。

使用构建系统

idf.py

idf.py 命令行工具提供了一个前端,可以帮助你轻松管理项目的构建过程,它管理了以下工具:

  • CMake,配置待构建的项目
  • Ninja,用于构建项目
  • esptool.py,烧录目标硬件设备

可通过 idf.py 配置构建系统,具体可参考 ESP-IDF 前端工具 idf.py

直接使用CMake

为了方便,idf.py 已经封装了 CMake 命令,但是你愿意,也可以直接调用 CMake

idf.py 在执行某些操作时,它会打印出其运行的每条命令以便参考。例如运行 idf.py build 命令与在 bash shell(或者 Windows Command Prompt)中运行以下命令是相同的:

mkdir -p build
cd build
cmake .. -G Ninja   # 或者 'Unix Makefiles'
ninja

在上面的命令列表中,cmake 命令对项目进行配置,并生成用于最终构建工具的构建文件。在这个例子中,最终构建工具是 Ninja: 运行 ninja 来构建项目。

没有必要多次运行 cmake。第一次构建后,往后每次只需运行 ninja 即可。如果项目需要重新配置,ninja 会自动重新调用 cmake

若在 CMake 中使用 ninjamake,则多数 idf.py 子命令也会有其对应的目标,例如在构建目录下运行 make menuconfigninja menuconfig 与运行 idf.py menuconfig 是相同的。

使用Ninja/Make来烧录

可以直接使用 ninja 或 make 运行如下命令来构建项目并烧录:

ninja flash

或:

make app-flash

可用的目标还包括:flashapp-flash (仅用于 app)、bootloader-flash (仅用于 bootloader)。

以这种方式烧录时,可以通过设置 ESPPORTESPBAUD 环境变量来指定串口设备和波特率。可以在操作系统或 IDE 项目中设置该环境变量,或者直接在命令行中进行设置:

ESPPORT=/dev/ttyUSB0 ninja flash

在命令的开头为环境变量赋值属于 Bash shell 的语法,可在 Linux 、macOS 和 Windows 的类 Bash shell 中运行,但在 Windows Command Prompt 中无法运行。

或:

make -j3 app-flash ESPPORT=COM4 ESPBAUD=2000000

在命令末尾为变量赋值属于 make 的语法,适用于所有平台的 make。

示例项目

一个ESP-IDF项目的目录树结构通常如下所示:

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

该示例项目 “myProject” 包含以下组成部分:

  • 顶层项目 CMakeLists.txt 文件,这是 CMake 用于学习如何构建项目的主要文件,可以在这个文件中设置项目全局的 CMake 变量。顶层项目 CMakeLists.txt 文件会导入 /tools/cmake/project.cmake 文件,由它负责实现构建系统的其余部分。该文件最后会设置项目的名称,并定义该项目。

  • “sdkconfig” 项目配置文件,执行 idf.py menuconfig 时会创建或更新此文件,文件中保存了项目中所有组件(包括 ESP-IDF 本身)的配置信息。 sdkconfig 文件可能会也可能不会被添加到项目的源码管理系统中。

  • 可选的 components 目录中包含了项目的部分自定义组件,并不是每个项目都需要这种自定义组件,但它有助于构建可复用的代码或者导入第三方(不属于 ESP-IDF)的组件。或者,你也可以在顶层 CMakeLists.txt 中设置 EXTRA_COMPONENT_DIRS 变量以查找其他指定位置处的组件。

  • main 目录是一个特殊的组件,它包含项目本身的源代码。main 是默认名称,CMake 变量 COMPONENT_DIRS 默认包含此组件,但你可以修改此变量。有关详细信息,请参阅 重命名 main 组件。如果项目中源文件较多,建议将其归于组件中,而不是全部放在 main 中。

  • build 目录是存放构建输出的地方,如果没有此目录,idf.py 会自动创建。CMake 会配置项目,并在此目录下生成临时的构建文件。随后,在主构建进程的运行期间,该目录还会保存临时目标文件、库文件以及最终输出的二进制文件。此目录通常不会添加到项目的源码管理系统中,也不会随项目源码一同发布。

每个组件目录都包含一个 CMakeLists.txt 文件,里面会定义一些变量以控制该组件的构建过程,以及其与整个项目的集成。

每个组件还可以包含一个 Kconfig 文件,它用于定义 menuconfig 时展示的 组件配置 选项。某些组件可能还会包含 Kconfig.projbuildproject_include.cmake 特殊文件,它们用于 覆盖项目的部分设置

以下是转换为Markdown格式后的内容,放置在Markdown代码块中:

项目 CMakeLists 文件

每个项目都有一个顶层 CMakeLists.txt 文件,包含整个项目的构建设置。默认情况下,项目 CMakeLists 文件会非常小。

最小 CMakeLists 文件示例

最小项目:

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

必要部分

每个项目都要按照上面显示的顺序添加上述三行代码:

  1. cmake_minimum_required(VERSION 3.16) 必须放在 CMakeLists.txt 文件的第一行,它会告诉 CMake 构建该项目所需要的最小版本号。ESP-IDF 支持 CMake 3.16 或更高的版本。

  2. include($ENV{IDF_PATH}/tools/cmake/project.cmake) 会导入 CMake 的其余功能来完成配置项目、检索组件等任务。

  3. project(myProject) 会创建项目本身,并指定项目名称。该名称会作为最终输出的二进制文件的名字,即 myProject.elfmyProject.bin。每个 CMakeLists 文件只能定义一个项目。

可选的项目变量

以下这些变量都有默认值,用户可以覆盖这些变量值以自定义构建行为。

  • COMPONENT_DIRS:组件的搜索目录,默认为 IDF_PATH/componentsPROJECT_DIR/componentsEXTRA_COMPONENT_DIRS。如果你不想在这些位置搜索组件,请覆盖此变量。

  • EXTRA_COMPONENT_DIRS:用于搜索组件的其它可选目录列表。路径可以是相对于项目目录的相对路径,也可以是绝对路径。

  • COMPONENTS:要构建进项目中的组件名称列表,默认为 COMPONENT_DIRS 目录下检索到的所有组件。使用此变量可以“精简”项目以缩短构建时间。请注意,如果一个组件通过 COMPONENT_REQUIRES 指定了它依赖的另一个组件,则会自动将其添加到 COMPONENTS 中,所以 COMPONENTS 列表可能会非常短。

以上变量中的路径可以是绝对路径,或者是相对于项目目录的相对路径。

请使用 cmake 中的 set 命令 来设置这些变量,如 set(VARIABLE "VALUE")。请注意,set() 命令需放在 include(...) 之前,cmake_minimum(...) 之后。

重命名 main 组件

构建系统会对 main 组件进行特殊处理。假如 main 组件位于预期的位置(即 ${PROJECT_PATH}/main),那么它会被自动添加到构建系统中。其他组件也会作为其依赖项被添加到构建系统中,这使用户免于处理依赖关系,并提供即时可用的构建功能。重命名 main 组件会减轻上述这些幕后工作量,但要求用户指定重命名后的组件位置,并手动为其添加依赖项。重命名 main 组件的步骤如下:

  1. 重命名 main 目录。

  2. 在项目 CMakeLists.txt 文件中设置 EXTRA_COMPONENT_DIRS,并添加重命名后的 main 目录。

  3. 在组件的 CMakeLists.txt 文件中设置 COMPONENT_REQUIRESCOMPONENT_PRIV_REQUIRES 以指定依赖项。

覆盖默认的构建规范

构建系统设置了一些全局的构建规范(编译标志、定义等),这些规范可用于编译来自所有组件的所有源文件。

例如,其中一个默认的构建规范是编译选项 -Wextra。假设一个用户想用 -Wno-extra 来覆盖这个选项,应在 project() 之后进行:

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

idf_build_set_property(COMPILE_OPTIONS "-Wno-error" APPEND)

这确保了用户设置的编译选项不会被默认的构建规范所覆盖,因为默认的构建规范是在 project() 内设置的。

公众号 | FunIO
微信搜一搜 “funio”,发现更多精彩内容。
个人博客 | blog.boringhex.top

esp32-freertos-sdk 工具包 See the Getting Started guide links above for a detailed setup guide. This is a quick reference for common commands when working with ESP-IDF projects: Setup Build Environment (See Getting Started guide for a full list of required steps with details.) Install host build dependencies mentioned in Getting Started guide. Add tools/ directory to the PATH Run python -m pip install -r requirements.txt to install Python dependencies Configuring the Project idf.py menuconfig Opens a text-based configuration menu for the project. Use up & down arrow keys to navigate the menu. Use Enter key to go into a submenu, Escape key to go out or to exit. Type ? to see a help screen. Enter key exits the help screen. Use Space key, or Y and N keys to enable (Yes) and disable (No) configuration items with checkboxes "[*]" Pressing ? while highlighting a configuration item displays help about that item. Type / to search the configuration items. Once done configuring, press Escape multiple times to exit and say "Yes" to save the new configuration when prompted. Compiling the Project idf.py build ... will compile app, bootloader and generate a partition table based on the config. Flashing the Project When the build finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this automatically by running: idf.py -p PORT flash Replace PORT with the name of your serial port (like COM3 on Windows, /dev/ttyUSB0 on Linux, or /dev/cu.usbserial-X on MacOS. If the -p option is left out, idf.py flash will try to flash the first available serial port. This will flash the entire project (app, bootloader and partition table) to a new chip. The settings for serial port flashing can be configured with idf.py menuconfig. You don't need to run idf.py build before running idf.py flash, idf.py flash will automatically rebuild anything which needs it. Viewing Serial Output The idf.py monitor target uses the idf_monitor tool to display se
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值