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

Appearance settings

Commit cddbff3

Browse filesBrowse files
Bikappaper1234
andauthored
feat: filter boards by fqbn in connected list (#2052)
Co-authored-by: per1234 <accounts@perglass.com>
1 parent 58c6bc3 commit cddbff3
Copy full SHA for cddbff3

File tree

Expand file treeCollapse file tree

20 files changed

+233
-98
lines changed
Filter options
Expand file treeCollapse file tree

20 files changed

+233
-98
lines changed

‎arduino/cores/fqbn.go

Copy file name to clipboardExpand all lines: arduino/cores/fqbn.go
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ func (fqbn *FQBN) String() string {
7676
return res
7777
}
7878

79+
// Match check if the target FQBN corresponds to the receiver one.
80+
// The core parts are checked for exact equality while board options are loosely
81+
// matched: the set of boards options of the target must be fully contained within
82+
// the one of the receiver and their values must be equal.
83+
func (fqbn *FQBN) Match(target *FQBN) bool {
84+
if fqbn.StringWithoutConfig() != target.StringWithoutConfig() {
85+
return false
86+
}
87+
88+
searchedProperties := target.Configs.Clone()
89+
actualConfigs := fqbn.Configs.AsMap()
90+
for neededKey, neededValue := range searchedProperties.AsMap() {
91+
targetValue, hasKey := actualConfigs[neededKey]
92+
if !hasKey || targetValue != neededValue {
93+
return false
94+
}
95+
}
96+
return true
97+
}
98+
7999
// StringWithoutConfig returns the FQBN without the Config part
80100
func (fqbn *FQBN) StringWithoutConfig() string {
81101
return fqbn.Package + ":" + fqbn.PlatformArch + ":" + fqbn.BoardID

‎arduino/cores/fqbn_test.go

Copy file name to clipboardExpand all lines: arduino/cores/fqbn_test.go
+34Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,37 @@ func TestFQBN(t *testing.T) {
121121
"properties.Map{\n \"cpu\": \"atmega\",\n \"speed\": \"1000\",\n \"extra\": \"core=arduino\",\n}",
122122
f.Configs.Dump())
123123
}
124+
125+
func TestMatch(t *testing.T) {
126+
expectedMatches := [][]string{
127+
{"arduino:avr:uno", "arduino:avr:uno"},
128+
{"arduino:avr:uno", "arduino:avr:uno:opt1=1,opt2=2"},
129+
{"arduino:avr:uno:opt1=1", "arduino:avr:uno:opt1=1,opt2=2"},
130+
{"arduino:avr:uno:opt1=1,opt2=2", "arduino:avr:uno:opt1=1,opt2=2"},
131+
{"arduino:avr:uno:opt3=3,opt1=1,opt2=2", "arduino:avr:uno:opt2=2,opt3=3,opt1=1,opt4=4"},
132+
}
133+
134+
for _, pair := range expectedMatches {
135+
a, err := ParseFQBN(pair[0])
136+
require.NoError(t, err)
137+
b, err := ParseFQBN(pair[1])
138+
require.NoError(t, err)
139+
require.True(t, b.Match(a))
140+
}
141+
142+
expectedMismatches := [][]string{
143+
{"arduino:avr:uno", "arduino:avr:due"},
144+
{"arduino:avr:uno", "arduino:avr:due:opt1=1,opt2=2"},
145+
{"arduino:avr:uno:opt1=1", "arduino:avr:uno"},
146+
{"arduino:avr:uno:opt1=1,opt2=", "arduino:avr:uno:opt1=1,opt2=3"},
147+
{"arduino:avr:uno:opt1=1,opt2=2", "arduino:avr:uno:opt2=2"},
148+
}
149+
150+
for _, pair := range expectedMismatches {
151+
a, err := ParseFQBN(pair[0])
152+
require.NoError(t, err)
153+
b, err := ParseFQBN(pair[1])
154+
require.NoError(t, err)
155+
require.False(t, b.Match(a))
156+
}
157+
}

‎commands/board/list.go

Copy file name to clipboardExpand all lines: commands/board/list.go
+26-1Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,15 @@ func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, discoveryStartError
205205
}
206206
defer release()
207207

208+
var fqbnFilter *cores.FQBN
209+
if f := req.GetFqbn(); f != "" {
210+
var err error
211+
fqbnFilter, err = cores.ParseFQBN(f)
212+
if err != nil {
213+
return nil, nil, &arduino.InvalidFQBNError{Cause: err}
214+
}
215+
}
216+
208217
dm := pme.DiscoveryManager()
209218
discoveryStartErrors = dm.Start()
210219
time.Sleep(time.Duration(req.GetTimeout()) * time.Millisecond)
@@ -222,11 +231,27 @@ func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, discoveryStartError
222231
Port: port.ToRPC(),
223232
MatchingBoards: boards,
224233
}
225-
retVal = append(retVal, b)
234+
235+
if fqbnFilter == nil || hasMatchingBoard(b, fqbnFilter) {
236+
retVal = append(retVal, b)
237+
}
226238
}
227239
return retVal, discoveryStartErrors, nil
228240
}
229241

