我使用IAR 8.10.1,它使用ILINK链接器。
我有一个共同的标题,两个编译单元使用。它包括具有外部链接的函数的原型,并构成一个API。根据构建的配置方式,我希望模块A或B与我的应用程序的其余部分链接。
[ Common_Header.h ]
| |
| +----- [Module_A.c] ---> [Module_A.o]
|
+--------------- [Module_B.c] ---> [Module_B.o]不知何故,我想将一个参数传递给ilinkarm.exe,以包括Module_A.o。
我在过去使用过的其他IAR工具链使用了XLINK链接器。XLINK有一个-A选项,我认为这与我所需要的类似。
本质上,我希望Module_B中的函数定义被当作是__weak,而Module_A是活动的,反之亦然。
如果可能的话,我希望避免将#pragma weak放在代码中。我需要能够用几个不同的工具链编译这段代码。因此,我需要用类似于#ifdef __ICCARM__的东西来包装任何这样的沼气。此外,我还需要定义一些额外的预处理符号,以便在一个模块处于活动状态时有条件地使另一个模块弱。这都是复杂的问题,我更希望避免出现在代码中。
此外,当module_B处于活动状态时,我不希望将module_A排除在构建之外。我希望这两个模块都能编译。如果有人对接口和module_A进行了更改,但未能更新module_B,我希望他们得到一个编译器错误。这将使module_B不会随着接口的发展而陷入孤立和中断的状态,我们的注意力集中在module_A上。
我已经复习了EWARM_DevelopmentGuide.ENU.pdf,我找不到一个命令行选项,它似乎可以做我想做的事情。我想知道是否存在这样的选择,我已经错过了,或者是否有另一种方式来完成我想要的。
发布于 2019-03-18 17:45:59
最后,我使用了类似于user694733建议的弱链接。但我的方法有点不同。
我在模块A和B的顶部加了一个这样的块。
#if (defined __ICCARM__)
#if(defined USE_MODULE_A) && (1 == USE_MODULE_A)
// do nothing, make definitions in this file strong
#elif(defined USE_MODULE_B) && (1 == USE_MODULE_B)
#pragma weak foo_fn
#pragma weak bar_fn
#pragma weak baz_fn
#pragma weak qux_fn
#else
#error USE_MODULE_A or USE_MODULE_B must be defined.
#endif
#endif这种方法不需要我用MY_WEAK来装饰每个函数原型。所以这些非标准的东西都被聚在一起了。
我不喜欢使用__weak / #pragma弱的几样东西:
我不喜欢的第一件事是,它增加了两个模块之间的耦合。如果两个符号都没有定义,那么两个模块都会有弱定义。在那个时候,你怎么知道哪一个会被使用?因此,每个模块都必须存在另一个模块,或者至少有一个以上的选项。我本可以使用单个定义,只需更改值,但我选择了这样做,以便名称具有描述性。
我不喜欢的第二件事是,我把代码中的东西弄得乱七八糟,这是一个关于如何构建项目的工件。我想把这种逻辑拉出来,并在实际可行的时候把它放到建设系统中。
第三,它不是完全可移植的,必须用#if (defined __ICCARM__)关闭。
但这将是我所使用的,除非我找到更好的方法来完成这对我。如果发生这种情况,我会发帖/接受其他答案。
发布于 2019-03-05 15:18:10
这并不是一个完整的答案,因为我没有你的编译器的新版本,但更多的是一个可能的解决办法。
Module_A.c
#if MODULE_A_SELECTED
#define MY_WEAK
#else
#define MY_WEAK __weak
#endif
MY_WEAK void foo(void) { ... }
...Module_B.c
#if MODULE_B_SELECTED
#define MY_WEAK
#else
#define MY_WEAK __weak
#endif
MY_WEAK void foo(void) { ... }
...然后根据需要在配置中定义MODULE_*_SELECTED。
发布于 2019-03-05 17:03:08
不需要依赖特定于链接器的支持或IDE特定的构建管理。一个完全可移植的解决方案是用不同的符号名定义A和B实现,然后使用有条件定义的宏来选择所需的实现。
示例:
#if defined USE_IMPLEMENTATION_A
#define doSomething implementationA_doSomething
#elif defined USE_IMPLEMENTATION_B
#define doSomething implementationB_doSomething
#else
#error API implementation not defined
#endif
int implementationA_doSomething( void ) ;
int implementationB_doSomething( void ) ;这样,实现A和B都将被编译,但是只使用宏doSomething而不是特定于实现的函数名来使用所选的API。
我不知道ILINK有多聪明,但是通过将实现放在单独的翻译单元(即.c文件)中,链接器应该能够从链接中消除未使用的函数。如果不是,如果您将对象代码放置在静态链接库(.lib或.a)中,则肯定会这样做。
为了解决维护两个除了名称空间前缀之外相同的实现文件的问题,您可以使用以下原型创建一个虚拟头文件:
int NAMESPACE_doSomething( void ) ;然后使用像sed这样的工具来生成实现原型头,例如:
sed -i 's/NAMESPACE/api_a/g' api_dummy.h > api_a.h
sed -i 's/NAMESPACE/api_b/g' api_dummy.h > api_b.h然后有一个api.h文件,其中包含(片段):
#if defined USE_IMPLEMENTATION_A
#define doSomething api_a_doSomething
#elif defined USE_IMPLEMENTATION_B
#define doSomething api_b_doSomething
#else
#error API implementation not defined
#endif
#include api_a.h
#include api_b.h您可以进一步编写代码生成器,从函数名列表生成api.h。在您喜欢的脚本语言甚至C语言中,这不会太困难。您可以编写这样的生成器来获取命令行参数:
generate_api <input> <output> <namespace1> <namespace2> ... <namespaceN>那就叫它:
generate_api functions.txt api.h api_a api_b您甚至可以使用虚拟头中的NAMESPACE_文本来为<input>生成函数名列表,这样就可以从单个虚拟头生成整个API头集。
https://stackoverflow.com/questions/54990942
复制相似问题