Skip to content

Don't bail out if got include unrelated error while preprocessing #285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 10, 2018

Conversation

facchinm
Copy link
Member

Preprocessing is a bit tricky, since it expects an error to resolve missing includes; to do this, it invokes gcc with -CC flag (https://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Preprocessor-Options.html#Preprocessor-Options) to expand macro AND comments. Since this flag is not used during normal compilation, the errors reported are mostly bogus.

This patch avoids writing the actual (non-include) error on stdout so the compilation can go on and fail on the proper errors (if there's any)

This patch fixes arduino/Arduino#7930

@matthijskooijman since it modifies a bit the behaviour introduced in fcc9c5d , could you give it a check before merging? Thanks

Preprocessing is a bit tricky, since it expects an error to resolve missing includes; to do this, it invokes gcc with -CC flag (https://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Preprocessor-Options.html#Preprocessor-Options) to expand macro AND comments. Since this flag is not used during normal compilation, the errors reported are mostly bogus.

This patch avoids writing the actual (non-include) error on stdout so the compilation can go on and fail on the proper errors (if there's any)
@facchinm
Copy link
Member Author

@ArduinoBot build this please

@arduino arduino deleted a comment from ArduinoBot Aug 28, 2018
@matthijskooijman
Copy link
Collaborator

I'm not so sure if this is really the right fix. Consider the following:

  • A file includes "A.h", which has these comments-in-macros that break with -CC, and then includes "B.h" from a different library.
  • builder first sees a missing "A.h" and includes library A.
  • builder then sees the comment errors, but not a missing "B.h".
  • In the current code, this bails out with an error. This might be unexpected to the user, but at least it shows what's actually the problem.
  • With your changes, this aborts include detection silently, continues to the actual compilation, which (I think?) does not complain about the comments, but will notice that "B.h" is not found (since the include detection hasn't added library B yet). This will not clearly show what the problem is, the user will be trying to figure out if library B is installed correctly, if the include directive is ok, etc. while the underlying problem is really unrelated.

One could argue that even with the comment errors, the missing "B.h" will also generate an error message which builder can pick up during include scanning, but it is entirely possible that these comment errors will somehow interfere with the "B.h" include (for example when the include is guarded by an #if that references one of the broken macros, I suspect).

Having said that: In the case of #7930, the real problem is that include detection gives errors which are not present during normal compilation. Ideally, the preprocessor run will just do the first preprocessing step of normal compilation.

Looking at the gcc manpage, shouldn't we just be passing -C rather than -CC? Or even leave out -C entirely? I can imagine we need this when generating preprocessed output for inserting generated prototypes (to keep the user's comments), but for include detection, I do not think comments should be kept (we're not even using the preprocessed output anyway).

   -C  Do not discard comments.  All comments are passed through to
       the output file, except for comments in processed directives,
       which are deleted along with the directive.

       You should be prepared for side effects when using -C; it
       causes the preprocessor to treat comments as tokens in their
       own right.  For example, comments appearing at the start of
       what would be a directive line have the effect of turning
       that line into an ordinary source line, since the first token
       on the line is no longer a #.

   -CC Do not discard comments, including during macro expansion.
       This is like -C, except that comments contained within macros
       are also passed through to the output file where the macro is
       expanded.

@ArduinoBot
Copy link
Contributor

✅ Build completed.

⬇️ Build URL: http://downloads.arduino.cc/PR/arduino-builder/arduino-builder-285.zip

ℹ️ To test this build:

  1. Replace arduino-builder binary (you can find it where you installed the IDE) with the provided one

@facchinm
Copy link
Member Author

Macro expansion need to be applied because there is plenty of code like

#ifdef __AVR__
#include "A.h"
#else
#include "B.h"
#endif

that would pick the wrong include without it. So -C or -CC are needed. -CC expands comments but also (from GCC documentation)
causes all C++-style comments inside a macro to be converted to C-style comments. This is to prevent later use of that macro from inadvertently commenting out the remainder of the source line. which I find reasonable 🙂

Apart from that, the biggest problem in the current behaviour is that it reports errors that are not going to appear during compilation. Diagnostic may be improved by adding a line like Could not detect includes from file "filename.ccp" in verbose mode, so a proper bugreport would spot them.

@matthijskooijman
Copy link
Collaborator

Macro expansion need to be applied because there is plenty of code like

Isn't this what -E does?

  -E  Stop after the preprocessing stage; do not run the compiler
      proper.  The output is in the form of preprocessed source
      code, which is sent to the standard output.

      Input files that don't require preprocessing are ignored.

If you read the -C and -CC options carefully, they instruct the compiler to not discard comments. AFAIK the historical compilation process does preprocessing first, where the preprocessor also discards comments, followed by a compilation step. Nowadays these steps are more integrated (for example to show comments and macros in error output, rather than have them discarded or expanded already), but I think things behave as if these steps are separate still. AFAICS, the -C and -CC options modify the preprocessor behaviour to not discard comments, which can be useful if you want to see preprocessed output but not lose comments, which I think does not apply here.

@matthijskooijman
Copy link
Collaborator

Apart from that, the biggest problem in the current behaviour is that it reports errors that are not going to appear during compilation. Diagnostic may be improved by adding a line like Could not detect includes from file "filename.ccp" in verbose mode, so a proper bugreport would spot them.

Correct, so ideally we would prevent errors from happening that do not also happen during actual compilation. In this case, I think remove -CC might have this result.

More generally, as I mentioned, hiding an error during include scanning seems contro-productive. Hiding the actual error, but instead showing a generic error as you propose could be a compromise (though having the real error output available right away might help diagnosing issues like these in the future). One other compromise could be to always show a "failed include scanning" error message as you propose, and in verbose mode add the actual compiler error?

@facchinm
Copy link
Member Author

The problem that I see is that the error that would be displayed is not an error in fact, but only a byproduct of the flags we apply. Removing -CC has a couple of side effects:

@matthijskooijman
Copy link
Collaborator

doxigen-like comments will be discarded and can't be used by arduino-preprocessor to give inline help on autocomplete

If we drop -CC for include scanning, that doesn't have to influence anything else, right? This might require adding a separate recipe for this, though.

some platforms override the default implementation and should be updated independently

Correct, that is indeed tricky. We might implement some way to automatically apply such changes to older cores. I've been brooding on such an approach for a while and already wrote a specific proposal, but just now realized I showed the proposal to @cmaglie for a first review, but then forgot to send it to the developers list. I just sent the proposal there.

If we would implement that proposal (or possible even without), we could let arduino-builder automatically remove -CC from the recipe it reads from platform.txt (in a similar way where it already removes -MD or something like that). This is of course ugly and might result in problems in the future, which is what my proposal on the list is intended to solve (by limiting the uglyness to cores that actually need it).

@facchinm
Copy link
Member Author

facchinm commented Sep 3, 2018

@ArduinoBot
Copy link
Contributor

✅ Build completed.

⬇️ Build URL: http://downloads.arduino.cc/PR/arduino-builder/arduino-builder-285.zip

ℹ️ To test this build:

  1. Replace arduino-builder binary (you can find it where you installed the IDE) with the provided one

Copy link
Collaborator

@matthijskooijman matthijskooijman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok to me, at first glance. Some observations:

  • This does not actually fix the problem with the marlin firmware. If the preprocessor error it runs into occurs after all include errors, it might actually compile with this fix, though the new error message remains. To really fix this issue, we should be looking at the -CC option as well (but both changes are independently useful, this PR now improves handling errors when the include scanning process differs from the main compilation process, while fiddling with -CC would serve to remove those differences).
  • When such an error in preprocessing occurs, a message is now shown and include scanning continues with the next file, rather than bailing out. This is probably good, since it might allow compilation tot succeed anyway, but when it fails due to a missing library, the error message does provide a pointer to where the problem lies.
  • I think the current implementation will happily add the "no includes found" to the cache, which means that on a subsequent compilation, the error message is not shown. To fix this, I would suggest not adding the empty result to the cache, which causes the cache to be invalidated on a subsequent run. This does mean that effectively no include detection after the error can be cached (but fixing that requires complicating the cache file structure, which is probably not worth it). I've added an inline comment with an implementation suggestion.

// No include found? Bail out.
os.Stderr.Write(preproc_stderr)
return i18n.WrapError(preproc_err)
ctx.GetLogger().Println(constants.LOG_LEVEL_DEBUG, constants.MSG_FIND_INCLUDES_FAILED, sourcePath)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add here:

// When no include is found, but gcc exited with an error, something
// else failed that we cannot fix, so stop processing this file.
// This intentionally does *not* update the cache, to make sure that
// on the next run, the error is shown again.
return nil

@cmaglie
Copy link
Member

cmaglie commented Sep 7, 2018

I'm not so sure if this is really the right fix. Consider the following:

  • A file includes "A.h", which has these comments-in-macros that break with -CC, and then includes "B.h" from a different library.
  • builder first sees a missing "A.h" and includes library A.
  • builder then sees the comment errors, but not a missing "B.h".

just tried this case, and the preprocessor actually finds the missing B.h. This is because the "comment error" doesn't stop the preprocessing: this is a preprocessing not a compile, even explicitly adding syntax errors doesn't produce the error of a real compile.

@cmaglie
Copy link
Member

cmaglie commented Sep 7, 2018

This is the test I've tried:

#include <avr/io.h>
#include <Arduino.h>

#define DIO54_PIN   PINF0
#define DIO54_RPORT PINF
#define DIO54_WPORT PORTF
#define DIO54_DDR   DDRF   
#define DIO54_PWM   NULL

#define OUT_WRITE(IO, v) do{ SET_OUTPUT(IO); }while(0)

#define _SET_OUTPUT(IO) do {DIO ## IO ## _DDR |= _BV(DIO ## IO ## _PIN); } while (0)
#define SET_OUTPUT(IO) _SET_OUTPUT(IO)

#define SUICIDE_PIN        54  // Must be enabled at startup to keep power flowing
 
void argh() {
  OUT_WRITE(SUICIDE_PIN, HIGH);
}

wrpwefèßðđ#òdsafs²ò43²4àò¹²4idsjmiofvsmniovsdckdsfnkjwnerr
>><;:>,.>;.;<)(/&%&/!($(£"()&$%&)(!"($£&!(&()$!$
#include <SPI.h>

and if I manually run the discovery build:

cmaglie@crocchetta:~/Code-NOBCK/Marlin-1.1.8 CTCPrusa i3$ /home/cmaglie/Code/arduino/build/linux/work/hardware/tools/avr/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10806 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -I/home/cmaglie/Code/arduino/build/linux/work/hardware/arduino/avr/cores/arduino -I/home/cmaglie/Code/arduino/build/linux/work/hardware/arduino/avr/variants/mega /tmp/arduino_build_946615/sketch/failing.cpp -o /dev/null
/tmp/arduino_build_946615/sketch/failing.cpp:16:32: error: pasting "/* Must be enabled at startup to keep power flowing*/" and "_DDR" does not give a valid preprocessing token
 #define SUICIDE_PIN        54  // Must be enabled at startup to keep power flowing
                                ^
/tmp/arduino_build_946615/sketch/failing.cpp:13:36: note: in definition of macro '_SET_OUTPUT'
 #define _SET_OUTPUT(IO) do {DIO ## IO ## _DDR |= _BV(DIO ## IO ## _PIN); } while (0)
                                    ^
/tmp/arduino_build_946615/sketch/failing.cpp:11:30: note: in expansion of macro 'SET_OUTPUT'
 #define OUT_WRITE(IO, v) do{ SET_OUTPUT(IO); }while(0)
                              ^
/tmp/arduino_build_946615/sketch/failing.cpp:19:3: note: in expansion of macro 'OUT_WRITE'
   OUT_WRITE(SUICIDE_PIN, HIGH);
   ^
/tmp/arduino_build_946615/sketch/failing.cpp:19:13: note: in expansion of macro 'SUICIDE_PIN'
   OUT_WRITE(SUICIDE_PIN, HIGH);
             ^
/tmp/arduino_build_946615/sketch/failing.cpp:16:32: error: pasting "/* Must be enabled at startup to keep power flowing*/" and "_PIN" does not give a valid preprocessing token
 #define SUICIDE_PIN        54  // Must be enabled at startup to keep power flowing
                                ^
/tmp/arduino_build_946615/sketch/failing.cpp:13:61: note: in definition of macro '_SET_OUTPUT'
 #define _SET_OUTPUT(IO) do {DIO ## IO ## _DDR |= _BV(DIO ## IO ## _PIN); } while (0)
                                                             ^
/tmp/arduino_build_946615/sketch/failing.cpp:11:30: note: in expansion of macro 'SET_OUTPUT'
 #define OUT_WRITE(IO, v) do{ SET_OUTPUT(IO); }while(0)
                              ^
/tmp/arduino_build_946615/sketch/failing.cpp:19:3: note: in expansion of macro 'OUT_WRITE'
   OUT_WRITE(SUICIDE_PIN, HIGH);
   ^
/tmp/arduino_build_946615/sketch/failing.cpp:19:13: note: in expansion of macro 'SUICIDE_PIN'
   OUT_WRITE(SUICIDE_PIN, HIGH);
             ^
/tmp/arduino_build_946615/sketch/failing.cpp:24:17: fatal error: SPI.h: No such file or directory

as you can see, even with the worst garbage in the middle, the preprocessor will always run until the end and find the include.

@ArduinoBot
Copy link
Contributor

✅ Build completed.

⬇️ Build URL: http://downloads.arduino.cc/PR/arduino-builder/arduino-builder-285.zip

ℹ️ To test this build:

  1. Replace arduino-builder binary (you can find it where you installed the IDE) with the provided one

@cmaglie cmaglie merged commit d1a1a1c into arduino:master Sep 10, 2018
@cmaglie cmaglie deleted the avoid_error_on_preprocessor branch September 10, 2018 08:46
@cmaglie cmaglie added this to the 1.4.1 milestone Sep 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

does not give a valid preprocessing token/Since 1.8.6
4 participants