Условное связывание в IAR EWARM - PullRequest
0 голосов
/ 04 марта 2019

Я использую 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 обрабатывались так, как если бы они были __weak, когда Module_A активени наоборот.

Я бы хотел не включать в свой код #pragma weak, если это возможно.Мне нужно иметь возможность скомпилировать этот код с помощью нескольких различных цепочек инструментов.Поэтому мне нужно было обернуть любую такую ​​детскую коляску чем-то вроде #ifdef __ICCARM__.Кроме того, мне нужно определить какой-то дополнительный символ препроцессора, чтобы условно сделать один модуль слабым, когда другой активен.Это все сложности, которые я бы предпочел не допускать в код.

Более того, я не хочу исключать module_B из сборки, когда module_A активен.Я хочу, чтобы оба модуля всегда компилировались.Если кто-то вносит изменения в интерфейс и в module_A, но не может обновить module_B, я бы хотел, чтобы он получил ошибку компилятора.Это будет препятствовать тому, чтобы module_B входил в какое-то потерянное и поврежденное состояние по мере развития интерфейса, и наше внимание сосредоточено на module_A.

Я просмотрел EWARM_DevelopmentGuide.ENU.pdf и не могу найти параметр командной строки, который кажетсяделать то, что я хочу.Я хотел бы знать, если такой вариант существует, и я пропустил его, или есть ли другой способ выполнить то, что я добиваюсь.

Ответы [ 3 ]

0 голосов
/ 05 марта 2019

Нет необходимости полагаться на поддержку компоновщика или управление компоновкой 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 всегда будет компилироваться, но только выбранный API будет использоваться с использованием макроса doSomething, а не имени функции для конкретной реализации.

Я не знаю, насколько умным является 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

Youможет даже использовать текст NAMESPACE_ в заголовке фиктивного элемента для генерации списка имен функций для <input>, так что весь набор заголовков API может быть сгенерирован из одного фиктивного заголовка.

0 голосов
/ 18 марта 2019

Я использовал слабое связывание, аналогичное тому, что предлагал пользователь 694733.Но мой метод был немного другим.

Я добавил такой блок в верхней части обоих модулей 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__).

*.1019 * Но это будет то, что я использую, если я не найду способ сделать это, который работает лучше для меня.Если это произойдет, я отправлю / приму другой ответ.
0 голосов
/ 05 марта 2019

На самом деле это не полный ответ, так как у меня не такая новая версия компилятора, как у вас, но больше возможного обходного пути.

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, как необходимо в вашей конфигурации.

...