Skip to content

Commit b780f9c

Browse files
authored
Implemented pluggable discovery board matching (#1330)
* Implemented pluggable discovery board matching Added also a compatibility layer for existing platforms. * Fixed docs and linter warnings * Better comments * Added tests and fixed a bug
1 parent 2173c12 commit b780f9c

File tree

7 files changed

+375
-53
lines changed

7 files changed

+375
-53
lines changed

Diff for: arduino/cores/board.go

+43
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,46 @@ func (b *Board) GeneratePropertiesForConfiguration(config string) (*properties.M
139139
}
140140
return b.GetBuildProperties(fqbn.Configs)
141141
}
142+
143+
// IsBoardMatchingIDProperties returns true if the board match the given
144+
// identification properties
145+
func (b *Board) IsBoardMatchingIDProperties(query *properties.Map) bool {
146+
portIDPropsSet := b.Properties.SubTree("upload_port")
147+
if portIDPropsSet.Size() == 0 {
148+
return false
149+
}
150+
151+
// check checks if the given set of properties p match the "query"
152+
check := func(p *properties.Map) bool {
153+
for k, v := range p.AsMap() {
154+
if !strings.EqualFold(query.Get(k), v) {
155+
return false
156+
}
157+
}
158+
return true
159+
}
160+
161+
// First check the identification properties with sub index "upload_port.N.xxx"
162+
idx := 0
163+
haveIndexedProperties := false
164+
for {
165+
idProps := portIDPropsSet.SubTree(fmt.Sprintf("%d", idx))
166+
idx++
167+
if idProps.Size() > 0 {
168+
haveIndexedProperties = true
169+
if check(idProps) {
170+
return true
171+
}
172+
} else if idx > 1 {
173+
// Always check sub-id 0 and 1 (https://github.com/arduino/arduino-cli/issues/456)
174+
break
175+
}
176+
}
177+
178+
// if there are no subindexed then check the whole "upload_port.xxx"
179+
if !haveIndexedProperties {
180+
return check(portIDPropsSet)
181+
}
182+
183+
return false
184+
}

Diff for: arduino/cores/board_test.go

+150
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,153 @@ func TestBoardOptions(t *testing.T) {
339339
// require.NoError(t, err, "marshaling result")
340340
// fmt.Print(string(data))
341341
}
342+
343+
func TestBoardMatching(t *testing.T) {
344+
brd01 := &Board{
345+
Properties: properties.NewFromHashmap(map[string]string{
346+
"upload_port.pid": "0x0010",
347+
"upload_port.vid": "0x2341",
348+
}),
349+
}
350+
require.True(t, brd01.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
351+
"pid": "0x0010",
352+
"vid": "0x2341",
353+
})))
354+
require.False(t, brd01.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
355+
"pid": "xxx",
356+
"vid": "0x2341",
357+
})))
358+
require.False(t, brd01.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
359+
"pid": "0x0010",
360+
})))
361+
// Extra port properties are OK
362+
require.True(t, brd01.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
363+
"pid": "0x0010",
364+
"vid": "0x2341",
365+
"serial": "942947289347893247",
366+
})))
367+
368+
// Indexed identifications
369+
brd02 := &Board{
370+
Properties: properties.NewFromHashmap(map[string]string{
371+
"upload_port.0.pid": "0x0010",
372+
"upload_port.0.vid": "0x2341",
373+
"upload_port.1.pid": "0x0042",
374+
"upload_port.1.vid": "0x2341",
375+
"upload_port.2.pid": "0x0010",
376+
"upload_port.2.vid": "0x2A03",
377+
"upload_port.3.pid": "0x0042",
378+
"upload_port.3.vid": "0x2A03",
379+
"upload_port.4.pid": "0x0210",
380+
"upload_port.4.vid": "0x2341",
381+
"upload_port.5.pid": "0x0242",
382+
"upload_port.5.vid": "0x2341",
383+
}),
384+
}
385+
require.True(t, brd02.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
386+
"pid": "0x0242",
387+
"vid": "0x2341",
388+
})))
389+
require.True(t, brd02.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
390+
"pid": "0x0242",
391+
"vid": "0x2341",
392+
"serial": "897439287289347",
393+
})))
394+
395+
// Indexed starting from 1
396+
brd03 := &Board{
397+
Properties: properties.NewFromHashmap(map[string]string{
398+
"upload_port.1.pid": "0x0042",
399+
"upload_port.1.vid": "0x2341",
400+
"upload_port.2.pid": "0x0010",
401+
"upload_port.2.vid": "0x2A03",
402+
"upload_port.3.pid": "0x0042",
403+
"upload_port.3.vid": "0x2A03",
404+
"upload_port.4.pid": "0x0210",
405+
"upload_port.4.vid": "0x2341",
406+
"upload_port.5.pid": "0x0242",
407+
"upload_port.5.vid": "0x2341",
408+
}),
409+
}
410+
require.True(t, brd03.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
411+
"pid": "0x0242",
412+
"vid": "0x2341",
413+
})))
414+
require.True(t, brd03.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
415+
"pid": "0x0242",
416+
"vid": "0x2341",
417+
"serial": "897439287289347",
418+
})))
419+
420+
// Mixed indentificiations (not-permitted)
421+
brd04 := &Board{
422+
Properties: properties.NewFromHashmap(map[string]string{
423+
"upload_port.pid": "0x2222",
424+
"upload_port.vid": "0x3333",
425+
"upload_port.0.pid": "0x0010",
426+
"upload_port.0.vid": "0x2341",
427+
"upload_port.1.pid": "0x0042",
428+
"upload_port.1.vid": "0x2341",
429+
"upload_port.2.pid": "0x0010",
430+
"upload_port.2.vid": "0x2A03",
431+
"upload_port.3.pid": "0x0042",
432+
"upload_port.3.vid": "0x2A03",
433+
}),
434+
}
435+
require.True(t, brd04.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
436+
"pid": "0x0042",
437+
"vid": "0x2341",
438+
})))
439+
require.True(t, brd04.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
440+
"pid": "0x0042",
441+
"vid": "0x2341",
442+
"serial": "897439287289347",
443+
})))
444+
require.False(t, brd04.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
445+
"pid": "0x2222",
446+
"vid": "0x3333",
447+
})))
448+
require.False(t, brd04.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
449+
"pid": "0x2222",
450+
"vid": "0x3333",
451+
"serial": "897439287289347",
452+
})))
453+
454+
// Mixed protocols
455+
brd05 := &Board{
456+
Properties: properties.NewFromHashmap(map[string]string{
457+
"upload_port.0.pid": "0x0010",
458+
"upload_port.0.vid": "0x2341",
459+
"upload_port.1.pears": "2",
460+
"upload_port.1.apples": "3",
461+
"upload_port.1.lemons": "X",
462+
"upload_port.2.pears": "100",
463+
"upload_port.3.mac": "0x0010",
464+
"upload_port.3.vid": "0x2341",
465+
}),
466+
}
467+
require.True(t, brd05.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
468+
"pid": "0x0010",
469+
"vid": "0x2341",
470+
})))
471+
require.True(t, brd05.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
472+
"pears": "2",
473+
"apples": "3",
474+
"lemons": "X",
475+
})))
476+
require.True(t, brd05.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
477+
"pears": "100",
478+
})))
479+
require.True(t, brd05.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
480+
"mac": "0x0010",
481+
"vid": "0x2341",
482+
})))
483+
require.False(t, brd05.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
484+
"pears": "2",
485+
})))
486+
require.True(t, brd05.IsBoardMatchingIDProperties(properties.NewFromHashmap(map[string]string{
487+
"pears": "100",
488+
"apples": "300",
489+
"lemons": "XXX",
490+
})))
491+
}

Diff for: arduino/cores/cores.go

+14-13
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,20 @@ type PlatformReleaseHelp struct {
4444

4545
// PlatformRelease represents a release of a plaform package.
4646
type PlatformRelease struct {
47-
Resource *resources.DownloadResource
48-
Version *semver.Version
49-
BoardsManifest []*BoardManifest
50-
Dependencies ToolDependencies // The Dependency entries to load tools.
51-
Help PlatformReleaseHelp `json:"-"`
52-
Platform *Platform `json:"-"`
53-
Properties *properties.Map `json:"-"`
54-
Boards map[string]*Board `json:"-"`
55-
Programmers map[string]*Programmer `json:"-"`
56-
Menus *properties.Map `json:"-"`
57-
InstallDir *paths.Path `json:"-"`
58-
IsIDEBundled bool `json:"-"`
59-
IsTrusted bool `json:"-"`
47+
Resource *resources.DownloadResource
48+
Version *semver.Version
49+
BoardsManifest []*BoardManifest
50+
Dependencies ToolDependencies // The Dependency entries to load tools.
51+
Help PlatformReleaseHelp `json:"-"`
52+
Platform *Platform `json:"-"`
53+
Properties *properties.Map `json:"-"`
54+
Boards map[string]*Board `json:"-"`
55+
Programmers map[string]*Programmer `json:"-"`
56+
Menus *properties.Map `json:"-"`
57+
InstallDir *paths.Path `json:"-"`
58+
IsIDEBundled bool `json:"-"`
59+
IsTrusted bool `json:"-"`
60+
PluggableDiscoveryAware bool `json:"-"` // true if the Platform supports pluggable discovery (no compatibility layer required)
6061
}
6162

6263
// BoardManifest contains information about a board. These metadata are usually

Diff for: arduino/cores/packagemanager/identify.go

+3-31
Original file line numberDiff line numberDiff line change
@@ -16,48 +16,20 @@
1616
package packagemanager
1717

1818
import (
19-
"fmt"
20-
"strings"
21-
2219
"github.com/arduino/arduino-cli/arduino/cores"
2320
properties "github.com/arduino/go-properties-orderedmap"
2421
)
2522

26-
// IdentifyBoard returns a list of boards matching the provided identification properties.
23+
// IdentifyBoard returns a list of boards whose identification properties match the
24+
// provided ones.
2725
func (pm *PackageManager) IdentifyBoard(idProps *properties.Map) []*cores.Board {
2826
if idProps.Size() == 0 {
2927
return []*cores.Board{}
3028
}
31-
32-
checkSuffix := func(props *properties.Map, s string) (present bool, matched bool) {
33-
for k, v1 := range idProps.AsMap() {
34-
v2, ok := props.GetOk(k + s)
35-
if !ok {
36-
return false, false
37-
}
38-
if !strings.EqualFold(v1, v2) {
39-
return true, false
40-
}
41-
}
42-
return false, true
43-
}
44-
4529
foundBoards := []*cores.Board{}
4630
for _, board := range pm.InstalledBoards() {
47-
if _, matched := checkSuffix(board.Properties, ""); matched {
31+
if board.IsBoardMatchingIDProperties(idProps) {
4832
foundBoards = append(foundBoards, board)
49-
continue
50-
}
51-
id := 0
52-
for {
53-
present, matched := checkSuffix(board.Properties, fmt.Sprintf(".%d", id))
54-
if matched {
55-
foundBoards = append(foundBoards, board)
56-
}
57-
if !present && id > 0 { // Always check id 0 and 1 (https://github.com/arduino/arduino-cli/issues/456)
58-
break
59-
}
60-
id++
6133
}
6234
}
6335

Diff for: arduino/cores/packagemanager/loader.go

+51
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020
"os"
2121
"path/filepath"
22+
"strconv"
2223
"strings"
2324

2425
"github.com/arduino/arduino-cli/arduino/cores"
@@ -319,6 +320,10 @@ func (pm *PackageManager) loadPlatformRelease(platform *cores.PlatformRelease, p
319320
return fmt.Errorf("loading %s: %s", platformTxtLocalPath, err)
320321
}
321322

323+
if platform.Properties.SubTree("discovery").Size() > 0 {
324+
platform.PluggableDiscoveryAware = true
325+
}
326+
322327
if platform.Platform.Name == "" {
323328
if name, ok := platform.Properties.GetOk("name"); ok {
324329
platform.Platform.Name = name
@@ -380,6 +385,14 @@ func (pm *PackageManager) loadBoards(platform *cores.PlatformRelease) error {
380385
platform.Menus = properties.NewMap()
381386
}
382387

388+
if !platform.PluggableDiscoveryAware {
389+
for _, boardProperties := range propertiesByBoard {
390+
convertVidPidIdentificationPropertiesToPluggableDiscovery(boardProperties)
391+
}
392+
}
393+
394+
platform.Menus = propertiesByBoard["menu"]
395+
383396
// This is not a board id so we remove it to correctly
384397
// set all other boards properties
385398
delete(propertiesByBoard, "menu")
@@ -420,6 +433,44 @@ func (pm *PackageManager) loadBoards(platform *cores.PlatformRelease) error {
420433
return nil
421434
}
422435

436+
// Converts the old:
437+
//
438+
// - xxx.vid.N
439+
// - xxx.pid.N
440+
//
441+
// properties into pluggable discovery compatible:
442+
//
443+
// - xxx.upload_port.N.vid
444+
// - xxx.upload_port.N.pid
445+
//
446+
func convertVidPidIdentificationPropertiesToPluggableDiscovery(boardProperties *properties.Map) {
447+
n := 0
448+
outputVidPid := func(vid, pid string) {
449+
boardProperties.Set(fmt.Sprintf("upload_port.%d.vid", n), vid)
450+
boardProperties.Set(fmt.Sprintf("upload_port.%d.pid", n), pid)
451+
n++
452+
}
453+
if boardProperties.ContainsKey("vid") && boardProperties.ContainsKey("pid") {
454+
outputVidPid(boardProperties.Get("vid"), boardProperties.Get("pid"))
455+
}
456+
457+
for _, k := range boardProperties.Keys() {
458+
if strings.HasPrefix(k, "vid.") {
459+
idx, err := strconv.ParseUint(k[4:], 10, 64)
460+
if err != nil {
461+
continue
462+
}
463+
vidKey := fmt.Sprintf("vid.%d", idx)
464+
pidKey := fmt.Sprintf("pid.%d", idx)
465+
vid, vidOk := boardProperties.GetOk(vidKey)
466+
pid, pidOk := boardProperties.GetOk(pidKey)
467+
if vidOk && pidOk {
468+
outputVidPid(vid, pid)
469+
}
470+
}
471+
}
472+
}
473+
423474
func (pm *PackageManager) loadToolsFromPackage(targetPackage *cores.Package, toolsPath *paths.Path) []*status.Status {
424475
pm.Log.Infof("Loading tools from dir: %s", toolsPath)
425476

0 commit comments

Comments
 (0)