目录结构
创建一个简单的esp_hello_word工程,目录结构如下
├── CMakeLists.txt        
├── main
│   ├── CMakeLists.txt
│   ├── component.mk
│   └── main.c
├── Makefile
└── sdkconfig   main:工程主代码目录
     CMakeLists.txt:main 文件夹下的的 CMake 配置文件
     component.mk:main 组件的配置文件
     main.c:主代码,程序入口文件
   CMakeLists.txt:CMake 配置文件
   Makefile:make 配置文件
   sdkconfig:【自动生成】通过 menuconfig 生成的配置文件
主目录下有CMakeLists.txt和Makefile文件,其实这分别对应着两种编译指令。CMakeLists.txt对应执行CMake指令,已经封装在idf.py指令中。对应文件如下:
├── CMakeLists.txt
└── main
    ├── CMakeLists.txt
    └── main.cMakefile对应执行make指令。对应文件如下:
├── main
│   ├── component.mk
│   └── main.c
└── Makefile接下来对文件进行分析。
CMakeLists.txt
主目录esp_hello_word文件夹下的CMakeLists.txt文件内容:
# 以下几行引用必须在您的项目中
# CMakeLists 的正确顺序,以便cmake正常工作
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp_hello_word)cmake_minimum_required(VERSION 3.5)
定义了 CMake 所支持的最小版本。
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
来包含 esp-idf 工程下的 project.cmake 文件,这个文件包含许多需要加载的组件。
project(esp_hello_word) 
工程名为 esp_hello_word。
Makefile
#    这是一个项目生成文件。
#    假设这个Makefile所在的目录是一个项目子目录。
#    项目名称
PROJECT_NAME := esp_hello_word
#    顶层项目目录,默认是包含 Makefile 文件的目录,许多其他的项目变量都基于此变量。
include $(IDF_PATH)/make/project.mkmain目录
CMakeLists.txt
idf_component_register(SRCS "hello_world_main.c"
                    INCLUDE_DIRS "")最小组件 CMakeLists.txt 文件通过使用 idf_component_register 将组件添加到构建系统中。
