C esp idf:具有相同功能的组件的条件包含
我正在做一个项目,需要为几个esp32开发固件。所有的微控制器共享一个共同的代码来处理wifi和mqtt,但是它们都有不同的行为,这是在特定的组件中定义的。我的项目结构如下:C esp idf:具有相同功能的组件的条件包含,c,esp-idf,C,Esp Idf,我正在做一个项目,需要为几个esp32开发固件。所有的微控制器共享一个共同的代码来处理wifi和mqtt,但是它们都有不同的行为,这是在特定的组件中定义的。我的项目结构如下: - CMakeLists.txt - Makefile - sdkconfig - main - CMakeLists.txt - main.c - components - wifi_fsm - wifi_fs
- CMakeLists.txt
- Makefile
- sdkconfig
- main
- CMakeLists.txt
- main.c
- components
- wifi_fsm
- wifi_fsm.h
- wifi_fsm.c
- CMakeLists.txt
- mqtt_fsm
- mqtt_fsm.h
- mqtt_fsm.c
- CMakeLists.txt
- entity_1
- entity_1.h
- entity_1.c
- CMakeLists.txt
- entity2
- entity2.h
- entity2.c
- CMakeLists.txt
...
每个实体都定义了一些具有标准名称的函数,这些函数为实体本身实现特定的逻辑,并在共享代码(main、wifi_-fsm、mqtt_-fsm)中调用
我的想法是使用一个条件语句任意包含一个特定的行为,这样编译器就可以根据所包含的实体,将对上述函数的调用链接到特定包含库中的函数。因此,在main.c
的开头,我刚刚添加了以下几行,目的是必须更改唯一定义的预处理器符号以编译不同的enity行为
#define ENTITY_1
#ifdef ENTITY_1
#include "entity_1.h"
#elif defined ENTITY_2
#include "entity_2.h"
#elif ...
#endif
#include "wifi_fsm.h"
#include "mqtt_fsm.h"
void app_main(void)
{
while(1){
...
}
}
一方面,编译器显然工作正常,在没有错误或警告的情况下成功编译,这意味着include链工作正常,否则将抛出标准函数的重复名称错误。另一方面,它总是按字母顺序与第一个实体链接,例如执行组件实体_1的init_entity()中包含的代码。如果我重命名实体_1中的标准函数,那么它将链接到实体_2
如果上述方法错误,我可能会使用指向标准调用的指针链接到每个实体中的特定函数,但我想首先了解我的方法中的错误
编辑以响应Bodo的请求(CMakeFile.txt的内容)
项目:
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(proj)
主要内容:
组成部分:
set(COMPONENT_SRCDIRS "src")
set(COMPONENT_ADD_INCLUDEDIRS "include")
set(COMPONENT_REQUIRES log freertos driver nvs_flash esp_http_server mqtt)
register_component()
这个答案是基于猜测,因为我没有足够的信息。出于同样的原因,它在某些部分是不完整的,或者可能与问题的用例不完全匹配 关于如何构建项目的详细信息似乎隐藏在
cmake
include文件中,如project.cmake
或嵌套的include文件
我的猜测是,构建系统从每个组件的源代码创建库,然后将主对象文件与库链接起来。在这种情况下,链接器将在满足依赖关系的第一个库中找到类似init_entity
的符号。这意味着将使用链接器命令行中首先列出的库(=组件)
如果链接器命令行将显式列出对象文件entity_1.o
和entity_2.o
,我将看到一条关于重复符号init_entity
的错误消息
我可以提出两种解决问题的方法:
对于第一种方法,您可以在
CMakeLists.txt
中使用条件。有关示例,请参见。可能是register\u component()
负责将组件添加到构建中。在这种情况下,您可以将其包装在一个条件中
但是,如果文件是自动生成的,那么修改CMakeLists.txt
可能是错误的
对于第二种方法,您应该重命名实体中的标识符以使其唯一。相应的头文件可以定义一个宏,用选定实体的特定标识符替换标识符的通用名称 在使用选定实体的代码中,您将始终使用通用名称,而不是单个名称 例如:
实体_1.c
:
#include "entity_1.h"
void init_entity_1(void)
{
}
#include "entity_2.h"
void init_entity_2(void)
{
}
实体_2.c
:
#include "entity_1.h"
void init_entity_1(void)
{
}
#include "entity_2.h"
void init_entity_2(void)
{
}
实体_1.h
:
void init_entity_1(void);
// This replaces the token/identifier "init_entity" with "init_entity_1" in subsequent source lines
#define init_entity init_entity_1
// or depending on the parameter list something like
// #define init_entity() init_entity_1()
// #define init_entity(x,y,z) init_entity_1(y,x,z)
void init_entity_2(void);
#define init_entity init_entity_2
实体_2.h
:
void init_entity_1(void);
// This replaces the token/identifier "init_entity" with "init_entity_1" in subsequent source lines
#define init_entity init_entity_1
// or depending on the parameter list something like
// #define init_entity() init_entity_1()
// #define init_entity(x,y,z) init_entity_1(y,x,z)
void init_entity_2(void);
#define init_entity init_entity_2
main.c
#define ENTITY_1
#ifdef ENTITY_1
#include "entity_1.h"
#elif defined ENTITY_2
#include "entity_2.h"
#elif ...
#endif
void some_function(void)
{
init_entity();
}
在本例中,使用#define ENTITY_1
,预处理器将某些功能更改为
void some_function(void)
{
init_entity_1();
}
在编译步骤之前,链接器将使用entity_1.c
中的init_entity_1
。然后,优化链接器可能会忽略对象文件实体_2.o
或相应的库,因为它未使用。请回答您的问题,并添加有关如何定义预处理器符号实体_1
或实体_2
的详细信息,等。您可以修改CMakeLists.txt
以有条件地仅使用组件/实体_1
、组件/实体_2
等中的一个。显示相关的CMakeLists.txt
文件。@Bodo CMakeList.txt设置为默认值。在组件之间相互依赖的情况下,我只需在集合(组件_需要…)列表中添加条目,“CMakeList.txt设置为默认值”不会告诉任何事情。当我使用编辑器创建CMakeLists.txt
时,我的“默认值”是一个空文件。如果您的IDE创建了CMakeLists.txt
文件,请在问题中说明您使用的IDE以及您如何创建项目/组件(如果与文件内容相关)。显示代码,而不是含糊其辞地描述您的工作。如果您的文件太大,无法在问题中显示,请使用较小的文件创建一个。如果没有看到您的CMakeLists.txt
文件,我无法提出解决方案。(可能是其他人拥有一个水晶球…@Bodo我不知道CMakeFile的内容会添加哪些附加信息,但我将其添加到了问题中。现在,如果您的答案是修改CMakeFile以有条件地包含一个特定的组件,那么这可能会起作用,我希望您能提供一些建议。但是,它仍然没有回答我的问题,这就是我的方法的错误所在,以及为什么一个组件中的方法是链接的,即使该组件未包含在内。事实上,CMakeLists.txt
文件中没有包含足够的信息