diff --git a/internal/arduino/builder/compilation.go b/internal/arduino/builder/compilation.go index 965280a2e9f..a30ad500622 100644 --- a/internal/arduino/builder/compilation.go +++ b/internal/arduino/builder/compilation.go @@ -167,8 +167,8 @@ func (b *Builder) compileFileWithRecipe( // Parse the output of the compiler to gather errors and warnings... if b.diagnosticsManager != nil { - b.diagnosticsManager.ParseOutput(command.GetArgs(), commandStdout.Bytes()) - b.diagnosticsManager.ParseOutput(command.GetArgs(), commandStderr.Bytes()) + b.diagnosticsManager.Parse(command.GetArgs(), commandStdout.Bytes()) + b.diagnosticsManager.Parse(command.GetArgs(), commandStderr.Bytes()) } // ...and then return the error diff --git a/internal/arduino/builder/internal/detector/detector.go b/internal/arduino/builder/internal/detector/detector.go index dbd20dd6b8b..3ca45774c26 100644 --- a/internal/arduino/builder/internal/detector/detector.go +++ b/internal/arduino/builder/internal/detector/detector.go @@ -341,7 +341,7 @@ func (l *SketchLibrariesDetector) findIncludesUntilDone( } var preprocErr error - var preprocStderr []byte + var preprocFirstResult preprocessor.Result var missingIncludeH string if unchanged && cache.valid { @@ -350,21 +350,20 @@ func (l *SketchLibrariesDetector) findIncludesUntilDone( l.logger.Info(tr("Using cached library dependencies for file: %[1]s", sourcePath)) } } else { - var preprocStdout []byte - preprocStdout, preprocStderr, preprocErr = preprocessor.GCC(sourcePath, targetFilePath, includeFolders, buildProperties, l.diagnosticManager) + preprocFirstResult, preprocErr = preprocessor.GCC(sourcePath, targetFilePath, includeFolders, buildProperties) if l.logger.Verbose() { - l.logger.WriteStdout(preprocStdout) + l.logger.WriteStdout(preprocFirstResult.Stdout()) } // Unwrap error and see if it is an ExitError. var exitErr *exec.ExitError if preprocErr == nil { // Preprocessor successful, done missingIncludeH = "" - } else if isExitErr := errors.As(preprocErr, &exitErr); !isExitErr || preprocStderr == nil { + } else if isExitErr := errors.As(preprocErr, &exitErr); !isExitErr || preprocFirstResult.Stderr() == nil { // Ignore ExitErrors (e.g. gcc returning non-zero status), but bail out on other errors return preprocErr } else { - missingIncludeH = IncludesFinderWithRegExp(string(preprocStderr)) + missingIncludeH = IncludesFinderWithRegExp(string(preprocFirstResult.Stderr())) if missingIncludeH == "" && l.logger.Verbose() { l.logger.Info(tr("Error while detecting libraries included by %[1]s", sourcePath)) } @@ -380,22 +379,25 @@ func (l *SketchLibrariesDetector) findIncludesUntilDone( library := l.resolveLibrary(missingIncludeH, platformArch) if library == nil { // Library could not be resolved, show error - if preprocErr == nil || preprocStderr == nil { + if preprocErr == nil || preprocFirstResult.Stderr() == nil { // Filename came from cache, so run preprocessor to obtain error to show - var preprocStdout []byte - preprocStdout, preprocStderr, preprocErr = preprocessor.GCC(sourcePath, targetFilePath, includeFolders, buildProperties, l.diagnosticManager) + result, err := preprocessor.GCC(sourcePath, targetFilePath, includeFolders, buildProperties) if l.logger.Verbose() { - l.logger.WriteStdout(preprocStdout) + l.logger.WriteStdout(result.Stdout()) } - if preprocErr == nil { + if err == nil { // If there is a missing #include in the cache, but running // gcc does not reproduce that, there is something wrong. // Returning an error here will cause the cache to be // deleted, so hopefully the next compilation will succeed. return errors.New(tr("Internal error in cache")) } + l.diagnosticManager.Parse(result.Args(), result.Stderr()) + l.logger.WriteStderr(result.Stderr()) + return err } - l.logger.WriteStderr(preprocStderr) + l.diagnosticManager.Parse(preprocFirstResult.Args(), preprocFirstResult.Stderr()) + l.logger.WriteStderr(preprocFirstResult.Stderr()) return preprocErr } diff --git a/internal/arduino/builder/internal/preprocessor/arduino_preprocessor.go b/internal/arduino/builder/internal/preprocessor/arduino_preprocessor.go index f2dc8eb035f..e991a6f0240 100644 --- a/internal/arduino/builder/internal/preprocessor/arduino_preprocessor.go +++ b/internal/arduino/builder/internal/preprocessor/arduino_preprocessor.go @@ -22,7 +22,6 @@ import ( "path/filepath" "runtime" - "github.com/arduino/arduino-cli/internal/arduino/builder/internal/diagnosticmanager" "github.com/arduino/arduino-cli/internal/arduino/builder/internal/utils" "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/go-paths-helper" @@ -34,21 +33,20 @@ import ( func PreprocessSketchWithArduinoPreprocessor( sk *sketch.Sketch, buildPath *paths.Path, includeFolders paths.PathList, lineOffset int, buildProperties *properties.Map, onlyUpdateCompilationDatabase bool, - diagnosticmanager *diagnosticmanager.Manager, -) ([]byte, []byte, error) { +) (Result, error) { verboseOut := &bytes.Buffer{} normalOut := &bytes.Buffer{} if err := buildPath.Join("preproc").MkdirAll(); err != nil { - return nil, nil, err + return Result{}, err } sourceFile := buildPath.Join("sketch", sk.MainFile.Base()+".cpp") targetFile := buildPath.Join("preproc", "sketch_merged.cpp") - gccStdout, gccStderr, err := GCC(sourceFile, targetFile, includeFolders, buildProperties, diagnosticmanager) - verboseOut.Write(gccStdout) - verboseOut.Write(gccStderr) + gccResult, err := GCC(sourceFile, targetFile, includeFolders, buildProperties) + verboseOut.Write(gccResult.Stdout()) + verboseOut.Write(gccResult.Stderr()) if err != nil { - return nil, nil, err + return Result{}, err } arduiniPreprocessorProperties := properties.NewMap() @@ -61,18 +59,18 @@ func PreprocessSketchWithArduinoPreprocessor( arduiniPreprocessorProperties.SetPath("source_file", targetFile) pattern := arduiniPreprocessorProperties.Get("pattern") if pattern == "" { - return nil, nil, errors.New(tr("arduino-preprocessor pattern is missing")) + return Result{}, errors.New(tr("arduino-preprocessor pattern is missing")) } commandLine := arduiniPreprocessorProperties.ExpandPropsInString(pattern) parts, err := properties.SplitQuotedString(commandLine, `"'`, false) if err != nil { - return nil, nil, err + return Result{}, err } command, err := paths.NewProcess(nil, parts...) if err != nil { - return nil, nil, err + return Result{}, err } if runtime.GOOS == "windows" { // chdir in the uppermost directory to avoid UTF-8 bug in clang (https://github.com/arduino/arduino-preprocessor/issues/2) @@ -83,14 +81,13 @@ func PreprocessSketchWithArduinoPreprocessor( commandStdOut, commandStdErr, err := command.RunAndCaptureOutput(context.Background()) verboseOut.Write(commandStdErr) if err != nil { - return normalOut.Bytes(), verboseOut.Bytes(), err + return Result{args: gccResult.Args(), stdout: verboseOut.Bytes(), stderr: normalOut.Bytes()}, err } result := utils.NormalizeUTF8(commandStdOut) destFile := buildPath.Join(sk.MainFile.Base() + ".cpp") if err := destFile.WriteFile(result); err != nil { - return normalOut.Bytes(), verboseOut.Bytes(), err + return Result{args: gccResult.Args(), stdout: verboseOut.Bytes(), stderr: normalOut.Bytes()}, err } - - return normalOut.Bytes(), verboseOut.Bytes(), err + return Result{args: gccResult.Args(), stdout: verboseOut.Bytes(), stderr: normalOut.Bytes()}, err } diff --git a/internal/arduino/builder/internal/preprocessor/ctags.go b/internal/arduino/builder/internal/preprocessor/ctags.go index 62615c7124e..44e5ebb77b1 100644 --- a/internal/arduino/builder/internal/preprocessor/ctags.go +++ b/internal/arduino/builder/internal/preprocessor/ctags.go @@ -26,7 +26,6 @@ import ( "strings" "github.com/arduino/arduino-cli/internal/arduino/builder/cpp" - "github.com/arduino/arduino-cli/internal/arduino/builder/internal/diagnosticmanager" "github.com/arduino/arduino-cli/internal/arduino/builder/internal/preprocessor/internal/ctags" "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/arduino-cli/internal/i18n" @@ -44,12 +43,11 @@ var DebugPreprocessor bool func PreprocessSketchWithCtags( sketch *sketch.Sketch, buildPath *paths.Path, includes paths.PathList, lineOffset int, buildProperties *properties.Map, onlyUpdateCompilationDatabase bool, - diagnosticManager *diagnosticmanager.Manager, -) ([]byte, []byte, error) { +) (Result, error) { // Create a temporary working directory tmpDir, err := paths.MkTempDir("", "") if err != nil { - return nil, nil, err + return Result{}, err } defer tmpDir.RemoveAll() ctagsTarget := tmpDir.Join("sketch_merged.cpp") @@ -59,13 +57,13 @@ func PreprocessSketchWithCtags( // Run GCC preprocessor sourceFile := buildPath.Join("sketch", sketch.MainFile.Base()+".cpp") - gccStdout, gccStderr, err := GCC(sourceFile, ctagsTarget, includes, buildProperties, diagnosticManager) - verboseOutput.Write(gccStdout) - verboseOutput.Write(gccStderr) - normalOutput.Write(gccStderr) + result, err := GCC(sourceFile, ctagsTarget, includes, buildProperties) + verboseOutput.Write(result.Stdout()) + verboseOutput.Write(result.Stderr()) + normalOutput.Write(result.Stderr()) if err != nil { if !onlyUpdateCompilationDatabase { - return normalOutput.Bytes(), verboseOutput.Bytes(), err + return Result{args: result.Args(), stdout: verboseOutput.Bytes(), stderr: normalOutput.Bytes()}, err } // Do not bail out if we are generating the compile commands database @@ -73,24 +71,24 @@ func PreprocessSketchWithCtags( tr("An error occurred adding prototypes"), tr("the compilation database may be incomplete or inaccurate"))) if err := sourceFile.CopyTo(ctagsTarget); err != nil { - return normalOutput.Bytes(), verboseOutput.Bytes(), err + return Result{args: result.Args(), stdout: verboseOutput.Bytes(), stderr: normalOutput.Bytes()}, err } } if src, err := ctagsTarget.ReadFile(); err == nil { filteredSource := filterSketchSource(sketch, bytes.NewReader(src), false) if err := ctagsTarget.WriteFile([]byte(filteredSource)); err != nil { - return normalOutput.Bytes(), verboseOutput.Bytes(), err + return Result{args: result.Args(), stdout: verboseOutput.Bytes(), stderr: normalOutput.Bytes()}, err } } else { - return normalOutput.Bytes(), verboseOutput.Bytes(), err + return Result{args: result.Args(), stdout: verboseOutput.Bytes(), stderr: normalOutput.Bytes()}, err } // Run CTags on gcc-preprocessed source ctagsOutput, ctagsStdErr, err := RunCTags(ctagsTarget, buildProperties) verboseOutput.Write(ctagsStdErr) if err != nil { - return normalOutput.Bytes(), verboseOutput.Bytes(), err + return Result{args: result.Args(), stdout: verboseOutput.Bytes(), stderr: normalOutput.Bytes()}, err } // Parse CTags output @@ -105,13 +103,13 @@ func PreprocessSketchWithCtags( if sourceData, err := sourceFile.ReadFile(); err == nil { source = string(sourceData) } else { - return normalOutput.Bytes(), verboseOutput.Bytes(), err + return Result{args: result.Args(), stdout: verboseOutput.Bytes(), stderr: normalOutput.Bytes()}, err } source = strings.ReplaceAll(source, "\r\n", "\n") source = strings.ReplaceAll(source, "\r", "\n") sourceRows := strings.Split(source, "\n") if isFirstFunctionOutsideOfSource(firstFunctionLine, sourceRows) { - return normalOutput.Bytes(), verboseOutput.Bytes(), nil + return Result{args: result.Args(), stdout: verboseOutput.Bytes(), stderr: normalOutput.Bytes()}, nil } insertionLine := firstFunctionLine + lineOffset - 1 @@ -137,7 +135,7 @@ func PreprocessSketchWithCtags( // Write back arduino-preprocess output to the sourceFile err = sourceFile.WriteFile([]byte(preprocessedSource)) - return normalOutput.Bytes(), verboseOutput.Bytes(), err + return Result{args: result.Args(), stdout: verboseOutput.Bytes(), stderr: normalOutput.Bytes()}, err } func composePrototypeSection(line int, prototypes []*ctags.Prototype) string { diff --git a/internal/arduino/builder/internal/preprocessor/gcc.go b/internal/arduino/builder/internal/preprocessor/gcc.go index 1925a5b3d24..d9cf1c446ea 100644 --- a/internal/arduino/builder/internal/preprocessor/gcc.go +++ b/internal/arduino/builder/internal/preprocessor/gcc.go @@ -23,7 +23,6 @@ import ( f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/internal/arduino/builder/cpp" - "github.com/arduino/arduino-cli/internal/arduino/builder/internal/diagnosticmanager" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" ) @@ -33,8 +32,7 @@ import ( func GCC( sourceFilePath, targetFilePath *paths.Path, includes paths.PathList, buildProperties *properties.Map, - diagnosticManager *diagnosticmanager.Manager, -) ([]byte, []byte, error) { +) (Result, error) { gccBuildProperties := properties.NewMap() gccBuildProperties.Set("preproc.macros.flags", "-w -x c++ -E -CC") gccBuildProperties.Merge(buildProperties) @@ -59,14 +57,14 @@ func GCC( pattern := gccBuildProperties.Get(gccPreprocRecipeProperty) if pattern == "" { - return nil, nil, errors.New(tr("%s pattern is missing", gccPreprocRecipeProperty)) + return Result{}, errors.New(tr("%s pattern is missing", gccPreprocRecipeProperty)) } commandLine := gccBuildProperties.ExpandPropsInString(pattern) commandLine = properties.DeleteUnexpandedPropsFromString(commandLine) args, err := properties.SplitQuotedString(commandLine, `"'`, false) if err != nil { - return nil, nil, err + return Result{}, err } // Remove -MMD argument if present. Leaving it will make gcc try @@ -75,16 +73,12 @@ func GCC( proc, err := paths.NewProcess(nil, args...) if err != nil { - return nil, nil, err + return Result{}, err } stdout, stderr, err := proc.RunAndCaptureOutput(context.Background()) - if diagnosticManager != nil { - diagnosticManager.ParseOutput(proc.GetArgs(), stderr) - } - // Append gcc arguments to stdout stdout = append([]byte(fmt.Sprintln(strings.Join(args, " "))), stdout...) - return stdout, stderr, err + return Result{args: proc.GetArgs(), stdout: stdout, stderr: stderr}, err } diff --git a/internal/arduino/builder/internal/preprocessor/result.go b/internal/arduino/builder/internal/preprocessor/result.go new file mode 100644 index 00000000000..eb01b8b42ef --- /dev/null +++ b/internal/arduino/builder/internal/preprocessor/result.go @@ -0,0 +1,19 @@ +package preprocessor + +type Result struct { + args []string + stdout []byte + stderr []byte +} + +func (r Result) Args() []string { + return r.args +} + +func (r Result) Stdout() []byte { + return r.stdout +} + +func (r Result) Stderr() []byte { + return r.stderr +} diff --git a/internal/arduino/builder/preprocess_sketch.go b/internal/arduino/builder/preprocess_sketch.go index e63b6d3f676..f88618fd10d 100644 --- a/internal/arduino/builder/preprocess_sketch.go +++ b/internal/arduino/builder/preprocess_sketch.go @@ -23,16 +23,16 @@ import ( // preprocessSketch fixdoc func (b *Builder) preprocessSketch(includes paths.PathList) error { // In the future we might change the preprocessor - normalOutput, verboseOutput, err := preprocessor.PreprocessSketchWithCtags( + result, err := preprocessor.PreprocessSketchWithCtags( b.sketch, b.buildPath, includes, b.lineOffset, b.buildProperties, b.onlyUpdateCompilationDatabase, - b.diagnosticsManager, ) if b.logger.Verbose() { - b.logger.WriteStdout(verboseOutput) + b.logger.WriteStdout(result.Stdout()) } else { - b.logger.WriteStdout(normalOutput) + b.logger.WriteStdout(result.Stderr()) } + b.diagnosticsManager.Parse(result.Args(), result.Stderr()) return err }