"warning: <symbol> redefined", probably because of -fpreprocessed

Hi,

It seems to me that during the ‘.ino’ conversion phase the ‘#include’ directives aren’t processed (because of ‘-fpreprocessed’), and if a ‘#if’ conditional block relies on a symbol defined in an include, then it’ll be evaluated incorrectly.
In fact, all ‘#iflines are just skipped, and the content of their conditional blocks evaluated anyway, causing a lot of ‘warning: redefined’ warnings.

The compilation itself handles them well (otherwise variable definitions would clash), but those warnings still trigger false alarms.

To reproduce, we need 2 files, one include to define a symbol in, and a main file to use that symbol in a #if

config.h:

#define PRODUCT_VARIANT     42
//#define PRODUCT_VARIANT     43

main.ino:

#include "config.h"

#if PRODUCT_VARIANT == 42
#define GPIO_PWM		4
int something[256];
#endif // PRODUCT_VARIANT == 42

#if PRODUCT_VARIANT == 43
#define GPIO_PWM		12
char something[256];
#endif // PRODUCT_VARIANT == 43

void setup() {
}

void loop() {
    while (true) {
    }
}

And this is what it generates (pio run -v):

...
xtensa-lx106-elf-g++ -o "/tmp/test/src/main.ino.cpp" -x c++ -fpreprocessed -dD -E "/tmp/tmpTMwbaM"
/tmp/test/src/main.ino:9:0: warning: "GPIO_PWM" redefined [enabled by default]
# define GPIO_PWM  12
^
/tmp/test/src/main.ino:4:0: note: this is the location of the previous definition
# define GPIO_PWM  4
^
...

I’like to keep my builds warning-free, so any suggestions are welcome.

I can’t reproduce this issue. But I did two things a little different. In PIO your main-file shouldn’t be an *.ino but a “normal” C++ -Sourcefile (*.cpp). That could cause the issue.
Also you should always include #include <Arduino.h> in PIO. Otherwise you will get errors later as soon as you try to use Arduino-fucntions like digitalRead().

Hi Thomseeen, thanks for the quick reply!

Yes, doing this in a .cpp file compiles fine, because the warnings come from the .ino-specific processing step: “Converting main.ino…”

As of Arduino.h, in the real code it’s certainly included, I just wanted to strip this example to the minimum that still triggers the warnings.

But you are right, it’s a crucial point that those #include and #ifs shall occur in a .ino file, because in a .cpp they work fine.

This is the exact code I’ve used:

In its folder I issued pio run (or optionally pio run -v) and that triggered the warnings.

(This code doesn’t make sense, it wouldn’t do anything at all, it’s just the bare minimum that still triggers them.)

…so why even use the *.ino file extension if you are not working in the Arduino IDE?

If you just want to investigate the bug why the “Converting…” step ignores compiler directives then you should probably post an issue on github. I’m somewhat confused on what exactly is happening. The *.ino gets convered by some python script from pio (?!) and then the compiler is called for the generated *.cpp file but with different options than if it would have been a *.cpp file from the getgo? Going that route I would even question wether compiler directives are part of the “Arduino-language”. If they are not (I don’t know) it wouldn’t even be a bug if they throw some sort of warnings and errors in the conversion step.

I’m somewhat confused on what exactly is happening.

OK, then the background: There is an open-source project (zapped) for which I’d like to contribute something (support for a new controller chip), and meanwhile I got these warnings.
I don’t control the codebase, but it already contains #define / #ifs, and AFAIK it works, but now I can’t see how :).

The *.ino gets convered by some python script from pio (?!)

You’re right, meanwhile I found the source

It merges all .ino files together, the first one being the one that contains setup() or loop(), then it does some Arduino-specific template expansion, then sends it through a c++ preprocessor (*) to remove the comments, and then sends the result to the compiler.

The issue is with the way the c++ preprocessor is applied: the directive -fpreprocessed switches off the processing of #include-s and #if-s, so the conditional blocks are processed unconditionally.

-fpreprocessed
Indicate to the preprocessor that the input file has already been preprocessed. This suppresses things like macro expansion, trigraph conversion, escaped newline splicing, and processing of most directives. The preprocessor still recognizes and removes comments, so that you can pass a file preprocessed with -C to the compiler without problems. In this mode the integrated preprocessor is little more than a tokenizer for the front ends.

I would even question wether compiler directives are part of the “Arduino-language”

#define and #include are, but #if indeed isn’t
mentioned officially, so I’m not sure either.

Anyway, I found no painless way to tell the preprocessor to leave also the #define-s alone (as it doesn’t expand macros, it makes no sense to parse them), so I think there is no easy cure for this.

At least not that easy that eliminating some false-alarm warnings would be worth it.

So I consider this case solved, the issue is harmless and it occurs only in a specific scenario, and it’s caused by a peculiarity of the g++ preprocessor.

Thanks for the assistance in this investigation, and have a nice day :slight_smile: !

1 Like

Some eager script keeps flagging my posts as spam.
I don’t know why, but never mind, the issue is only about a false alarm anyway, so it’s not that important.