arduino / arduino-cli

Arduino command line tool
https://arduino.github.io/arduino-cli/latest/
GNU General Public License v3.0
4.32k stars 373 forks source link

Generated prototype incorrectly prefixed with `extern "C"` when using `extern "C" { ... }` to mix C functions in an `.ino` file. #1618

Open dmalenic opened 2 years ago

dmalenic commented 2 years ago

Describe the problem

Compiling the following blink sketch for ESP32 board:

#ifdef __cplusplus
extern "C"
{
#endif

void dummy(void) {}

#ifdef __cplusplus
}
#endif

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
}

results in errors:

sketch_esp32_blink:12:12: error: conflicting declaration of 'void setup()' with 'C' linkage
 void setup()
            ^
In file included from /tmp/arduino_build_620310/sketch/sketch_esp32_blink.ino.cpp:1:0:
.arduino15/packages/esp32/hardware/esp32/1.0.6/cores/esp32/Arduino.h:118:6: note: previous declaration with 'C++' linkage
 void setup(void);
      ^
sketch_esp32_blink:17:11: error: conflicting declaration of 'void loop()' with 'C' linkage
 void loop()
           ^
In file included from /tmp/arduino_build_620310/sketch/sketch_esp32_blink.ino.cpp:1:0:
.arduino15/packages/esp32/hardware/esp32/1.0.6/cores/esp32/Arduino.h:119:6: note: previous declaration with 'C++' linkage
 void loop(void);
      ^
exit status 1
conflicting declaration of 'void setup()' with 'C' linkage

Arduino CLI version

Original report

Arduino IDE 1.8.19

Last verified with

60c1c98

Operating system

Operating system version

Additional context

If the extern "C" { ... } block does not define any function but only variables, or if the whole extern "C" { ... } block is moved after the definition of setup and loop functions, the code compiles successfully.

If the extern "C" { ... } block only declares functions, and functions are defined in a separate module (file), everything compiles correctly.

The files with .cpp extensions do not have this problem, and allow mixing C linkage functions defined within extern "C" { ... } blocks.

Looks like, when the compiler encounters a C linkage function definition in a sketch file (.ino extension), it assumes that the rest of file defines only C linkage functions.

The error can not be reproduced when compiling for boards using AVR or ARM processors that I could test with.

Related

Issue checklist

per1234 commented 2 years ago

Thanks @dmalenic

In case it will be useful to the developers, I'll provide a minimal demo of the bug:

$ arduino-cli version
arduino-cli.exe  Version: nightly-20220110 Commit: 60c1c98 Date: 2022-01-10T01:28:45Z

$ SKETCH_PATH="/tmp/ExternCBug"
$ mkdir "$SKETCH_PATH"
$ echo "#ifdef __cplusplus
extern \"C\" {
#endif
void dummy(void) {}
#ifdef __cplusplus
}
#endif
void setup() {}
void loop() {}
" > "$SKETCH_PATH"/ExternCBug.ino

$ arduino-cli compile --fqbn arduino:avr:uno --preprocess "$SKETCH_PATH"
#include <Arduino.h>
#line 1 "C:\\Users\\per\\AppData\\Local\\Temp\\ExternCBug\\ExternCBug.ino"
#ifdef __cplusplus
extern "C" {
#endif
#line 4 "C:\\Users\\per\\AppData\\Local\\Temp\\ExternCBug\\ExternCBug.ino"
 extern "C" void dummy(void);
#line 8 "C:\\Users\\per\\AppData\\Local\\Temp\\ExternCBug\\ExternCBug.ino"
void setup();
#line 9 "C:\\Users\\per\\AppData\\Local\\Temp\\ExternCBug\\ExternCBug.ino"
void loop();
#line 4 "C:\\Users\\per\\AppData\\Local\\Temp\\ExternCBug\\ExternCBug.ino"
void dummy(void) {}
#ifdef __cplusplus
}
#endif
void setup() {}
void loop() {}

Vaguely related to https://github.com/arduino/arduino-cli/issues/1591

matthijskooijman commented 2 years ago

I think this is another case where the automatic prototype generation that arduino-cli does cannot cope with more complicated sketches. What I suspect happens, is that a prototype is generated for the loop() and setup() functions. IIRC prototypes are inserted directly before the first other function definition or declaration found in the .ino file (so they can end up after any initial type declarations when those are before all functions). In this case, that puts the prototypes inside the extern "C" blocks, which leads to conflicting declarations.

I'm not sure, but I suspect that this is going to be hard to fix (certainly with the ctags-based approached, but even for the clang-based parsing approach this probably needs some special casing). Also, if you're working with external C functions like this, chances are you're already a more advanced that does not really need the auto-generated prototypes at all.

Here's some things you could do/try to get your code working right now:

matthijskooijman commented 2 years ago

I've updated the issue title, as this is not an ESP32-specific issue.

dmalenic commented 2 years ago

Additional observation, that might be helpful. My original report was Arduino IDE specific, and within Arduino IDE the problem is limited to ESP32 boards (don't have ESP 8266 board to test). As @per1234 showed, if arduino-cli is used the bug is not ESP32 specific.

I just tried script @per1234 provided and it breaks. But If I load the sketch his script created /tmp/ExternCBug/ExternCBug.ino to Arduino IDE, and select target Arduino UNO (as in @per1234 example), it compiles and loads to the UNO board, as well as on MEGA 2560, Nano Every, DUE, MKR Vidor 4000, MKR WiFi 1010, Raspberry Pi Pico and Teensy 4.1. I tried arduino-cli on all those platforms (except Teensy) and it breaks on all of them exactly as @per1234 noticed.

Hopefully this observation helps, at least with resolving the arduino-cli problem for AVR and ARM boards. Maybe there are two separate but related issues - one with IDE and one with CLI.