- SRCS是源文件列表(.c、.cpp、.cc、.S),里面所有的源文件都将会编译进组件库中。
- INCLUDE_DIRS是目录列表,里面的路径会被添加到所有需要该组件的组件(包括 main 组件)全局 include 搜索路径中。
- REQUIRES实际上并不是必需的,但通常需要它来声明该组件需要使用哪些其它组件,请参考 组件依赖.
上述命令会构建生成与组件同名的库,并最终被链接到应用程序中。
上述目录通常设置为相对于 CMakeLists.txt 文件的相对路径,当然也可以设置为绝对路径。
有关更完整的 CMakeLists.txt 示例,请参阅 组件依赖示例 和 组件 CMakeLists 示例。
idf_component_register原型
idf_component_register([[SRCS src1 src2 ...] | [[SRC_DIRS dir1 dir2 ...] [EXCLUDE_SRCS src1 src2 ...]]
                       [INCLUDE_DIRS dir1 dir2 ...]
                       [PRIV_INCLUDE_DIRS dir1 dir2 ...]
                       [REQUIRES component1 component2 ...]
                       [PRIV_REQUIRES component1 component2 ...]
                       [LDFRAGMENTS ldfragment1 ldfragment2 ...]
                       [REQUIRED_IDF_TARGETS target1 target2 ...]
                       [EMBED_FILES file1 file2 ...]
                       [EMBED_TXTFILES file1 file2 ...]
                       [KCONFIG kconfig]
                       [KCONFIG_PROJBUILD kconfig_projbuild])- SRCS - 组件的源文件,用于为组件创建静态库;如果没有指定,组件将被视为仅配置组件,从而创建接口库。
- SRC_DIRS, EXCLUDE_SRCS - 用于通过指定目录来 glob 源文件 (.c、.cpp、.S),而不是通过 SRCS 手动指定源文件。请注意,这受 CMake 中通配符的限制。 在 EXCLUDE_SRCS - 中指定的源文件会从被 glob 的文件中移除。
- INCLUDE_DIRS - 相对于组件目录的路径,该路径将被添加到需要当前组件的所有其他组件的 include 搜索路径中。
- PRIV_INCLUDE_DIRS - 必须是相对于组件目录的目录路径,它仅被添加到这个组件源文件的 include 搜索路径中。
- REQUIRES - 组件的公共组件依赖项。
- PRIV_REQUIRES - 组件的私有组件依赖项;在仅用于配置的组件上会被忽略。
- LDFRAGMENTS - 组件链接器片段文件。
- REQUIRED_IDF_TARGETS - 指定该组件唯一支持的目标。
- KCONFIG - 覆盖默认的 Kconfig 文件。
- KCONFIG_PROJBUILD - 覆盖默认的 Kconfig.projbuild 文件。
main.c
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
void app_main(void)
{
    printf("Hello world!\n");
    /* 打印芯片信息 */
    esp_chip_info_t chip_info;
    esp_chip_info(&chip_info);
    printf("芯片内核数: %d\n", chip_info.cores);
    printf("芯片修订号: %d\n", chip_info.revision);
    printf("%s flash:%dMB \n", (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "片上" : "片外",
                              spi_flash_get_chip_size() / (1024 * 1024));
    printf("芯片功能标志: %x  ", chip_info.revision);
    printf("[%s%s%s]\n",  (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "2.4GHz WiFi" : "",
                                (chip_info.features & CHIP_FEATURE_BT) ? "/常规蓝牙" : "",
                                (chip_info.features & CHIP_FEATURE_BLE) ? "/低功耗蓝牙" : "");
    /* 重启 */
    for (int i = 10; i >= 0; i--) {
        printf("%2d 秒后重启...\n", i);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    printf("重启...\n");
    fflush(stdout);
    esp_restart();
}sdkconfig
sdkconfig 文件由 make menuconfig 自动生成,存储配置参数。
添加组件
传统 GNU Make
目录结构更改如下
├── components            -- 新增 组件目录
│   ├── component.mk      -- 新增 子目录component.mk
│   ├── include           -- 新增 组件头文件目录
│   │   └── chip.h        -- 新增 chip.h 头文件
│   └── src               -- 新增 组件头文件目录
│       └── chip.c        -- 新增 chip.c 源文件           
├── main
│   ├── component.mk
│   └── main.c            -- 修改
├── Makefile              -- 修改
└── sdkconfig修改主目录下的Makefile
# 将自建组件添加到包含路径
COMPONENT_ADD_INCLUDEDIRS := components/includecomponents 目录下 component.mk内容如下
#
# Component Makefile
#
# 头文件的包含路径
COMPONENT_ADD_INCLUDEDIRS := include
# 源文件的包含路径
COMPONENT_SRCDIRS := srcchip.c
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
void chip_info(void)
{
    /* 打印芯片信息 */
    esp_chip_info_t chip_info;
    esp_chip_info(&chip_info);
    printf("芯片内核数: %d\n", chip_info.cores);
    printf("芯片修订号: %d\n", chip_info.revision);
    printf("%s flash:%dMB \n", (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "片上" : "片外",
                              spi_flash_get_chip_size() / (1024 * 1024));
    printf("芯片功能标志: %x  ", chip_info.revision);
    printf("[%s%s%s]\n",  (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "2.4GHz WiFi" : "",
                                (chip_info.features & CHIP_FEATURE_BT) ? "/常规蓝牙" : "",
                                (chip_info.features & CHIP_FEATURE_BLE) ? "/低功耗蓝牙" : "");
}chip.h
#ifndef __CHIP_H__
#define __CHIP_H__
void chip_info(void);
#endifmain.c
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "chip.h"
void app_main(void)
{
    printf("Hello world!\n");
    chip_info();
    /* 重启 */
    for (int i = 10; i >= 0; i--) {
        printf("%2d 秒后重启...\n", i);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    printf("重启...\n");
    fflush(stdout);
    esp_restart();
}
CMake方式
├── CMakeLists.txt
├── components            -- 新增 组件目录
│   ├── CMakeLists.txt    -- 新增 CMakeLists.txt
│   ├── include           -- 新增 组件头文件目录
│   │   └── chip.h        -- 新增 chip.h 头文件
│   └── src               -- 新增 组件头文件目录
│       └── chip.c        -- 新增 chip.c 源文件  
├── main
│   ├── CMakeLists.txt
│   └── main.c
└── sdkconfigcomponents文件夹下CMakeLists.txt内容如下:
set(srcs "src/chip.c")
idf_component_register(SRCS "${srcs}"
                    INCLUDE_DIRS include
                    REQUIRES spi_flash)其中chip.c引用了#include "esp_spi_flash.h"文件,所以使用REQUIRES进行声明。
而"esp_spi_flash.h" 文件位于esp-idf->components->spi_flash组件下。
完整目录如下
├── CMakeLists.txt
├── components
│   ├── CMakeLists.txt
│   ├── component.mk
│   ├── include
│   │   └── chip.h
│   └── src
│       └── chip.c
├── main
│   ├── CMakeLists.txt
│   ├── component.mk
│   └── main.c
├── Makefile
└── sdkconfig最小组件 Makefile
最小的 component.mk 文件是一个空文件。如果文件为空,则设置默认组件行为:与 makefile 在相同的目录中的所有源文件(.c,.cpp,.cc,.S)将被编译到组件库中子目录 “include” 将被添加到所有其他组件的全局 include 搜索路径中。组件库将链接到项目应用程序中。
预设组件变量COMPONENT_PATH:组件目录.计算包含 component.mk 的目录的绝对路径.组件路径不能包含空格.COMPONENT_NAME:组件的名称.默认为组件目录的名称.COMPONENT_BUILD_DIR:组件构建目录.计算 $(BUILD_DIR_BASE) 中要构建此组件源文件的目录的绝对路径.每次构建组件时,这也是当前工作目录,因此 make 等目标中的相对路径都是相对于此目录.COMPONENT_LIBRARY:将为此组件构建的静态库文件的名称(相对于组件构建目录).默认为 $(COMPONENT_NAME).a.
以下变量在项目级别设置,但会导出在组件构建中使用:
PROJECT_NAME:项目名称,在项目 Makefile 中设置PROJECT_PATH:包含项目 Makefile 的项目目录的绝对路径.COMPONENTS:此构建中包含的所有组件的名称.CONFIG_ *:项目配置中的每个值都有一个 make 中可用的对应变量.所有名称都以 CONFIG_ 开头.
CC,LD,AR,OBJCOPY:gcc xtensa 交叉工具链中每个工具的完整路径.
HOSTCC,HOSTLD,HOSTAR:来自主机本机工具链的每个工具的全名.IDF_VER:ESP-IDF 版本,使用 git 命令 git describe 从 $(IDF_PATH)/version.txt 文件(如果存在)中检索.这里推荐的格式是单独的一行指定主要 IDF 发布版本,例如标记版本的 v2.0 或任意提交的 v2.0-275-g0efaa4f.应用程序可以通过调用 esp_get_idf_version() 来使用它.PROJECT_VER: 项目版本
如果 PROJECT_VER 变量在项目 Makefile 文件中设置,则将使用其值。
否则,如果 $PROJECT_PATH/version.txt 存在,其内容将用作 PROJECT_VER。
否则,如果项目位于 Git 存储库中,则将使用git describe的输出。
否则,PROJECT_VER 将为“1”。
如果您修改 component.mk 中的任何这些变量,那么这不会阻止构建其他组件,但它可能使您的组件难以构建或者调试.
可选项目范围的组件变量
可以在 component.mk 中设置以下变量来控制整个项目中的构建设置:
COMPONENT_ADD_INCLUDEDIRS:相对于组件目录的路径,将添加到项目中所有组件的 “include” 搜索路径.如果未被覆盖,则默认include.如果仅需要编译此特定组件的 “include” 目录,请将其添加到 COMPONENT_PRIV_INCLUDEDIRSCOMPONENT_ADD_LDFLAGS:为 LDFLAGS 添加链接器参数以用于应用程序可执行文件.默认为 -l$(COMPONENT_NAME).如果将预编译库添加到此目录,请将它们添加为绝对路径 -e$(COMPONENT_PATH)/libwhatever.aCOMPONENT_DEPENDS:应在此组件之前编译的组件名称的可选列表.对于链接时依赖性,这不是必需的,因为所有组件"include"目录始终可用.如果一个组件生成一个"include"文件,然后您想要包含在另一个组件中,则这是必要的.大多数组件不需要设置此变量.COMPONENT_ADD_LINKER_DEPS:相对组件路径的文件的可选列表,如果它们发生更改,应触发 ELF 文件的重新链接。 通常用于链接描述文件和二进制库。大多数组件不需要设置此变量。
以下变量仅适用于属于 esp-idf 本身的组件:
COMPONENT_SUBMODULES:组件使用的 git 子模块路径(相对于 COMPONENT_PATH)的可选列表.这些将由构建过程检查(并在必要时初始化).如果组件位于 IDF_PATH 目录之外,则忽略此变量.
可选特定的组件变量
可以在component.mk中设置以下变量来控制该组件的构建:
COMPONENT_PRIV_INCLUDEDIRS:目录路径,必须相对于组件目录,该组件目录将仅添加到此组件源文件的"include"搜索路径中.COMPONENT_EXTRA_INCLUDES:编译组件源文件时使用的任何额外包含路径.这些将以’-I’为前缀,并按原样传递给编译器.与COMPONENT_PRIV_INCLUDEDIRS变量类似,但这些路径不会相对于组件目录进行扩展.COMPONENT_SRCDIRS:目录路径,必须相对于组件目录,将用以搜索源文件( .cpp, .c,* .S).默认为’.’,即组件目录本身.覆盖它以指定包含源文件的不同目录列表.COMPONENT_OBJS:要编译的对象文件.默认值是COMPONENT_SRCDIRS中找到的每个源文件的 a.o 文件.覆盖此列表允许您排除COMPONENT_SRCDIRS中的源文件,否则将被编译.请参阅指定源文件COMPONENT_EXTRA_CLEAN:相对于组件构建目录的路径,使用component.mk文件中的自定义make规则生成的任何文件,以及作为make clean的一部分需要删除的文件.有关示例,请参阅源代码生成.
COMPONENT_OWNBUILDTARGET&COMPONENT_OWNCLEANTARGET:这些目标允许您完全覆盖组件的默认构建行为.有关详细信息,请参阅完全覆盖组件 Makefile.COMPONENT_CONFIG_ONLY:如果设置,则此标志指示组件根本不生成任何内置输出(即未构建 COMPONENT_LIBRARY),并忽略大多数其他组件变量.此标志用于 IDF 内部组件,其中仅包含 KConfig.projbuild 和/或 Makefile.projbuild 文件以配置项目,但没有源文件.CFLAGS:传递给 C 编译器的标志.根据项目设置定义一组默认 CFLAGS.可以通过 CFLAGS += 进行组件特定的添加.也可以(尽管不推荐)完全覆盖该组件的变量.CPPFLAGS:传递给 C 预处理器的标志(用于. c , .cpp 和 .S 文件).根据项目设置定义一组默认的 CPPFLAGS.可以通过 CPPFLAGS += 进行组件特定的添加.也可以(尽管不推荐)完全覆盖该组件的变量.CXXFLAGS:传递给 C++ 编译器的标志.根据项目设置定义一组默认的 CXXFLAGS.可以通过 CXXFLAGS += 进行组件特定的添加.也可以(尽管不推荐)完全覆盖该组件的变量.
结构参考
CMake示例项目
- 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/GNU Make示例项目
- myProject/
             - Makefile
             - sdkconfig
             - components/ - component1/ - component.mk
                                         - Kconfig
                                         - src1.c
                           - component2/ - component.mk
                                         - Kconfig
                                         - src1.c
                                         - include/ - component2.h
             - main/       - src1.c
                           - src2.c
                           - component.mk
             - build/个人工程分析
目录结构如下
.
├── CMakeLists.txt
├── components
│   ├── mj_http
│   │   ├── CMakeLists.txt
│   │   ├── component.mk
│   │   ├── include
│   │   │   └── mj_http.h
│   │   └── mj_http.c
│   ├── mj_nvs
│   │   ├── CMakeLists.txt
│   │   ├── component.mk
│   │   ├── include
│   │   │   └── mj_nvs.h
│   │   └── mj_nvs.c
│   └── mj_wifi
│       ├── CMakeLists.txt
│       ├── component.mk
│       ├── include
│       │   └── mj_wifi.h
│       └── mj_wifi.c
├── main
│   ├── CMakeLists.txt
│   ├── component.mk
│   ├── howsmyssl_com_root_cert.pem
│   └── mj_main.c
├── Makefile
├── README.md
└── sdkconfig
CMake 部分
CMakeLists.txt
# The following five 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.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(daobanmojie)main/CMakeLists.txt
# Embed the server root certificate into the final binary
#
# (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.)
idf_component_register(SRCS "mj_main.c"
                    INCLUDE_DIRS "."
                    EMBED_TXTFILES howsmyssl_com_root_cert.pem)components/mj_http/CMakeLists.txt
idf_component_register(SRCS "mj_http.c"
                    INCLUDE_DIRS "include"
                    REQUIRES esp_http_client json esp-tls   )GNU Make 部分
Makefile
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := daobanmojie
include $(IDF_PATH)/make/project.mkmain/component.mk
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
#
components/mj_http/component.mk
#
# Component Makefile
#
# 头文件的包含路径
COMPONENT_ADD_INCLUDEDIRS := include
# 源文件的包含路径
#COMPONENT_SRCDIRS := src
COMPONENT_SRCDIRS := . 
                            