Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0e5f629

Browse files
authored
Report compiler errors in machine readable format (#2182)
* Added gRPC protocol for compiler diagnostics reporting * Builder now support parsing of compiler output * Added gcc output parser * Implementation of compile output parser in gRPC command * Tell prettier to ignore testdata files * Added proper result structure for diagnostics * Added integration test * Fixed parser bug and added unit test
1 parent 7a9be52 commit 0e5f629

File tree

23 files changed

+1646
-47
lines changed

23 files changed

+1646
-47
lines changed

‎.prettierignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
.vs/
55
.ionide/
66

7+
testdata
78
/arduino/libraries/librariesindex/testdata/invalid.json
89
/arduino/security/testdata/
910
/.licenses/

‎arduino/builder/builder.go‎

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"github.com/arduino/arduino-cli/arduino/builder/internal/compilation"
2727
"github.com/arduino/arduino-cli/arduino/builder/internal/detector"
28+
"github.com/arduino/arduino-cli/arduino/builder/internal/diagnostics"
2829
"github.com/arduino/arduino-cli/arduino/builder/internal/logger"
2930
"github.com/arduino/arduino-cli/arduino/builder/internal/progress"
3031
"github.com/arduino/arduino-cli/arduino/builder/internal/utils"
@@ -36,6 +37,7 @@ import (
3637
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3738
"github.com/arduino/go-paths-helper"
3839
"github.com/arduino/go-properties-orderedmap"
40+
"github.com/sirupsen/logrus"
3941
)
4042

4143
// ErrSketchCannotBeLocatedInBuildPath fixdoc
@@ -90,6 +92,12 @@ type Builder struct {
9092
buildOptions *buildOptions
9193

9294
libsDetector *detector.SketchLibrariesDetector
95+
96+
// This is a function used to parse the output of the compiler
97+
// It is used to extract errors and warnings
98+
compilerOutputParser diagnostics.CompilerOutputParserCB
99+
// and here are the diagnostics parsed from the compiler
100+
compilerDiagnostics diagnostics.Diagnostics
93101
}
94102

95103
// buildArtifacts contains the result of various build
@@ -189,7 +197,7 @@ func NewBuilder(
189197
logger.Warn(string(verboseOut))
190198
}
191199

192-
return &Builder{
200+
b:= &Builder{
193201
sketch: sk,
194202
buildProperties: buildProperties,
195203
buildPath: buildPath,
@@ -226,7 +234,26 @@ func NewBuilder(
226234
buildProperties.GetPath("runtime.platform.path"),
227235
buildProperties.GetPath("build.core.path"), // TODO can we buildCorePath ?
228236
),
229-
}, nil
237+
}
238+
239+
b.compilerOutputParser = func(cmdline []string, out []byte) {
240+
compiler := diagnostics.DetectCompilerFromCommandLine(
241+
cmdline,
242+
false, // at the moment compiler-probing is not required
243+
)
244+
if compiler == nil {
245+
logrus.Warnf("Could not detect compiler from: %s", cmdline)
246+
return
247+
}
248+
diags, err := diagnostics.ParseCompilerOutput(compiler, out)
249+
if err != nil {
250+
logrus.Warnf("Error parsing compiler output: %s", err)
251+
return
252+
}
253+
b.compilerDiagnostics = append(b.compilerDiagnostics, diags...)
254+
}
255+
256+
return b, nil
230257
}
231258

232259
// GetBuildProperties returns the build properties for running this build
@@ -249,6 +276,11 @@ func (b *Builder) ImportedLibraries() libraries.List {
249276
return b.libsDetector.ImportedLibraries()
250277
}
251278

