Skip to content

Commit 2ea1385

Browse files
matthijskooijmanfacchinm
authored andcommitted
Improve error handling in include detection
For include detection, the preprocessor is run on all source files, collecting #included filenames from the stderr output, each of which are then resolved to a library to include. A caching mechanism is used to only run the preprocessor when needed. This commit improves the error handling during include detection in a number of ways: - When the preprocessor runs succesfully, processing stops for the current file. Previously, it would always look at stderr to find a missing include filename and only stop if none was found. - When the preprocessor fails, but no filename can be found, show the error preprocessor error. Previously, it would assume that the process was done and stop processing the file without any error. - When no library can be found for a missing include, show the stored error output instead of running the preprocessor again. Previously, the preprocessor would be run a second time, to (re)generate the error message. When the include filename comes from the cache and the preprocessor was not run yet, it is still run to show its errors to the user. This should be very rare, as normally changes that cause a cached filename to become unresolvable to a library also cause the file to be marked as changed, bypassing the cache. When this does happen, the preprocessor is now run using `GCCPreprocRunnerForDiscoveringIncludes()` instead of `GCCPreprocRunner()`, which ensures the preprocessor command is always exactly the same. Before this change, there could be specific circumstances where the first preprocessor run would generate an error, but where the second run would not show the error and include detection would continue as if nothing happened. One such circumstance is described in arduino#230. Signed-off-by: Matthijs Kooijman <[email protected]>
1 parent f536f7b commit 2ea1385

File tree

1 file changed

+33
-10
lines changed

1 file changed

+33
-10
lines changed

Diff for: container_find_includes.go

+33-10
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ import (
119119
"github.com/arduino/arduino-builder/i18n"
120120
"github.com/arduino/arduino-builder/types"
121121
"github.com/arduino/arduino-builder/utils"
122+
"github.com/go-errors/errors"
122123
)
123124

124125
type ContainerFindIncludes struct{}
@@ -318,22 +319,33 @@ func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile t
318319
if library, ok := sourceFile.Origin.(*types.Library); ok && library.UtilityFolder != "" {
319320
includes = append(includes, library.UtilityFolder)
320321
}
322+
var preproc_err error
323+
var preproc_stderr []byte
321324
if unchanged && cache.valid {
322325
include = cache.Next().Include
323326
if first && ctx.Verbose {
324327
ctx.GetLogger().Println(constants.LOG_LEVEL_INFO, constants.MSG_USING_CACHED_INCLUDES, sourcePath)
325328
}
326329
} else {
327-
stderr, err := GCCPreprocRunnerForDiscoveringIncludes(ctx, sourcePath, targetFilePath, includes)
330+
preproc_stderr, preproc_err = GCCPreprocRunnerForDiscoveringIncludes(ctx, sourcePath, targetFilePath, includes)
328331
// Unwrap error and see if it is an ExitError.
329-
// Ignore ExitErrors (e.g. gcc returning
330-
// non-zero status), but bail out on other
331-
// errors
332-
_, is_exit_error := i18n.UnwrapError(err).(*exec.ExitError)
333-
if err != nil && !is_exit_error {
334-
return i18n.WrapError(err)
332+
_, is_exit_error := i18n.UnwrapError(preproc_err).(*exec.ExitError)
333+
if preproc_err == nil {
334+
// Preprocessor successful, done
335+
include = ""
336+
} else if !is_exit_error || preproc_stderr == nil {
337+
// Ignore ExitErrors (e.g. gcc returning
338+
// non-zero status), but bail out on
339+
// other errors
340+
return i18n.WrapError(preproc_err)
341+
} else {
342+
include = IncludesFinderWithRegExp(ctx, string(preproc_stderr))
343+
if include == "" {
344+
// No include found? Bail out.
345+
os.Stderr.Write(preproc_stderr)
346+
return i18n.WrapError(preproc_err)
347+
}
335348
}
336-
include = IncludesFinderWithRegExp(ctx, string(stderr))
337349
}
338350

339351
if include == "" {
@@ -345,8 +357,19 @@ func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile t
345357
library := ResolveLibrary(ctx, include)
346358
if library == nil {
347359
// Library could not be resolved, show error
348-
err := GCCPreprocRunner(ctx, sourcePath, constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E, includes)
349-
return i18n.WrapError(err)
360+
if preproc_err == nil || preproc_stderr == nil {
361+
// Filename came from cache, so run preprocessor to obtain error to show
362+
preproc_stderr, preproc_err = GCCPreprocRunnerForDiscoveringIncludes(ctx, sourcePath, targetFilePath, includes)
363+
if preproc_err == nil {
364+
// If there is a missing #include in the cache, but running
365+
// gcc does not reproduce that, there is something wrong.
366+
// Returning an error here will cause the cache to be
367+
// deleted, so hopefully the next compilation will succeed.
368+
return errors.New("Internal error in cache")
369+
}
370+
}
371+
os.Stderr.Write(preproc_stderr)
372+
return i18n.WrapError(preproc_err)
350373
}
351374

352375
// Add this library to the list of libraries, the

0 commit comments

Comments
 (0)