Apply build flags to specific directories/files

Hi I have been trying to configure my IDE to use very restrictive build flags inside my src/ directory, as I want to use “-Wall -Wpedantic -Werror -Wundef -Wdouble-promotion -Wshadow -fno-common -fstack-usage -ffunction-sections -fdata-sections --gc-sections -Os -g3” as build flags for those files.
Using -Wall -Wpedantic -Werror on all framework files and libraries is a bloodbath (thousands of warnings, thus errors, show up and it’s impossible to debug the code I actually wrote). I tried achieving this behavior by using an extra_script, but I couldn’t make it work out:

Import("env")

def apply_build_flags(env, node):
    print("########################## Applying special flags ###########################")
    print(node.name)
    print(node.path)
    if ".pio/build/arduino_nano_esp32/src" in node.path:
        env.Replace(
            CFLAGS=[],
            CXXFLAGS=[],
            CCFLAGS=[
                "-Wall",
                "-Wpedantic",
                "-Werror",
                "-Os",
                "-g3"
               # And all the others...
            ]
        )
        return env.Object(node)
    return node

env.AddBuildMiddleware(apply_build_flags)

And then adding

extra_scripts = pre:my_script.py

to my platformio.ini file.
But I got an error which basically said that I was trying to apply two sets of build flags to the same target (in my case one simple main.cpp file in the src/ directory).
ChatGPT and Copilot both provided non working or very confusing solutions.

I have tried this solution both on esp-idf and arduino frameworks (target board is an arduino_nano_esp32 and platform is espressif32).

Do you have any suggestions? Is what I’m trying to do actually possible?

…but that’s what build_src_flags are for. They only get applied to the sources in src/, not to the framework package ones. build_flags would apply to everything.

https://docs.platformio.org/en/latest/projectconf/sections/env/options/build/build_src_flags.html

1 Like

But it looks to me that the default flags are still being applied?
With the flags I wrote an unused variable (-Wunused-variable) should cause the compilation to fail, but it actually does not, so I’m guessing that the default flags override the build_src_flags option.

My main.c:

#include <stdio.h>
void app_main(void) {
    int a; // Warning is here, and compialtion should fail at this point
    int b = 0;
    printf("%d\n",b);
}

My platformio.ini:

[env:arduino_nano_esp32]
platform = espressif32
board = arduino_nano_esp32
framework = espidf
build_src_flags = -Wall -Wpedantic -Werror

(Partial) compilation output:

Building in release mode
Compiling .pio/build/arduino_nano_esp32/src/main.o
Generating LD script .pio/build/arduino_nano_esp32/memory.ld
src/main.c: In function 'app_main':
src/main.c:3:9: warning: unused variable 'a' [-Wunused-variable]
    3 |     int a;
      |         ^

No? Adding a -W<warning> variable just makes it emit that warning. Which it does. If you want all warnings to cause a failure in compilation, you want to add -pedantic on top of that.

Sorry, what I did not use the -Wunused-variable flag, I only used the ones I listed, as you can see in the platformio.ini I sent.
The problem is that even if the warning is detected, that wouldn’t trigger an error at compile-time (even if the -Werror flag has been specified).
With -Wall -Wpedantic -Werror the compile should trigger an error when it reaches the unused variable, but it just generates a warning, the compilation is still successful.
What I am thinking here is that the list of flags specified in the build_src_flag option gets overridden by some other flags (for instance the default build flags set by platformio), which prevent causing an error when the unused-variable warning is detected.
I am looking for a way to override this behavior and only use the flags I specified.
The steps I did are:

  1. Create a new project from the platformio home (I am using the module on CLion if that helps).
    1.1 board: Arduino Nano Esp32.
    1.2 framework: espidf.
  2. Add the showed code to the src/main.c file.
  3. Add the build_src_flags = -Wall -Wpedantic -Werror option to the platformio.ini file.
  4. Build the project.

That should indeed be correct. Maybe ESP-IDF is a special case here. Can you check with a verbose build what the compiler invocation is for main.c? PlatformIO terminalpio run -v -j1 as needed.

I’ve got a lot of hardly readable lines here, should I use pio run -v -j1 | grep main.o?

Heres what the main.c compilation line looks like:

xtensa-esp32s3-elf-gcc -o .pio/build/arduino_nano_esp32/firmware.elf -T memory.ld -T esp32s3.peripherals.ld -T esp32s3.rom.ld -T esp32s3.rom.api.ld -T esp32s3.rom.libgcc.ld -T esp32s3.rom.newlib.ld -T esp32s3.rom.version.ld -T sections.ld -u nvs_sec_provider_include_impl -u esp_app_desc -u pthread_include_pthread_impl -u pthread_include_pthread_cond_var_impl -u pthread_include_pthread_local_storage_impl -u pthread_include_pthread_rwlock_impl -u pthread_include_pthread_semaphore_impl -u ld_include_highint_hdl -u start_app -u start_app_other_cores -u __ubsan_include -u __assert_func -u app_main -u newlib_include_heap_impl -u newlib_include_syscalls_impl -u newlib_include_pthread_impl -u newlib_include_assert_impl -u __cxa_guard_dummy -u __cxx_fatal_exception -u include_esp_phy_override -u vfs_include_syscalls_impl -Wl,--Map=/home/aleemont/uni/art-test-idf/.pio/build/arduino_nano_esp32/art-test-idf.map -Wl,--cref -Wl,--defsym=IDF_TARGET_ESP32S3=0 -Wl,--gc-sections -Wl,--no-warn-rwx-segments -Wl,--undefined=FreeRTOS_openocd_params -Wl,--warn-common -Wl,--wrap=_Unwind_Backtrace -Wl,--wrap=_Unwind_DeleteException -Wl,--wrap=_Unwind_FindEnclosingFunction -Wl,--wrap=_Unwind_Find_FDE -Wl,--wrap=_Unwind_ForcedUnwind -Wl,--wrap=_Unwind_GetCFA -Wl,--wrap=_Unwind_GetDataRelBase -Wl,--wrap=_Unwind_GetGR -Wl,--wrap=_Unwind_GetIP -Wl,--wrap=_Unwind_GetIPInfo -Wl,--wrap=_Unwind_GetLanguageSpecificData -Wl,--wrap=_Unwind_GetRegionStart -Wl,--wrap=_Unwind_GetTextRelBase -Wl,--wrap=_Unwind_RaiseException -Wl,--wrap=_Unwind_Resume -Wl,--wrap=_Unwind_Resume_or_Rethrow -Wl,--wrap=_Unwind_SetEnableExceptionFdeSorting -Wl,--wrap=_Unwind_SetGR -Wl,--wrap=_Unwind_SetIP -Wl,--wrap=__cxa_call_unexpected -Wl,--wrap=__deregister_frame_info -Wl,--wrap=__deregister_frame_info_bases -Wl,--wrap=__gxx_personality_v0 -Wl,--wrap=__register_frame -Wl,--wrap=__register_frame_info -Wl,--wrap=__register_frame_info_bases -Wl,--wrap=__register_frame_info_table -Wl,--wrap=__register_frame_info_table_bases -Wl,--wrap=__register_frame_table -Wl,--wrap=longjmp -fno-lto -fno-rtti -mlongcalls .pio/build/arduino_nano_esp32/src/main.o -L.pio/build/arduino_nano_esp32 -L/home/aleemont/.platformio/packages/framework-espidf/components/soc/esp32s3/ld -L/home/aleemont/.platformio/packages/framework-espidf/components/esp_rom/esp32s3/ld -L.pio/build/arduino_nano_esp32/esp-idf/esp_system/ld -L/home/aleemont/.platformio/packages/framework-espidf/components/esp_phy/lib/esp32s3 -L/home/aleemont/.platformio/packages/framework-espidf/components/esp_wifi/lib/esp32s3 -L/home/aleemont/.platformio/packages/framework-espidf/components/xtensa/esp32s3 -Wl,--start-group .pio/build/arduino_nano_esp32/esp-idf/xtensa/libxtensa.a .pio/build/arduino_nano_esp32/esp-idf/app_trace/libapp_trace.a .pio/build/arduino_nano_esp32/esp-idf/unity/libunity.a .pio/build/arduino_nano_esp32/esp-idf/cmock/libcmock.a .pio/build/arduino_nano_esp32/esp-idf/console/libconsole.a .pio/build/arduino_nano_esp32/esp-idf/esp_hid/libesp_hid.a .pio/build/arduino_nano_esp32/esp-idf/esp_lcd/libesp_lcd.a .pio/build/arduino_nano_esp32/esp-idf/protobuf-c/libprotobuf-c.a .pio/build/arduino_nano_esp32/esp-idf/protocomm/libprotocomm.a .pio/build/arduino_nano_esp32/esp-idf/esp_local_ctrl/libesp_local_ctrl.a .pio/build/arduino_nano_esp32/esp-idf/espcoredump/libespcoredump.a .pio/build/arduino_nano_esp32/esp-idf/wear_levelling/libwear_levelling.a .pio/build/arduino_nano_esp32/esp-idf/sdmmc/libsdmmc.a .pio/build/arduino_nano_esp32/esp-idf/fatfs/libfatfs.a .pio/build/arduino_nano_esp32/esp-idf/json/libjson.a .pio/build/arduino_nano_esp32/esp-idf/mqtt/libmqtt.a .pio/build/arduino_nano_esp32/esp-idf/nvs_sec_provider/libnvs_sec_provider.a .pio/build/arduino_nano_esp32/esp-idf/perfmon/libperfmon.a .pio/build/arduino_nano_esp32/esp-idf/spiffs/libspiffs.a .pio/build/arduino_nano_esp32/esp-idf/touch_element/libtouch_element.a .pio/build/arduino_nano_esp32/esp-idf/usb/libusb.a .pio/build/arduino_nano_esp32/esp-idf/wifi_provisioning/libwifi_provisioning.a .pio/build/arduino_nano_esp32/esp-idf/app_update/libapp_update.a .pio/build/arduino_nano_esp32/esp-idf/bootloader_support/libbootloader_support.a .pio/build/arduino_nano_esp32/esp-idf/cxx/libcxx.a .pio/build/arduino_nano_esp32/esp-idf/driver/libdriver.a .pio/build/arduino_nano_esp32/esp-idf/efuse/libefuse.a .pio/build/arduino_nano_esp32/esp-idf/esp-tls/libesp-tls.a .pio/build/arduino_nano_esp32/esp-idf/esp_adc/libesp_adc.a .pio/build/arduino_nano_esp32/esp-idf/esp_app_format/libesp_app_format.a .pio/build/arduino_nano_esp32/esp-idf/esp_bootloader_format/libesp_bootloader_format.a .pio/build/arduino_nano_esp32/esp-idf/esp_coex/libesp_coex.a .pio/build/arduino_nano_esp32/esp-idf/esp_common/libesp_common.a .pio/build/arduino_nano_esp32/esp-idf/esp_eth/libesp_eth.a .pio/build/arduino_nano_esp32/esp-idf/esp_event/libesp_event.a .pio/build/arduino_nano_esp32/esp-idf/esp_gdbstub/libesp_gdbstub.a .pio/build/arduino_nano_esp32/esp-idf/esp_http_client/libesp_http_client.a .pio/build/arduino_nano_esp32/esp-idf/esp_http_server/libesp_http_server.a .pio/build/arduino_nano_esp32/esp-idf/esp_https_ota/libesp_https_ota.a .pio/build/arduino_nano_esp32/esp-idf/esp_hw_support/libesp_hw_support.a .pio/build/arduino_nano_esp32/esp-idf/esp_mm/libesp_mm.a .pio/build/arduino_nano_esp32/esp-idf/esp_netif/libesp_netif.a .pio/build/arduino_nano_esp32/esp-idf/esp_partition/libesp_partition.a .pio/build/arduino_nano_esp32/esp-idf/esp_phy/libesp_phy.a .pio/build/arduino_nano_esp32/esp-idf/esp_pm/libesp_pm.a .pio/build/arduino_nano_esp32/esp-idf/esp_ringbuf/libesp_ringbuf.a .pio/build/arduino_nano_esp32/esp-idf/esp_rom/libesp_rom.a .pio/build/arduino_nano_esp32/esp-idf/esp_system/libesp_system.a .pio/build/arduino_nano_esp32/esp-idf/esp_timer/libesp_timer.a .pio/build/arduino_nano_esp32/esp-idf/esp_wifi/libesp_wifi.a .pio/build/arduino_nano_esp32/esp-idf/freertos/libfreertos.a .pio/build/arduino_nano_esp32/esp-idf/hal/libhal.a .pio/build/arduino_nano_esp32/esp-idf/heap/libheap.a .pio/build/arduino_nano_esp32/esp-idf/http_parser/libhttp_parser.a .pio/build/arduino_nano_esp32/esp-idf/log/liblog.a .pio/build/arduino_nano_esp32/esp-idf/lwip/liblwip.a .pio/build/arduino_nano_esp32/esp-idf/mbedtls/libmbedtls.a .pio/build/arduino_nano_esp32/esp-idf/newlib/libnewlib.a .pio/build/arduino_nano_esp32/esp-idf/nvs_flash/libnvs_flash.a .pio/build/arduino_nano_esp32/esp-idf/pthread/libpthread.a .pio/build/arduino_nano_esp32/esp-idf/soc/libsoc.a .pio/build/arduino_nano_esp32/esp-idf/spi_flash/libspi_flash.a .pio/build/arduino_nano_esp32/esp-idf/tcp_transport/libtcp_transport.a .pio/build/arduino_nano_esp32/esp-idf/vfs/libvfs.a .pio/build/arduino_nano_esp32/esp-idf/wpa_supplicant/libwpa_supplicant.a .pio/build/arduino_nano_esp32/esp-idf/mbedtls/mbedtls/3rdparty/everest/libeverest.a .pio/build/arduino_nano_esp32/esp-idf/mbedtls/mbedtls/library/libmbedcrypto.a .pio/build/arduino_nano_esp32/esp-idf/mbedtls/mbedtls/library/libmbedtls.a .pio/build/arduino_nano_esp32/esp-idf/mbedtls/mbedtls/library/libmbedx509.a .pio/build/arduino_nano_esp32/esp-idf/mbedtls/mbedtls/3rdparty/p256-m/libp256m.a -lcore -lespnow -lmesh -lnet80211 -lpp -lsmartconfig -lwapi -lxt_hal -lc -lm -lstdc++ -lgcc -lphy -lbtbb -Wl,--end-group

