0
votes

I am trying to make a modular project in C. I am writing a firmware for an embedded ARM CPU. It is composed of different parts, which cannot all be compiled at the same time due to memory limitation.

Simplifying, let's say I have a module A which manages all the optional parts of the firmware, each coded in its module (B, C, D, etc...)

in module A I use #ifdef clauses:

#ifdef USE_B
  #include "B.h"
#endif

#ifdef USE_C
  #include "C.h"
#endif

...

And then on top of that I simply #define the keyword for the module I want to include.

I have some global variables in file Z.c that should stay there. Since my aim is minimizing memory use, I enclose their declaration in #ifdef as well

#ifdef USE_B
  uint8_t usedonlybyb;
#endif

#ifdef USE_C
  uint8_t usedonlybyc;
#endif

...

What happens is that even if file B and file C are completely disconnected from other part of the project at compile time, at the end of the compilation I get "Undefined symbol" errors for the extern variables "usedonlybyx", which are not reachable by the disabled modules.

I'd like to keep the usedonlybyx variables declared in module Z, because that one is the general settings module.

Is there an elegant way to suppress those errors, and still be able to succeed in my scope?

Should I simply enclose the whole modules B, C, ... within #ifdef / #endif ?

2
If you compile with USE_C defined, won't the binary file overwrite those that were compiled before, with USE_B defined? Could this be the reason why symbols are not found? - peter_the_oak
you mention there is not enough room to compile all the modules at the same time. Then suggest break down some of the files into sub files. Use a make file that is not multithreaded. compile the files one by one. then have final link step to link them all together. - user3629249
I think my statement was not clear, I have no problem compiling the whole project, but I cannot run the "complete" project on the target CPU. I basically need to tailor the features I want to compile, according to CPU model and customer requests. But I want to keep a unique project because it's more practical. - Vitomakes

2 Answers

2
votes

Easier solution is to enclose the whole implementation and declaration inside b.h and b.c files inside #ifdef USE_B and #endif. Same for other modules. That way all the conditional compilation for one module is isolated and does not have to be throughout other code where #include "b.h" is used.

// b.h
#ifdef USE_B
// declarations and include guards
#endif

// b.c
#ifdef USE_B
// definitions
#endif

// a.c
#include "b.h" // <- no USE_B needed here
0
votes

You could try splitting the B and C modules into discrete library components, making sure that any globals used by those libraries are contained in them. This may require moving the extern declarations of the variables to teh library interface files, or providing accessor methods, as necessary.

Alternatively, if you're using GCC, the compiler has a language extension in the form of __attribute__((weak)), which will declare a 'just-in-case-it's-not-defined-anywhere-else' version of the variable that will keep the linker happy.