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 2f7eac2

Browse files
committed
Added gcc output parser
1 parent 7558a71 commit 2f7eac2

File tree

11 files changed

+1033
-0
lines changed

11 files changed

+1033
-0
lines changed
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+
}

‎arduino/builder/internal/diagnostics/parser.go‎

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,159 @@
1515

1616
package diagnostics
1717

18+
import (
19+
"fmt"
20+
"strings"
21+
22+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
23+
)
24+
1825
// CompilerOutputParserCB is a callback function that is called to feed a parser
1926
// with the plain-text compiler output.
2027
type CompilerOutputParserCB func(cmdline []string, out []byte)
28+
29+
// Diagnostics represents a list of diagnostics
30+
type Diagnostics []*Diagnostic
31+
32+
// Diagnostic represents a diagnostic (a compiler error, warning, note, etc.)
33+
type Diagnostic struct {
34+
Severity Severity `json:"severity,omitempty"`
35+
Message string `json:"message"`
36+
File string `json:"file,omitempty"`
37+
Line int `json:"line,omitempty"`
38+
Column int `json:"col,omitempty"`
39+
Context FullContext `json:"context,omitempty"`
40+
Suggestions Notes `json:"suggestions,omitempty"`
41+
}
42+
43+
// Severity is a diagnostic severity
44+
type Severity string
45+
46+
const (
47+
// SeverityUnspecified is the undefined severity
48+
SeverityUnspecified Severity = ""
49+
// SeverityWarning is a warning
50+
SeverityWarning = "WARNING"
51+
// SeverityError is an error
52+
SeverityError = "ERROR"
53+
// SeverityFatal is a fatal error
54+
SeverityFatal = "FATAL"
55+
)
56+
57+
// Notes represents a list of Note
58+
type Notes []*Note
59+
60+
// Note represents a compiler annotation or suggestion
61+
type Note struct {
62+
Message string `json:"message"`
63+
File string `json:"file,omitempty"`
64+
Line int `json:"line,omitempty"`
65+
Column int `json:"col,omitempty"`
66+
}
67+
68+
// FullContext represents a list of Context
69+
type FullContext []*Context
70+
71+
// Context represents a context, i.e. a reference to a file, line and column
72+
// or a part of the code that a Diagnostic refers to.
73+
type Context struct {
74+
Message string `json:"message"`
75+
File string `json:"file,omitempty"`
76+
Line int `json:"line,omitempty"`
77+
Column int `json:"col,omitempty"`
78+
}
79+
80+
// ParseCompilerOutput parses the output of a compiler and returns a list of
81+
// diagnostics.
82+
func ParseCompilerOutput(compiler *DetectedCompiler, out []byte) ([]*Diagnostic, error) {
83+
lines := splitLines(out)
84+
switch compiler.Family {
85+
case "gcc":
86+
return parseGccOutput(lines)
87+
default:
88+
return nil, fmt.Errorf("unsupported compiler: %s", compiler)
89+
}
90+
}
91+
92+
func splitLines(in []byte) []string {
93+
res := strings.Split(string(in), "\n")
94+
for i, line := range res {
95+
res[i] = strings.TrimSuffix(line, "\r")
96+
}
97+
if l := len(res) - 1; res[l] == "" {
98+
res = res[:l]
99+
}
100+
return res
101+
}
102+
103+
// ToRPC converts a Diagnostics to a slice of rpc.CompileDiagnostic
104+
func (d Diagnostics) ToRPC() []*rpc.CompileDiagnostic {
105+
if len(d) == 0 {
106+
return nil
107+
}
108+
var res []*rpc.CompileDiagnostic
109+
for _, diag := range d {
110+
res = append(res, diag.ToRPC())
111+
}
112+
return res
113+
}
114+
115+
// ToRPC converts a Diagnostic to a rpc.CompileDiagnostic
116+
func (d *Diagnostic) ToRPC() *rpc.CompileDiagnostic {
117+
if d == nil {
118+
return nil
119+
}
120+
return &rpc.CompileDiagnostic{
121+
Severity: string(d.Severity),
122+
Message: d.Message,
123+
File: d.File,
124+
Line: int64(d.Line),
125+
Column: int64(d.Column),
126+
Context: d.Context.ToRPC(),
127+
Notes: d.Suggestions.ToRPC(),
128+
}
129+
}
130+
131+
// ToRPC converts a Notes to a slice of rpc.CompileDiagnosticNote
132+
func (s Notes) ToRPC() []*rpc.CompileDiagnosticNote {
133+
var res []*rpc.CompileDiagnosticNote
134+
for _, suggestion := range s {
135+
res = append(res, suggestion.ToRPC())
136+
}
137+
return res
138+
}
139+
140+
// ToRPC converts a Note to a rpc.CompileDiagnosticNote
141+
func (s *Note) ToRPC() *rpc.CompileDiagnosticNote {
142+
if s == nil {
143+
return nil
144+
}
145+
return &rpc.CompileDiagnosticNote{
146+
File: s.File,
147+
Line: int64(s.Line),
148+
Column: int64(s.Column),
149+
Message: s.Message,
150+
}
151+
}
152+
153+
// ToRPC converts a FullContext to a slice of rpc.CompileDiagnosticContext
154+
func (t FullContext) ToRPC() []*rpc.CompileDiagnosticContext {
155+
var res []*rpc.CompileDiagnosticContext
156+
for _, trace := range t {
157+
res = append(res, trace.ToRPC())
158+
}
159+
return res
160+
}
161+
162+
// ToRPC converts a Context to a rpc.CompileDiagnosticContext
163+
func (d *Context) ToRPC() *rpc.CompileDiagnosticContext {
164+
if d == nil {
165+
return nil
166+
}
167+
return &rpc.CompileDiagnosticContext{
168+
File: d.File,
169+
Line: int64(d.Line),
170+
Column: int64(d.Column),
171+
Message: d.Message,
172+
}
173+
}

0 commit comments

Comments
(0)

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