Arduino-ESP32. Is it possible to compile translation units in C99 or C89 standart?

Hello!

I used to write C code with -ansi, -pedantic flags. Is it possible to build translation modules (mod.c, mod.h) with these flags with arduino-cli for esp32?

the ESP32 Arduino core relies on GCC with some extensions and expects C++11 / C++17 or later for most code.

I think the -ansi and -pedantic flags are strict C flags and thus would likely break Arduino/ESP32 code, especially anything using Arduino headers or C++ features.

➜ However you could probably compile individual .c translation modules in strict C mode separately with -ansi -pedantic and then link them into the Arduino build.

(Mixing strict C modules with the ESP32 Arduino C++ core might likely require extern "C" wrappers for headers and careful handling of types).

Hi @evily. Arduino CLI allows the user to set the values of platform properties via the --build-property flag of the arduino-cli compile command:

https://arduino.github.io/arduino-cli/latest/commands/arduino-cli_compile/#options

The esp32:esp32 boards platform follows the standard convention of allowing the user to inject arbitrary arguments into the compilation commands via a set of dedicated platform properties:

The compiler.c.extra_flags property is referenced in the esp32:esp32 platform's recipe.c.o.pattern property, which defines the template from which Arduino CLI generates the compilation command used for C language files:

This means you can add the -ansi and -pedantic flags to the commands used to compile all C files by adding the following flag to your arduino-cli compile command:

--build-property "compiler.c.extra_flags=-ansi -pedantic"

By default, the esp32:esp32 platform adds the -w flag to the compilation commands, which disables warnings, which would render your -pedantic flag useless. You can enable warnings via the --warnings flag of the arduino-cli compile comand. So you will want to also add something like --warnings all to your arduino-cli compile command:

arduino-cli compile --build-property "compiler.c.extra_flags=-ansi -pedantic" --warnings all ...

Alternatively, you can do more granular diagnostics configuration from your source code by using diagnostic pragmas:

You do need to use the -Wpedantic flag instead of its -pedantic synonym.

For example, if you want to enable the "pedantic" set of warnings for a translation unit, you would add the following line to the top of the .c file:

#pragma GCC diagnostic warning "-Wpedantic"

Or if you want to enforce warning-free code by promoting the warnings to errors:

#pragma GCC diagnostic error "-Wpedantic"

I discovered a problem with this. Unfortunately there is a bug in the esp32:esp32 platform;

The platform adds its own flags after the point where the flags the user specifies via the compiler.c.extra_flags are injected. This is a problem because, in cases where two flags conflict, the flag that comes later overrides the prior occurrences of the flag.

This doesn't affect the -pedantic flag (other than the easily worked around problem I mentioned in my previous reply of the -w flag counteracting it). However, it does completely nullify the injected -ansi flag, since the platform's flags include -std=gnu17, which overrides -ansi.

It is possible to work around this, by using the --build-property of arduino-cli compile to also redefine the platform's recipe.c.o.pattern property:

--build-property "recipe.c.o.pattern=\"{compiler.path}{compiler.c.cmd}\" {compiler.c.flags} {compiler.c.extra_flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD=\"{build.board}\" -DARDUINO_VARIANT=\"{build.variant}\" -DARDUINO_PARTITION_{build.partitions} {build.extra_flags} {compiler.cpreprocessor.flags} {includes} \"@{build.opt.path}\" \"@{file_opts.path}\" \"{source_file}\" -o \"{object_file}\""

Note that I swapped the order of the {compiler.c.flags} and {compiler.c.extra_flags} property references in my definition of the recipe.c.o.pattern property. This is the equivalent of fixing the bug in the platform's platform.txt configuration file:

--- a/platform.txt
+++ b/platform.txt
@@ -146,7 +146,7 @@ recipe.hooks.prebuild.8.pattern=/usr/bin/env bash -c "cp -f "{compiler.sdk.path}
 recipe.hooks.prebuild.8.pattern.windows=cmd /c COPY /y "{compiler.sdk.path}\sdkconfig" "{build.path}\sdkconfig"
 
 ## Compile c files
-recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.extra_flags} {compiler.c.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_VARIANT="{build.variant}" -DARDUINO_PARTITION_{build.partitions} {build.extra_flags} {compiler.cpreprocessor.flags} {includes} "@{build.opt.path}" "@{file_opts.path}" "{source_file}" -o "{object_file}"
+recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} {compiler.c.extra_flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_VARIANT="{build.variant}" -DARDUINO_PARTITION_{build.partitions} {build.extra_flags} {compiler.cpreprocessor.flags} {includes} "@{build.opt.path}" "@{file_opts.path}" "{source_file}" -o "{object_file}"
 
 ## Compile c++ files
 recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.extra_flags} {compiler.cpp.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_VARIANT="{build.variant}" -DARDUINO_PARTITION_{build.partitions} {build.extra_flags} {compiler.cpreprocessor.flags} {includes} "@{build.opt.path}" "@{file_opts.path}" "{source_file}" -o "{object_file}"

Oh, great! I glad to read it! And thank you for you work! I’ll try later to do it and will let you know.

Okay, I used this patch. Seems it works.

I’ve created the file hello/hello.ino:

void setup()
{}
void loop()
{}

Compiling without -ansi -pedantic:

arduino-cli compile -b esp32:esp32:esp32 hello.ino --warnings all --quiet  --build-property build.partitions=min_spiffs

Everything OK.

Compiling with -ansi -pedantic:

arduino-cli compile -b esp32:esp32:esp32 hello.ino --warnings all --quiet  --build-property build.partitions=min_spiffs --build-property "compiler.c.extra_flags=-ansi -pedantic"

But got an error and many warnings (I added only a little part of warnings):

/home/evi/.arduino15/packages/esp32/hardware/esp32/3.2.0/cores/esp32/ColorFormat.c:23:1: error: C++ style comments are not allowed in ISO C90
   23 | // define a clamp macro to substitute the std::clamp macro which is available from C++17 onwards
      | ^
/home/evi/.arduino15/packages/esp32/hardware/esp32/3.2.0/cores/esp32/ColorFormat.c:23:1: note: (this will be reported only once per input file)
/home/evi/.arduino15/packages/esp32/hardware/esp32/3.2.0/cores/esp32/ColorFormat.c: In function 'espHsvToRgbColor':
/home/evi/.arduino15/packages/esp32/hardware/esp32/3.2.0/cores/esp32/ColorFormat.c:52:24: warning: initializer element is not computable at load time [-Wpedantic]
   52 |   espHsvColor_t hsv = {h, s, v};
      |                        ^
/home/evi/.arduino15/packages/esp32/hardware/esp32/3.2.0/cores/esp32/ColorFormat.c:52:27: warning: initializer element is not computable at load time [-Wpedantic]
   52 |   espHsvColor_t hsv = {h, s, v};
      |

Is it possible to this fix error somehow with saving -ansi -pedantic flags? Maybe somehow compile only my units with these flags.

For -pedantic, you can do that by using the alternative diagnostic pragma approach instead of the approach of injecting flags into the compilation commands:

However, I am not aware of a way to do the same with -ansi, since this is not a diagnostic flag. Maybe you will find -Wpedantic alone to be sufficient?

I've tried to compile without -pedantic:

arduino-cli compile -b esp32:esp32:esp32 hello.ino --warnings all --quiet  --build-property build.partitions=min_spiffs --clean --build-property "compiler.c.extra_flags=-ansi"

No warnings, got the same error:

/home/evi/.arduino15/packages/esp32/hardware/esp32/3.2.0/cores/esp32/ColorFormat.c:23:1: error: C++ style comments are not allowed in ISO C90
   23 | // define a clamp macro to substitute the std::clamp macro which is available from C++17 onwards
      | ^

I deleted all comment lines, but got more errors.

Were you expecting a different result? If so, please provide a detailed description of why you expected that. Understanding this will allow the forum helpers to effectively assist you.

I have an ugly solution to what I want. For example, I have 3 files: prog.ino, my_mod.c, my_mod.h.

  1. I compile without -ansi flag.
  2. Do touch my_mod.*
  3. I compile with -ansi flag.

So, I have compiled esp32 parts without standard problems. Then I can compile my units with my favorite standard.

Can I compile separate esp32 part without -ansi and mein part with -ansi and then ld then without touch hack?

Nice one! It should probably be considered a bug that Arduino CLI doesn't invalidate the compilation cache when the platform properties are changed via --build-property, but in this case it actually turned out to be useful!

I don't know how that can be accomplished. Maybe one of the other forum helpers will have an idea.

Are you sure you really need -ansi? I'm all for encouraging/enforcing best practices via -pedantic, but compiling for an ancient C standard via -ansi seems a bit excessive to me.

There are some reasons:

  1. I'm involved in some projects where even C99 is unacceptable because of VLA (I won't explain why, you can look why it's bad for yourself). So if write units for esp32 in ANSI C I can use it later.
  2. I used to it.
  3. I just like ansi standard. ;^) It has less opportunities. The fewer opportunities, the more creativity.