簡體   English   中英

IAR EWARM中的條件鏈接

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM