1
votes

I am using IAR EWARM 8.10.1 which uses the ILINK linker.

I have a common header that two compilation units use. It includes prototypes of functions with external linkage and constitutes an API. Depending on how the build is configured I would like Module A or B to be linked with rest of my application.

[ Common_Header.h ]
    |         |
    |         +----- [Module_A.c] ---> [Module_A.o]
    |
    +--------------- [Module_B.c] ---> [Module_B.o]

Somehow I would like to pass an argument to ilinkarm.exe to include Module_A.o.

Other IAR toolchains I have used in the past used the XLINK linker. XLINK had a -A option, and I suppose that is similar to what I need.

What I am essentially wanting is for the function definitions in Module_B to be treated as if they were __weak when Module_A is active and vice versa.

I would like to avoid putting #pragma weak in my code if possible. I need to be able to compile this code with a few different tool chains. So I would need to wrap any such pramgas with something like #ifdef __ICCARM__. Furthermore, I'd need to define some extra preprocessor symbol to conditionally make one module weak when the other is active. This is all complexity I'd prefer to keep out of the code.

Furthermore, I do not want to exclude module_B from the build when module_A is active. I want both modules to always compile. If someone makes changes to the interface and in module_A, but fails to update module_B, I would like them to get a compiler error. This will keep module_B from getting in some orphaned and broken state as the interface evolves and our attentions is focused on module_A.

I have reviewed EWARM_DevelopmentGuide.ENU.pdf and I can't find a command line option that seems to do what I want. I would like to know if such an option exists and I have missed it, or if there is another way to accomplish what I am after.

3
You haven't asked a question that we could answer. Presumably you tried something but it didn't work. What did you try? What was the problem? It seems that you are already aware of options of how to do this (-A option and __weak keyword). - user694733
I don't use EWARM so cannot answer, but it is certainly possible to exclude individual files or groups from a build configuration within the IDE - so you simply need separate build configurations for each build. I am not sure if it is possible to separately select compile but not link, or whether it will do that in any case but worth a try (Keil uVision allows that so perhaps EWARM too). - Clifford
@user694733 Perhaps I should clarify my question. I am asking if an option exists in the first place to do this. It seems like an option should exist, considering that xlink, another IAR product, has such an option. The __weak keyword isn't a desirable option because both definitions can't be simultaneously weak or I am back where I started. It also involves cluttering the code with pragmas that I have to hide from other compilers. - Nick
@clifford I could exclude certain files from the build and put them into different configurations. However, I already have three build configurations that represent different release configurations in terms of optimization, debug symbols, level of diagnostic output, etc. Adding this would double the number of build configurations I have to manage. Furthermore, one of my stated goals is that both modules should always build so that changes to the interface do not leave one of the modules in an orphaned state. - Nick
"one of my stated goals is that both modules should always build" That makes sense, but: "Adding this would double the number of build configurations I have to manage" How else would you select whether to use A or B, if not with build configurations? You have to have that information somewhere. - user694733

3 Answers

1
votes

This is not really a complete answer, since I don't have as new version of the compiler as yours, but more of a possible workaround.

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) { ... }
 ...

You would then define MODULE_*_SELECTED as needed in your configuration.

1
votes

There is no need to rely on linker specific support or IDE specific build management. An entirely portable solution is to define the A and B implementations with different symbol names, then use conditionally defined macros to select the required implementation.

Example:

#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 ) ;

In that way, both implementation A and B will always be compiled, but only the selected API will be used by using the macro doSomething rather than the implementation-specific function name.

I don't know how smart ILINK is but by placing the implementations in separate translation units (i.e. .c files), the linker should be able to eliminate the unused functions from the link. If not it certainly will if you place the object code in a static-link library (.lib or .a).


To solve the issue of maintaining two implementation files that are identical except for the namespace prefix, you might create a single dummy header file with the prototypes such as:

int NAMESPACE_doSomething( void ) ;

Then have a pre-build step using a tool such as sed to generate the implementation prototype headers by for example:

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

Then you have an a file api.h that contains (fragment):

#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

You could further write a code generator to generate api.h from a list of function names. That would not be too difficult in your preferred scripting language or even C. You could write such a generator to take command line arguments:

generate_api <input> <output> <namespace1> <namespace2> ... <namespaceN>

then call it:

generate_api functions.txt api.h api_a api_b

You could even use the NAMESPACE_ text in the dummy header to generate the function name list for <input> such that the entire API header set can be generated from a single dummy header.

1
votes

I ended up using weak linking similar to what user694733 suggested. But my method was a little bit different.

I added a block like this at the top of both module A and 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

This approach doesn't require me to decorate every function prototype with MY_WEAK. So the non-standard stuff is all grouped together.

I dislike several things about using __weak / #pragma weak:

The first thing I dislike is that it increases coupling between the two modules. If neither symbol is defined then both modules would have weak definitions. At that point how can you know which one will be used? Therefore, it is necessary for each module to that the other exists, or at least that there is more than one option. I could have used a single definition and just changed the value, but I opted to do it this way so the name would be descriptive.

The second thing I dislike is that I am cluttering up the code with something that is an artifact of how the project is built. I would like to pull such logic out and put it into the build system when practical.

The third is that it isn't totally portable and has to be gated off with the #if (defined __ICCARM__).

But this will be what I use unless I find a way to accomplish this that works better for me. If that occurs I'll post/accept some other answer.