I tried adding

build_unflags = -Wno-error=unused-variable -Wno-error=unused-but-set-variable -Wno-error=unused-function -Wno-error=unused-parameter 
build_src_flags = -Wextra -Wpedantic -Os 

to the platformio.ini file and it seems to fix the issue.
I will update if I notice anything weird.

Here’s my current platformio.ini file:

[env:arduino_nano_esp32]
platform = espressif32
board = arduino_nano_esp32
framework = espidf
build_unflags = -Wno-error=unused-variable -Wno-error=unused-but-set-variable -Wno-error=unused-function -Wno-error=unused-parameter -Wno-error=shadow -O2
build_src_flags = -Wall
                  -Wpedantic
                  -Wextra
                  -Wundef
                  -Wshadow
                  -Wpadded
                  -Wformat=2
                  -Wformat-truncation=2
                  -Werror=double-promotion
                  -Werror=shadow
                  -Werror=conversion
                  -fno-common
                  -fstack-usage
                  -ffunction-sections
                  -fdata-sections
                  -Os
                  -g3

I enabled very restrictive warnings as I’ll be using this project for some people to learn how to code an ESP32 so I want to be sure to enforce good code quality and readability.
If you have any suggesions on warnings I may want to enable (and treat as errors) it’d be great.

1 Like

