文章目录
- 前言
- 简介
- 顶层CMakeLists.txt
- 组件CMakeLists.txt
- 组件依赖示例
- Car 组件
- Engine 组件
- Spark Plug 组件
- 源文件 Include 目录
- 顶层CMakeLists.txt
- 总结:
- 参考:
前言
因为熟悉了STM32的开发方式,同时随着项目文件越来越多,可以将自己写的代码分模块添加到工程中,下面分析如何将自己写的组件添加到工程中使其能够正常编译运行。
简介
在ESP-IDF中,构建,编译,以及下载都是通过idf.py脚本来实现的,该脚本使用
-
CMake,配置待构建的项目
-
Ninja,用于构建项目
-
esptool.py,烧录目标硬件设备
从上面可以看出管理项目架构使用的是CMake,一个 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/
-
顶层项目 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
可以看出,CMake文件分为顶层CMake和组件CMake,顶层CMake通过包含的方式包含组件CMake
顶层CMake为文件如下所示
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(EXTRA_COMPONENT_DIRS "./conponets/conponent1" | "./conponets/conponent2")
project(myProject)
一个CMake文件应该首先指定所需的CMake的最小版本,然后包含了$IDF_PATH/tools/cmake/project.cmake
这个配置文件,最后project(project name)
指定了构建项目的名称。
在这个文件中就出现了EXTRA_COMPONENTS_DIRS,其实从其名字里不难看出EXTRA(外部)、COMPONENTS(组件)、DIRS(文件夹)它是用来保存外部组件文件夹的一个CMake变量
因此可以通过set(EXTRA_CONPONENTS)的方式导入外部组件,但是这条命令只能使用一次,多次使用时后面的会覆盖前面的,可以通过set(EXTRA_COMPONENT_DIRS “./user_components_1” | “./user_components_2”)的方式或者使用CMake的函数list(APPEND EXTRA_COMPONENT_DIRS “子目录”)这两种方法实现包含多个组件。
组件CMakeLists.txt
每个组件都包含一个CMakeLists.txt用于包含组件所包含的文件,便于顶层CMake索引。
最小组件 CMakeLists.txt 文件通过使用 idf_component_register
将组件添加到构建系统中。
idf_component_register(SRCS “src1.c”INCLUDE_DIRS “include” REQUIRES demo1PRI_REQUIRES demo2)
-
SRCS 是组件包含的源文件列表(.c、.cpp、.cc、.S),里面所有的源文件都将会编译进组件库中。
-
INCLUDE_DIRS 是组件的头文件列表,里面的路径会被添加到所有需要该组件的组件(包括 main 组件)全局 include 搜索路径中。
-
REQUIRES 实际上并不是必需的,但通常需要它来声明该组件需要使用哪些其它组件,举个例子,当自己写的组件的头文件需要依赖其他组件的时候需要在REQUIRES后跟所需的组件名称
-
PRI_REQUIRES与REQUIRES的作用类似,都是包含所需的组件,但是PRI_REQUIRES的意思是私有依赖,即源文件中需要包含组件的头文件时可以使用PRI_REQUIRES来包含组件,而REQUIRES是头文件包含其他组件的头文件时用的。
在 CMake 中,REQUIRES 和 PRIV_REQUIRES 是 CMake 函数 target_link_libraries(… PUBLIC …) 和 target_link_libraries(… PRIVATE …) 的近似包装。
下面是一个官方的示例:
组件依赖示例
假设现在有一个 car 组件,它需要使用 engine 组件,而 engine 组件需要使用 spark_plug 组件:
- autoProject/- CMakeLists.txt- components/ - car/ - CMakeLists.txt- car.c- car.h- engine/ - CMakeLists.txt- engine.c- include/ - engine.h- spark_plug/ - CMakeLists.txt- spark_plug.c- spark_plug.h
Car 组件
car.h 头文件是 car 组件的公共接口。该头文件直接包含了 engine.h,这是因为它需要使用 engine.h 中的一些声明:
/* car.h */
#include "engine.h"#ifdef ENGINE_IS_HYBRID
#define CAR_MODEL "Hybrid"
#endif
同时 car.c 也包含了 car.h:
/* car.c */
#include "car.h"
这代表文件 car/CMakeLists.txt 需要声明 car 需要 engine:
idf_component_register(SRCS "car.c"INCLUDE_DIRS "."REQUIRES engine)
-
SRCS 提供 car 组件中源文件列表。
-
INCLUDE_DIRS 提供该组件公共头文件目录列表,由于 car.h 是公共接口,所以这里列出了所有包含了 car.h 的目录。
-
REQUIRES 给出该组件的公共接口所需的组件列表。由于 car.h 是一个公共头文件并且包含了来自 engine 的头文件,所以我们这里包含 engine。这样可以确保任何包含 car.h 的其他组件也能递归地包含所需的 engine.h。
Engine 组件
engine 组件也有一个公共头文件 include/engine.h,但这个头文件更为简单:
/* engine.h */
#define ENGINE_IS_HYBRIDvoid engine_start(void);
engine.c :
/* engine.c */
#include "engine.h"
#include "spark_plug.h"...
在该组件中,engine 依赖于 spark_plug,但这是私有依赖关系。编译 engine.c 需要 spark_plug.h 但不需要包含 engine.h。
这代表文件 engine/CMakeLists.txt 可以使用 PRIV_REQUIRES
:
idf_component_register(SRCS "engine.c"INCLUDE_DIRS "include"PRIV_REQUIRES spark_plug)
因此,car 组件中的源文件不需要在编译器搜索路径中添加 spark_plug include 目录。这可以加快编译速度,避免编译器命令行过于的冗长。
Spark Plug 组件
spark_plug 组件没有依赖项,它有一个公共头文件 spark_plug.h,但不包含其他组件的头文件。
这代表 spark_plug/CMakeLists.txt 文件不需要任何 REQUIRES 或 PRIV_REQUIRES:
idf_component_register(SRCS "spark_plug.c"INCLUDE_DIRS ".")
源文件 Include 目录
每个组件的源文件都是用这些 Include 路径目录编译的,这些路径在传递给 idf_component_register 的参数中指定:
idf_component_register(..INCLUDE_DIRS "include"PRIV_INCLUDE_DIRS "other")
当前组件的 INCLUDE_DIRS 和 PRIV_INCLUDE_DIRS。
REQUIRES 和 PRIV_REQUIRES 参数指定的所有其他组件(即当前组件的所有公共和私有依赖项)所设置的 INCLUDE_DIRS。
递归列出所有组件 REQUIRES 列表中 INCLUDE_DIRS 目录(如递归展开这个组件的所有公共依赖项)。
顶层CMakeLists.txt
參考上面列出來的頂層CMake,需要使用set(EXTRA_COMPONENT_DIRS "./user_components_1" | "./user_components_2")
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(EXTRA_COMPONENT_DIRS "./car")
project(smartconfig)
总结:
如果要添加一个组件,我们需要修改顶层CMakeLists.txt以及新添加的组件的CMakeLists.txt,具体内容参考上面示例
参考:
-
乐鑫官方构建系统
-
https://blog.csdn.net/lum250/article/details/123382392
-
https://blog.csdn.net/RuiyaoNi/article/details/124804251
-
https://blog.csdn.net/DINGDING_GO/article/details/112724707