279+
// CompilerDiagnostics returns the parsed compiler diagnostics
280+
func (b *Builder) CompilerDiagnostics() diagnostics.Diagnostics {
281+
return b.compilerDiagnostics
282+
}
283+
252284
// Preprocess fixdoc
253285
func (b *Builder) Preprocess() ([]byte, error) {
254286
b.Progress.AddSubSteps(6)

‎arduino/builder/compilation.go‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ func (b *Builder) compileFileWithRecipe(
166166
}
167167
b.logger.WriteStderr(commandStderr.Bytes())
168168

169+
// Parse the output of the compiler to gather errors and warnings...
170+
if b.compilerOutputParser != nil {
171+
b.compilerOutputParser(command.GetArgs(), commandStdout.Bytes())
172+
b.compilerOutputParser(command.GetArgs(), commandStderr.Bytes())
173+
}
174+
169175
// ...and then return the error
170176
if err != nil {
171177
return nil, errors.WithStack(err)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2023 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to license@arduino.cc.
15+
16+
package diagnostics
17+
18+
import (
19+
"bytes"
20+
"path/filepath"
21+
"strings"
22+
23+
"github.com/arduino/arduino-cli/executils"
24+
semver "go.bug.st/relaxed-semver"
25+
)
26+
27+
// DetectedCompiler represents a compiler detected from a given command line
28+
type DetectedCompiler struct {
29+
Name string
30+
Family string
31+
Version *semver.Version
32+
DetailedVersion []string
33+
}
34+
35+
// This function is overridden for mocking unit tests
36+
var runProcess = func(args ...string) []string {
37+
if cmd, err := executils.NewProcess(nil, args...); err == nil {
38+
out := &bytes.Buffer{}
39+
cmd.RedirectStdoutTo(out)
40+
cmd.Run()
41+
return splitLines(out.Bytes())
42+
}
43+
return nil
44+
}
45+
46+
// DetectCompilerFromCommandLine tries to detect a compiler from a given command line.
47+
// If probeCompiler is true, the compiler may be executed with different flags to
48+
// infer the version or capabilities.
49+
func DetectCompilerFromCommandLine(args []string, probeCompiler bool) *DetectedCompiler {
50+
if len(args) == 0 {
51+
return nil
52+
}
53+
basename := filepath.Base(args[0])
54+
family := ""
55+
if strings.Contains(basename, "g++") || strings.Contains(basename, "gcc") {
56+
family = "gcc"
57+
}
58+
res := &DetectedCompiler{
59+
Name: basename,
60+
Family: family,
61+
}
62+
63+
if family == "gcc" && probeCompiler {
64+
// Run "gcc --version" to obtain more info
65+
res.DetailedVersion = runProcess(args[0], "--version")
66+
67+
// Usually on the first line we get the compiler architecture and
68+
// version (as last field), followed by the compiler license, for
69+
// example:
70+
//
71+
// g++ (Ubuntu 12.2.0-3ubuntu1) 12.2.0
72+
// Copyright (C) 2022 Free Software Foundation, Inc.
73+
// This is free software; see the source for copying conditions. There is NO
74+
// warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
75+
//
76+
if len(res.DetailedVersion) > 0 {
77+
split := strings.Split(res.DetailedVersion[0], " ")
78+
if len(split) >= 3 {
79+
res.Name = split[0]
80+
res.Version, _ = semver.Parse(split[len(split)-1])
81+
}
82+
}
83+
}
84+
return res
85+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2023 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to license@arduino.cc.
15+
16+
package diagnostics
17+
18+
import (
19+
"strings"
20+
"testing"
21+
22+
"github.com/stretchr/testify/require"
23+
)
24+
25+
func init() {
26+
runProcess = mockedRunProcessToGetCompilerVersion
27+
}
28+
29+
func mockedRunProcessToGetCompilerVersion(args ...string) []string {
30+
if strings.HasSuffix(args[0], "7.3.0-atmel3.6.1-arduino7/bin/avr-g++") && args[1] == "--version" {
31+
return []string{
32+
"avr-g++ (GCC) 7.3.0",
33+
"Copyright (C) 2017 Free Software Foundation, Inc.",
34+
"This is free software; see the source for copying conditions. There is NO",
35+
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.",
36+
"",
37+
}
38+
}
39+
if strings.HasSuffix(args[0], "7.3.0-atmel3.6.1-arduino7/bin/avr-gcc") && args[1] == "--version" {
40+
return []string{
41+
"avr-gcc (GCC) 7.3.0",
42+
"Copyright (C) 2017 Free Software Foundation, Inc.",
43+
"This is free software; see the source for copying conditions. There is NO",
44+
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.",
45+
"",
46+
}
47+
}
48+
if strings.HasSuffix(args[0], "xtensa-esp32-elf-gcc/gcc8_4_0-esp-2021r2-patch3/bin/xtensa-esp32-elf-g++") && args[1] == "--version" {
49+
return []string{
50+
"xtensa-esp32-elf-g++ (crosstool-NG esp-2021r2-patch3) 8.4.0",
51+
"Copyright (C) 2018 Free Software Foundation, Inc.",
52+
"This is free software; see the source for copying conditions. There is NO",
53+
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.",
54+
"",
55+
}
56+
}
57+
58+
panic("missing mock for command line: " + strings.Join(args, " "))
59+
}
60+
61+
func TestCompilerDetection(t *testing.T) {
62+
comp := DetectCompilerFromCommandLine([]string{"~/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++"}, true)
63+
require.NotNil(t, comp)
64+
require.Equal(t, "gcc", comp.Family)
65+
require.Equal(t, "avr-g++", comp.Name)
66+
require.Equal(t, "7.3.0", comp.Version.String())
67+
68+
comp = DetectCompilerFromCommandLine([]string{"~/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc"}, true)
69+
require.NotNil(t, comp)
70+
require.Equal(t, "gcc", comp.Family)
71+
require.Equal(t, "avr-gcc", comp.Name)
72+
require.Equal(t, "7.3.0", comp.Version.String())
73+
74+
comp = DetectCompilerFromCommandLine([]string{"/home/megabug/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/gcc8_4_0-esp-2021r2-patch3/bin/xtensa-esp32-elf-g++"}, true)
75+
require.NotNil(t, comp)
76+
require.Equal(t, "gcc", comp.Family)
77+
require.Equal(t, "xtensa-esp32-elf-g++", comp.Name)
78+
require.Equal(t, "8.4.0", comp.Version.String())
79+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /