[英]Conditional Linking in IAR EWARM
我正在使用IAR EWARM 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中的函數定義視為在Module_A處於活動狀態時__weak
,反之亦然。
如果可能的話,我想避免在我的代碼中使用#pragma weak
。 我需要能夠使用一些不同的工具鏈來編譯此代碼。 所以我需要用#ifdef __ICCARM__
類的東西包裝任何這樣的#ifdef __ICCARM__
。 此外,我需要定義一些額外的預處理器符號,以便在另一個模塊處於活動狀態時有條件地使一個模塊變弱。 這就是我寧願避開代碼的所有復雜性。
此外,我不想在module_A處於活動狀態時從構建中排除module_B。 我希望兩個模塊始終編譯。 如果有人對接口和module_A進行了更改,但無法更新module_B,我希望他們得到編譯器錯誤。 隨着界面的發展,這將使module_B不會陷入某種孤立和破碎的狀態,我們的注意力集中在module_A上。
我查看了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
。
無需依賴鏈接器特定支持或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
然后你有一個包含(fragment)的文件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標頭集。
我最終使用類似於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__)
進行關閉。
但這將是我使用的,除非我找到一種方法來實現這一點對我更好。 如果發生這種情況,我會發布/接受其他答案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.