242+
func hasMatchingBoard(b *rpc.DetectedPort, fqbnFilter *cores.FQBN) bool {
243+
for _, detectedBoard := range b.MatchingBoards {
244+
detectedFqbn, err := cores.ParseFQBN(detectedBoard.Fqbn)
245+
if err != nil {
246+
continue
247+
}
248+
if detectedFqbn.Match(fqbnFilter) {
249+
return true
250+
}
251+
}
252+
return false
253+
}
254+
230255
// Watch returns a channel that receives boards connection and disconnection events.
231256
// It also returns a callback function that must be used to stop and dispose the watch.
232257
func Watch(req *rpc.BoardListWatchRequest) (<-chan *rpc.BoardListWatchResponse, func(), error) {

‎internal/cli/board/list.go

Copy file name to clipboardExpand all lines: internal/cli/board/list.go
+10-2Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
package board
1717

1818
import (
19+
"errors"
1920
"fmt"
2021
"os"
2122
"sort"
2223

24+
"github.com/arduino/arduino-cli/arduino"
2325
"github.com/arduino/arduino-cli/arduino/cores"
2426
"github.com/arduino/arduino-cli/commands/board"
2527
"github.com/arduino/arduino-cli/internal/cli/arguments"
@@ -47,6 +49,7 @@ func initListCommand() *cobra.Command {
4749
}
4850

4951
timeoutArg.AddToCommand(listCommand)
52+
fqbn.AddToCommand(listCommand)
5053
listCommand.Flags().BoolVarP(&watch, "watch", "w", false, tr("Command keeps running and prints list of connected boards whenever there is a change."))
5154

5255
return listCommand
@@ -63,14 +66,19 @@ func runListCommand(cmd *cobra.Command, args []string) {
6366
return
6467
}
6568

66-
ports, discvoeryErrors, err := board.List(&rpc.BoardListRequest{
69+
ports, discoveryErrors, err := board.List(&rpc.BoardListRequest{
6770
Instance: inst,
6871
Timeout: timeoutArg.Get().Milliseconds(),
72+
Fqbn: fqbn.String(),
6973
})
74+
var invalidFQBNErr *arduino.InvalidFQBNError
75+
if errors.As(err, &invalidFQBNErr) {
76+
feedback.Fatal(tr(err.Error()), feedback.ErrBadArgument)
77+
}
7078
if err != nil {
7179
feedback.Warning(tr("Error detecting boards: %v", err))
7280
}
73-
for _, err := range discvoeryErrors {
81+
for _, err := range discoveryErrors {
7482
feedback.Warning(tr("Error starting discovery: %v", err))
7583
}
7684
feedback.PrintResult(result{ports})

‎internal/integrationtest/board/board_test.go

Copy file name to clipboardExpand all lines: internal/integrationtest/board/board_test.go
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,39 @@ func TestBoardList(t *testing.T) {
9191
MustBeEmpty()
9292
}
9393

94+
func TestBoardListWithFqbnFilter(t *testing.T) {
95+
if os.Getenv("CI") != "" {
96+
t.Skip("VMs have no serial ports")
97+
}
98+
99+
env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t)
100+
defer env.CleanUp()
101+
102+
_, _, err := cli.Run("core", "update-index")
103+
require.NoError(t, err)
104+
stdout, _, err := cli.Run("board", "list", "-b", "foo:bar:baz", "--format", "json")
105+
require.NoError(t, err)
106+
// this is a bit of a passpartout test, it actually filters the "bluetooth boards" locally
107+
// but it would succeed even if the filtering wasn't working properly
108+
// TODO: find a way to simulate connected boards or create a unit test which
109+
// mocks or initializes multiple components
110+
requirejson.Parse(t, stdout).
111+
MustBeEmpty()
112+
}
113+
114+
func TestBoardListWithFqbnFilterInvalid(t *testing.T) {
115+
if os.Getenv("CI") != "" {
116+
t.Skip("VMs have no serial ports")
117+
}
118+
119+
env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t)
120+
defer env.CleanUp()
121+
122+
_, stderr, err := cli.Run("board", "list", "-b", "yadayada", "--format", "json")
123+
require.Error(t, err)
124+
requirejson.Query(t, stderr, ".error", `"Invalid FQBN: not an FQBN: yadayada"`)
125+
}
126+
94127
func TestBoardListWithInvalidDiscovery(t *testing.T) {
95128
env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t)
96129
defer env.CleanUp()

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.