IAR EWARM 中的条件链接
Conditional Linking in IAR EWARM
我正在使用 IAR EWARM 8.10.1,它使用 ILINK 链接器。
我有两个编译单元共同使用的header。它包括具有外部链接的函数原型,构成了一个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 中的函数定义在 Module_A 处于活动状态时被视为 __weak
,反之亦然。
我想尽可能避免在我的代码中加入 #pragma weak
。我需要能够使用几个不同的工具链来编译这段代码。所以我需要用 #ifdef __ICCARM__
之类的东西包裹任何这样的 pramgas。此外,我需要定义一些额外的预处理器符号,以有条件地使一个模块在另一个模块处于活动状态时变弱。这些都是我希望避免出现在代码中的复杂性。
此外,当 module_A 处于活动状态时,我不想从构建中排除 module_B。我希望两个模块都能编译。如果有人对界面和 module_A 进行了更改,但未能更新 module_B,我希望他们得到一个编译器错误。随着界面的发展和我们的注意力集中在 module_A.
上,这将防止 module_B 进入某些孤立和损坏的状态
我查看了 EWARM_DevelopmentGuide.ENU.pdf,但我找不到似乎可以满足我要求的命令行选项。我想知道是否存在这样的选项,但我错过了,或者是否有其他方法可以实现我所追求的目标。
这并不是一个完整的答案,因为我没有像您那样的新版本的编译器,但有更多可能的解决方法。
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
。
无需依赖 link 特定支持或 IDE 特定构建管理。一个完全可移植的解决方案是使用不同的符号名称定义 A 和 B 实现,然后使用条件定义的宏来 select 所需的实现。
示例:
#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 都将始终被编译,但只有 selected API 将通过使用宏 doSomething
而不是 implementation-specific 函数名.
我不知道 ILINK 有多聪明,但通过将实现放在单独的翻译单元(即 .c 文件)中,linker 应该能够从 link.如果不是,如果您将 object 代码放在 static-link 库(.lib 或 .a)中,它肯定会。
要解决维护两个除了命名空间前缀外都相同的实现文件的问题,您可以创建一个虚拟 header 文件,其原型如下:
int NAMESPACE_doSomething( void ) ;
然后 pre-build 步骤使用 sed 等工具生成实现原型 headers,例如:
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
您甚至可以使用虚拟 header 中的 NAMESPACE_
文本来生成 <input>
的函数名称列表,这样整个 API header set 可以从单个虚拟 header.
生成
我最终使用了类似于 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
weak:
的几个方面
我首先不喜欢的是它增加了两个模块之间的耦合。如果两个符号都没有定义,那么两个模块都将具有弱定义。到那时,您怎么知道将使用哪一个?因此,每个模块都必须存在另一个模块,或者至少有多个选项。我本可以使用单一定义并更改值,但我选择这样做以便名称具有描述性。
我不喜欢的第二件事是我用项目构建方式的人工产物使代码混乱。我想把这样的逻辑拉出来,在可行的时候把它放到构建系统中。
第三个是它不是完全便携的,必须使用 #if (defined __ICCARM__)
。
但这将是我使用的,除非我找到一种更适合我的方法来完成它。如果发生这种情况,我会 post/accept 其他答案。
我正在使用 IAR EWARM 8.10.1,它使用 ILINK 链接器。
我有两个编译单元共同使用的header。它包括具有外部链接的函数原型,构成了一个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 中的函数定义在 Module_A 处于活动状态时被视为 __weak
,反之亦然。
我想尽可能避免在我的代码中加入 #pragma weak
。我需要能够使用几个不同的工具链来编译这段代码。所以我需要用 #ifdef __ICCARM__
之类的东西包裹任何这样的 pramgas。此外,我需要定义一些额外的预处理器符号,以有条件地使一个模块在另一个模块处于活动状态时变弱。这些都是我希望避免出现在代码中的复杂性。
此外,当 module_A 处于活动状态时,我不想从构建中排除 module_B。我希望两个模块都能编译。如果有人对界面和 module_A 进行了更改,但未能更新 module_B,我希望他们得到一个编译器错误。随着界面的发展和我们的注意力集中在 module_A.
上,这将防止 module_B 进入某些孤立和损坏的状态我查看了 EWARM_DevelopmentGuide.ENU.pdf,但我找不到似乎可以满足我要求的命令行选项。我想知道是否存在这样的选项,但我错过了,或者是否有其他方法可以实现我所追求的目标。
这并不是一个完整的答案,因为我没有像您那样的新版本的编译器,但有更多可能的解决方法。
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
。
无需依赖 link 特定支持或 IDE 特定构建管理。一个完全可移植的解决方案是使用不同的符号名称定义 A 和 B 实现,然后使用条件定义的宏来 select 所需的实现。
示例:
#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 都将始终被编译,但只有 selected API 将通过使用宏 doSomething
而不是 implementation-specific 函数名.
我不知道 ILINK 有多聪明,但通过将实现放在单独的翻译单元(即 .c 文件)中,linker 应该能够从 link.如果不是,如果您将 object 代码放在 static-link 库(.lib 或 .a)中,它肯定会。
要解决维护两个除了命名空间前缀外都相同的实现文件的问题,您可以创建一个虚拟 header 文件,其原型如下:
int NAMESPACE_doSomething( void ) ;
然后 pre-build 步骤使用 sed 等工具生成实现原型 headers,例如:
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
您甚至可以使用虚拟 header 中的 NAMESPACE_
文本来生成 <input>
的函数名称列表,这样整个 API header set 可以从单个虚拟 header.
我最终使用了类似于 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
weak:
我首先不喜欢的是它增加了两个模块之间的耦合。如果两个符号都没有定义,那么两个模块都将具有弱定义。到那时,您怎么知道将使用哪一个?因此,每个模块都必须存在另一个模块,或者至少有多个选项。我本可以使用单一定义并更改值,但我选择这样做以便名称具有描述性。
我不喜欢的第二件事是我用项目构建方式的人工产物使代码混乱。我想把这样的逻辑拉出来,在可行的时候把它放到构建系统中。
第三个是它不是完全便携的,必须使用 #if (defined __ICCARM__)
。
但这将是我使用的,除非我找到一种更适合我的方法来完成它。如果发生这种情况,我会 post/accept 其他答案。