Skip to content

Auto-import libraries based on sketch profile. #2951

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 13 commits into from
Jul 11, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Moved Result structure into his own package
This change is preparatory for next refactorings
  • Loading branch information
cmaglie committed Jul 7, 2025
commit d25a7e9ab03d42d9bff7076f01386609f3d0104a
21 changes: 11 additions & 10 deletions internal/arduino/builder/internal/detector/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (

"github.com/arduino/arduino-cli/internal/arduino/builder/internal/diagnostics"
"github.com/arduino/arduino-cli/internal/arduino/builder/internal/preprocessor"
"github.com/arduino/arduino-cli/internal/arduino/builder/internal/runner"
"github.com/arduino/arduino-cli/internal/arduino/builder/internal/utils"
"github.com/arduino/arduino-cli/internal/arduino/builder/logger"
"github.com/arduino/arduino-cli/internal/arduino/cores"
Expand Down Expand Up @@ -342,7 +343,7 @@ func (l *SketchLibrariesDetector) findIncludesUntilDone(
}

var preprocErr error
var preprocFirstResult preprocessor.Result
var preprocFirstResult *runner.Result

var missingIncludeH string
if unchanged && cache.valid {
Expand All @@ -353,18 +354,18 @@ func (l *SketchLibrariesDetector) findIncludesUntilDone(
} else {
preprocFirstResult, preprocErr = preprocessor.GCC(ctx, sourcePath, targetFilePath, includeFolders, buildProperties)
if l.logger.VerbosityLevel() == logger.VerbosityVerbose {
l.logger.WriteStdout(preprocFirstResult.Stdout())
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 || preprocFirstResult.Stderr() == nil {
} else if isExitErr := errors.As(preprocErr, &exitErr); !isExitErr || len(preprocFirstResult.Stderr) == 0 {
// Ignore ExitErrors (e.g. gcc returning non-zero status), but bail out on other errors
return preprocErr
} else {
missingIncludeH = IncludesFinderWithRegExp(string(preprocFirstResult.Stderr()))
missingIncludeH = IncludesFinderWithRegExp(string(preprocFirstResult.Stderr))
if missingIncludeH == "" && l.logger.VerbosityLevel() == logger.VerbosityVerbose {
l.logger.Info(i18n.Tr("Error while detecting libraries included by %[1]s", sourcePath))
}
Expand All @@ -380,11 +381,11 @@ func (l *SketchLibrariesDetector) findIncludesUntilDone(
library := l.resolveLibrary(missingIncludeH, platformArch)
if library == nil {
// Library could not be resolved, show error
if preprocErr == nil || preprocFirstResult.Stderr() == nil {
if preprocErr == nil || len(preprocFirstResult.Stderr) == 0 {
// Filename came from cache, so run preprocessor to obtain error to show
result, err := preprocessor.GCC(ctx, sourcePath, targetFilePath, includeFolders, buildProperties)
if l.logger.VerbosityLevel() == logger.VerbosityVerbose {
l.logger.WriteStdout(result.Stdout())
l.logger.WriteStdout(result.Stdout)
}
if err == nil {
// If there is a missing #include in the cache, but running
Expand All @@ -393,12 +394,12 @@ func (l *SketchLibrariesDetector) findIncludesUntilDone(
// deleted, so hopefully the next compilation will succeed.
return errors.New(i18n.Tr("Internal error in cache"))
}
l.diagnosticStore.Parse(result.Args(), result.Stderr())
l.logger.WriteStderr(result.Stderr())
l.diagnosticStore.Parse(result.Args, result.Stderr)
l.logger.WriteStderr(result.Stderr)
return err
}
l.diagnosticStore.Parse(preprocFirstResult.Args(), preprocFirstResult.Stderr())
l.logger.WriteStderr(preprocFirstResult.Stderr())
l.diagnosticStore.Parse(preprocFirstResult.Args, preprocFirstResult.Stderr)
l.logger.WriteStderr(preprocFirstResult.Stderr)
return preprocErr
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"path/filepath"
"runtime"

"github.com/arduino/arduino-cli/internal/arduino/builder/internal/runner"
"github.com/arduino/arduino-cli/internal/arduino/builder/internal/utils"
"github.com/arduino/arduino-cli/internal/arduino/sketch"
"github.com/arduino/arduino-cli/internal/i18n"
Expand All @@ -35,7 +36,7 @@ func PreprocessSketchWithArduinoPreprocessor(
ctx context.Context,
sk *sketch.Sketch, buildPath *paths.Path, includeFolders paths.PathList,
lineOffset int, buildProperties *properties.Map, onlyUpdateCompilationDatabase bool,
) (*Result, error) {
) (*runner.Result, error) {
verboseOut := &bytes.Buffer{}
normalOut := &bytes.Buffer{}
if err := buildPath.Join("preproc").MkdirAll(); err != nil {
Expand All @@ -45,8 +46,8 @@ func PreprocessSketchWithArduinoPreprocessor(
sourceFile := buildPath.Join("sketch", sk.MainFile.Base()+".cpp")
targetFile := buildPath.Join("preproc", "sketch_merged.cpp")
gccResult, err := GCC(ctx, sourceFile, targetFile, includeFolders, buildProperties)
verboseOut.Write(gccResult.Stdout())
verboseOut.Write(gccResult.Stderr())
verboseOut.Write(gccResult.Stdout)
verboseOut.Write(gccResult.Stderr)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -83,13 +84,13 @@ func PreprocessSketchWithArduinoPreprocessor(
commandStdOut, commandStdErr, err := command.RunAndCaptureOutput(ctx)
verboseOut.Write(commandStdErr)
if err != nil {
return &Result{args: gccResult.Args(), stdout: verboseOut.Bytes(), stderr: normalOut.Bytes()}, err
return &runner.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 &Result{args: gccResult.Args(), stdout: verboseOut.Bytes(), stderr: normalOut.Bytes()}, err
return &runner.Result{Args: gccResult.Args, Stdout: verboseOut.Bytes(), Stderr: normalOut.Bytes()}, err
}
return &Result{args: gccResult.Args(), stdout: verboseOut.Bytes(), stderr: normalOut.Bytes()}, err
return &runner.Result{Args: gccResult.Args, Stdout: verboseOut.Bytes(), Stderr: normalOut.Bytes()}, err
}
23 changes: 12 additions & 11 deletions internal/arduino/builder/internal/preprocessor/ctags.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"github.com/arduino/arduino-cli/internal/arduino/builder/cpp"
"github.com/arduino/arduino-cli/internal/arduino/builder/internal/preprocessor/internal/ctags"
"github.com/arduino/arduino-cli/internal/arduino/builder/internal/runner"
"github.com/arduino/arduino-cli/internal/arduino/sketch"
"github.com/arduino/arduino-cli/internal/i18n"
"github.com/arduino/go-paths-helper"
Expand All @@ -43,7 +44,7 @@ func PreprocessSketchWithCtags(
sketch *sketch.Sketch, buildPath *paths.Path, includes paths.PathList,
lineOffset int, buildProperties *properties.Map,
onlyUpdateCompilationDatabase, verbose bool,
) (*Result, error) {
) (*runner.Result, error) {
// Create a temporary working directory
tmpDir, err := paths.MkTempDir("", "")
if err != nil {
Expand All @@ -57,29 +58,29 @@ func PreprocessSketchWithCtags(
// Run GCC preprocessor
sourceFile := buildPath.Join("sketch", sketch.MainFile.Base()+".cpp")
result, err := GCC(ctx, sourceFile, ctagsTarget, includes, buildProperties)
stdout.Write(result.Stdout())
stderr.Write(result.Stderr())
stdout.Write(result.Stdout)
stderr.Write(result.Stderr)
if err != nil {
if !onlyUpdateCompilationDatabase {
return &Result{args: result.Args(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, err
return &runner.Result{Args: result.Args, Stdout: stdout.Bytes(), Stderr: stderr.Bytes()}, err
}

// Do not bail out if we are generating the compile commands database
fmt.Fprintf(stderr, "%s: %s",
i18n.Tr("An error occurred adding prototypes"),
i18n.Tr("the compilation database may be incomplete or inaccurate"))
if err := sourceFile.CopyTo(ctagsTarget); err != nil {
return &Result{args: result.Args(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, err
return &runner.Result{Args: result.Args, Stdout: stdout.Bytes(), Stderr: stderr.Bytes()}, err
}
}

if src, err := ctagsTarget.ReadFile(); err == nil {
filteredSource := filterSketchSource(sketch, bytes.NewReader(src), false, stderr)
if err := ctagsTarget.WriteFile([]byte(filteredSource)); err != nil {
return &Result{args: result.Args(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, err
return &runner.Result{Args: result.Args, Stdout: stdout.Bytes(), Stderr: stderr.Bytes()}, err
}
} else {
return &Result{args: result.Args(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, err
return &runner.Result{Args: result.Args, Stdout: stdout.Bytes(), Stderr: stderr.Bytes()}, err
}

// Run CTags on gcc-preprocessed source
Expand All @@ -89,7 +90,7 @@ func PreprocessSketchWithCtags(
stderr.Write(ctagsStdErr)
}
if err != nil {
return &Result{args: result.Args(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, err
return &runner.Result{Args: result.Args, Stdout: stdout.Bytes(), Stderr: stderr.Bytes()}, err
}

// Parse CTags output
Expand All @@ -104,13 +105,13 @@ func PreprocessSketchWithCtags(
if sourceData, err := sourceFile.ReadFile(); err == nil {
source = string(sourceData)
} else {
return &Result{args: result.Args(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, err
return &runner.Result{Args: result.Args, Stdout: stdout.Bytes(), Stderr: stderr.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 &Result{args: result.Args(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, nil
return &runner.Result{Args: result.Args, Stdout: stdout.Bytes(), Stderr: stderr.Bytes()}, nil
}

insertionLine := firstFunctionLine + lineOffset - 1
Expand All @@ -136,7 +137,7 @@ func PreprocessSketchWithCtags(

// Write back arduino-preprocess output to the sourceFile
err = sourceFile.WriteFile([]byte(preprocessedSource))
return &Result{args: result.Args(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, err
return &runner.Result{Args: result.Args, Stdout: stdout.Bytes(), Stderr: stderr.Bytes()}, err
}

func composePrototypeSection(line int, prototypes []*ctags.Prototype) string {
Expand Down
13 changes: 7 additions & 6 deletions internal/arduino/builder/internal/preprocessor/gcc.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"strings"

"github.com/arduino/arduino-cli/internal/arduino/builder/cpp"
"github.com/arduino/arduino-cli/internal/arduino/builder/internal/runner"
"github.com/arduino/arduino-cli/internal/i18n"
"github.com/arduino/go-paths-helper"
"github.com/arduino/go-properties-orderedmap"
Expand All @@ -35,7 +36,7 @@ func GCC(
ctx context.Context,
sourceFilePath, targetFilePath *paths.Path,
includes paths.PathList, buildProperties *properties.Map,
) (Result, error) {
) (*runner.Result, error) {
gccBuildProperties := properties.NewMap()
gccBuildProperties.Set("preproc.macros.flags", "-w -x c++ -E -CC")
gccBuildProperties.Merge(buildProperties)
Expand All @@ -60,14 +61,14 @@ func GCC(

pattern := gccBuildProperties.Get(gccPreprocRecipeProperty)
if pattern == "" {
return Result{}, errors.New(i18n.Tr("%s pattern is missing", gccPreprocRecipeProperty))
return nil, errors.New(i18n.Tr("%s pattern is missing", gccPreprocRecipeProperty))
}

commandLine := gccBuildProperties.ExpandPropsInString(pattern)
commandLine = properties.DeleteUnexpandedPropsFromString(commandLine)
args, err := properties.SplitQuotedString(commandLine, `"'`, false)
if err != nil {
return Result{}, err
return nil, err
}

// Remove -MMD argument if present. Leaving it will make gcc try
Expand All @@ -76,7 +77,7 @@ func GCC(

proc, err := paths.NewProcess(nil, args...)
if err != nil {
return Result{}, err
return nil, err
}

stdout := bytes.NewBuffer(nil)
Expand All @@ -103,13 +104,13 @@ func GCC(
fmt.Fprintln(stdout, strings.Join(args, " "))

if err := proc.Start(); err != nil {
return Result{}, err
return &runner.Result{}, err
}

// Wait for the process to finish
err = proc.WaitWithinContext(ctx)

return Result{args: proc.GetArgs(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, err
return &runner.Result{Args: proc.GetArgs(), Stdout: stdout.Bytes(), Stderr: stderr.Bytes()}, err
}

type writerFunc func(p []byte) (n int, err error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,11 @@
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.

package preprocessor
package runner

// Result contains the output of a command execution
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
Args []string
Stdout []byte
Stderr []byte
}
6 changes: 3 additions & 3 deletions internal/arduino/builder/preprocess_sketch.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ func (b *Builder) preprocessSketch(includes paths.PathList) error {
)
if result != nil {
if b.logger.VerbosityLevel() == logger.VerbosityVerbose {
b.logger.WriteStdout(result.Stdout())
b.logger.WriteStdout(result.Stdout)
}
b.logger.WriteStderr(result.Stderr())
b.diagnosticStore.Parse(result.Args(), result.Stderr())
b.logger.WriteStderr(result.Stderr)
b.diagnosticStore.Parse(result.Args, result.Stderr)
}

return err
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy