Using preprocessor directives defined in platformio.ini

I’m trying to use defines placed in platformio.ini in code so that each “env” can have conditional code. However, it’s not working they way it seems it should…

In platformio.ini:

[common_env_data]
build_flags =
    -D VERSION=\"1.2.3\"
    -D BUILD_ENV_NAME=$PIOENV
    -D BUILD_PLATFORM=$PIOPLATFORM
    -D BUILD_TIME=$UNIX_TIME

[env:adafruit_feather_nrf52832]
platform = nordicnrf52
board = adafruit_feather_nrf52832
framework = arduino
build_flags =
    ${common_env_data.build_flags}
lib_deps = 
    ${common_env_data.lib_deps}
monitor_speed = 115200

[env:adafruit_feather_m0]
platform = atmelsam
board = adafruit_feather_m0
framework = arduino
build_flags =
    ${common_env_data.build_flags}
lib_deps = 
    ${common_env_data.lib_deps}
monitor_speed = 115200

[env:featheresp32]
platform = espressif32
board = featheresp32
framework = arduino
build_flags =
    ${common_env_data.build_flags}
    -D ESP32
lib_deps = 
    ${common_env_data.lib_deps}
monitor_speed = 115200

in cpp code:

#if BUILD_ENV_NAME==adafruit_feather_nrf52832
#include <bluefruit.h>
#elif BUILD_ENV_NAME==adafruit_feather_m0
#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BluefruitLE_UART.h"
#elif BUILD_ENV_NAME==featheresp32
#include "BluetoothSerial.h"
#endif

What is happening is that the first section is always being included regardless of which env is choses in the IDE, and all others are not. For example if I choose env:featheresp32 in the IDE and then compile I get an error cannot open bluefruit.h. This file should even be attempted loading since this is not the env selected. If I hover the mouse over the BUILD_ENV_NAME the IDE correctly shows that it evaluates to featheresp32. Yet for some reason, the section the wrong part is being included.

I need to be able to use an #if directive with a condition of the specific env being compiled.

1 Like

Yeah but probably not in quotes, so it’s not a string. Try quoting the value the same way you did with VERSION. BUILD_PLATFORM needs the same treatment.

Oh, actually you can’t use strings in a preprocessor #if(source).

You should use implicitly defined macros like

  • ARDUINO_ARCH_ESP32 (here)
  • ARDUINO_ARCH_NRF52 (here)
  • ARDUINO_ARCH_SAMD (here)

to differentiate the build environment types.

You can still stringify the macros and use them in print statements or whatever.

Adafruit is just an example and not specific to the issue, so I can’t rely on adafruit’s *.py lib code for cppdefines when not using adafruit boards. Nor, do I think it should be necessary to track down these cppdefines with whatever board I happen to be using. I’m relying on build_flags in the platformio.ini file as documented in Redirecting..., c.f. “build_flags = -D BUILD_ENV_NAME=$PIOENV”.

So do I understand correctly that this specifically documented use of $PIOENV in platformio.ini has no actual use case with #if directives within cpp code? If so, what could the possible uses of “build_flags = -D BUILD_ENV_NAME=$PIOENV” be?

After some experimenting I see that the platformio.ini file build_flags = -D BUILD_ENV_NAME=$PIOENV is when the env is set to “adafruit_feather_nrf52832” is equivalent to cpp #define BUILD_ENV_NAME adafruit_feather_nrf52832 followed by #define adafruit_feather_nrf52832 1. So using that build_flag will cause the value of $PIOENV to be it’s own #define! This is counterintuitive and I wonder if this could cause strange compilation errors if I use an environment name that happens to step on some #define in some library somewhere.

Anyway I can just use #ifdef adafruit_feather_nrf52832 which means that it’s much simpler to change the platformio.ini define to build_flags = -D $PIOENV in this case and not use BUILD_ENV_NAME at all.

However, if I leave build_flags = -D BUILD_ENV_NAME=$PIOENV as is I have to also add in cpp some explicit redefines of the various envs like “#define adafruit_feather_nrf52832 100”, adafruit_feather_m0 101" etc. which will allow me to use #if BUILD_ENV_NAME==adafruit_feather_nrf52832, and follow with an #else and #error "BUILD ENV NOT DEFINED, if I forget to add one. I cannot start the redefines from 1 (as in #define adafruit_feather_nrf52832 1) since the selected environment is already set to 1 and all other environments will compare to 0 which will cause the various #if directives to fail.

So I can get it to work as expected, but it’s not as elegant as I had hoped.

#ifndef BUILD_ENV_NAME
    #error "Add -D BUILD_ENV_NAME=$PIOENV to platformio.ini build_flags"
#else
    #define adafruit_feather_nrf52832 101
    #define adafruit_feather_nrf52840 102
    #define adafruit_feather_32u4 103
    #define adafruit_feather_m0 104
    #define featheresp32 105
#endif

#if BUILD_ENV_NAME==adafruit_feather_nrf52832
#include <bluefruit.h>
#elif BUILD_ENV_NAME==adafruit_feather_nrf52840
#include <bluefruit.h>
#elif BUILD_ENV_NAME==adafruit_feather_m0
#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BluefruitLE_UART.h"
#elif BUILD_ENV_NAME==featheresp32
#include "BluetoothSerial.h"
#else
#error "BUILD_ENV_NAME NOT RECOGNIZED"
#endif

I will probably rename all my environments to prepend “env_” to the beginning of them just in case some other library happens to use a #if that happens to be the same name as my envonment.

1 Like