P.S: This WON’T work on the arduino framework, as you need to #include <Arduino.h> in your main file and it contains a few warnings it would make debugging extremely confuse.
If anybody knows whether it is possible to avoid applying the flags to header files it’d be a pleasure if they shared how.

To surpress warnings from the framework’s header files, you can convert them from -I <path> to -isystem <path>, then GCC will treat them as holy system includes that do not produce warnings. A conversion script to do that dynamically was already written in Silence warnings for dependencies / external libraries? - #8 by maxgerhardt.

1 Like

Thanks that was very helpful even though I still have a single warning on a clean main file:

src/main.ino.cpp:1:3: warning: style of line directive is a GCC extension
 # 1 "/tmp/tmpb_3yqk78"
   ^

But I think I can’t exclude this warning directly as it is about a line generated by the preprocessor (I think), directly in my main file:

#include <Arduino.h>
# 1 "/home/aleemont/uni/art-sw/src/main.ino"                // <---- warning is here
void setup()
{

}

void loop()
{

}

and this warnings seems to not have a specific name hence it looks like I cannot disable it with -Wno-<warning>.

EDIT: It looks like removing -Wpedantic will solve this too.
Thank you for your time.

You shouldn’t be using .ino files with PlatformIO because Intellisense on them is really bad. Better use regular .cpp per FAQ. With that side effect from the .ino ->.cpp conversion, it should work out better.

1 Like

The script prevents the build to succeed on Windows.
It works on Linux but on Windows I get this error:

xtensa-esp32s3-elf-g++: error: CreateProcess: No such file or directory
Compiling .pio\build\arduino_nano_esp32\FrameworkArduino\HardwareSerial.cpp.o
* [.pio\build\arduino_nano_esp32\src\main.ino.cpp.o] Error 1

If I remove the extra_scripts option the build succeeds on Windows.
Do you possibly know why and how I might solve the problem?