我正在使用IAR链接程序使用IAR EWARM 8.10.1。
我有两个编译单元使用的公共头文件。它包括具有外部链接的功能原型,并构成一个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_A处于活动状态时将Module_B中的函数定义视为__weak
,反之亦然。
如果可能,我想避免在代码中加入#pragma weak
。我需要能够使用一些不同的工具链来编译此代码。因此,我需要用#ifdef __ICCARM__
之类的东西包装任何这样的婴儿车。此外,我需要定义一些额外的预处理器符号,以有条件地使一个模块在另一个模块处于活动状态时处于弱状态。这就是我想避免的所有复杂性。
此外,当module_A处于活动状态时,我不想从构建中排除module_B。我希望两个模块都能始终编译。如果有人对接口和module_A进行了更改,但未能更新module_B,我希望他们得到编译器错误。随着接口的发展,这将防止module_B陷入孤立和破裂的状态,我们的注意力将集中在module_A上。
我已经查看了EWARM_DevelopmentGuide.ENU.pdf,但找不到似乎可以满足我要求的命令行选项。我想知道是否存在这样的选择,而我错过了,还是有另一种方式来实现我所追求的目标。
答案 0 :(得分:1)
这并不是一个完整的答案,因为我没有像您这样的新版本的编译器,而是更多可行的解决方法。
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
。
答案 1 :(得分:1)
无需依赖链接程序特定的支持或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头文件集。
答案 2 :(得分:1)
我最终使用了类似于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__)
来封闭。
但这将是我所使用的,除非我找到一种方法来完成对我来说更好的工作。如果发生这种情况,我会发布/接受其他答案。