Skip to content

Commit 18349c1

Browse files
silvanocerzacmaglie
authored andcommitted
Add loading of PluggableDiscoveries when loading a platform release
1 parent 3a4246b commit 18349c1

File tree

3 files changed

+198
-0
lines changed

3 files changed

+198
-0
lines changed

arduino/cores/packagemanager/loader.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,3 +591,96 @@ func (pm *PackageManager) LoadToolsFromBundleDirectory(toolsPath *paths.Path) er
591591
}
592592
return nil
593593
}
594+
595+
// LoadDiscoveries load all discoveries for all loaded platforms
596+
// Returns error if:
597+
// * A PluggableDiscovery instance can't be created
598+
// * Tools required by the PlatformRelease cannot be found
599+
// * Command line to start PluggableDiscovery has malformed or mismatched quotes
600+
func (pm *PackageManager) LoadDiscoveries() []*status.Status {
601+
statuses := []*status.Status{}
602+
for _, platform := range pm.InstalledPlatformReleases() {
603+
statuses = append(statuses, pm.loadDiscoveries(platform)...)
604+
}
605+
return statuses
606+
}
607+
608+
func (pm *PackageManager) loadDiscoveries(release *cores.PlatformRelease) []*status.Status {
609+
statuses := []*status.Status{}
610+
discoveryProperties := release.Properties.SubTree("discovery")
611+
612+
if discoveryProperties.Size() == 0 {
613+
return nil
614+
}
615+
616+
// Handles discovery properties formatted like so:
617+
//
618+
// Case 1:
619+
// "discovery.required": "PLATFORM:DISCOVERY_NAME",
620+
//
621+
// Case 2:
622+
// "discovery.required.0": "PLATFORM:DISCOVERY_ID_1",
623+
// "discovery.required.1": "PLATFORM:DISCOVERY_ID_2",
624+
//
625+
// If both indexed and unindexed properties are found the unindexed are ignored
626+
for _, id := range discoveryProperties.ExtractSubIndexLists("required") {
627+
tool := pm.GetTool(id)
628+
if tool == nil {
629+
statuses = append(statuses, status.Newf(codes.FailedPrecondition, "discovery not found: %s", id))
630+
continue
631+
}
632+
toolRelease := tool.GetLatestInstalled()
633+
discoveryPath := toolRelease.InstallDir.Join(tool.Name).String()
634+
d, err := discovery.New(id, discoveryPath)
635+
if err != nil {
636+
statuses = append(statuses, status.Newf(codes.FailedPrecondition, "creating discovery: %s", err))
637+
continue
638+
}
639+
pm.discoveryManager.Add(d)
640+
}
641+
642+
discoveryIDs := discoveryProperties.FirstLevelOf()
643+
delete(discoveryIDs, "required")
644+
// Get the list of tools only we if have there are discoveries that use Direct discovery integration.
645+
// See:
646+
// https://github.com/arduino/tooling-rfcs/blob/main/RFCs/0002-pluggable-discovery.md#direct-discovery-integration-not-recommended
647+
// We need the tools only in that case since we might need some tool's
648+
// runtime properties to expand the discovery pattern to run it correctly.
649+
var tools []*cores.ToolRelease
650+
if len(discoveryIDs) > 0 {
651+
var err error
652+
tools, err = pm.FindToolsRequiredFromPlatformRelease(release)
653+
if err != nil {
654+
statuses = append(statuses, status.New(codes.Internal, err.Error()))
655+
}
656+
}
657+
658+
// Handles discovery properties formatted like so:
659+
//
660+
// discovery.DISCOVERY_ID.pattern: "COMMAND_TO_EXECUTE"
661+
for discoveryID, props := range discoveryIDs {
662+
pattern, ok := props.GetOk("pattern")
663+
if !ok {
664+
statuses = append(statuses, status.Newf(codes.FailedPrecondition, "can't find pattern for discovery with id %s", discoveryID))
665+
continue
666+
}
667+
configuration := release.Properties.Clone()
668+
configuration.Merge(release.RuntimeProperties())
669+
configuration.Merge(props)
670+
671+
for _, tool := range tools {
672+
configuration.Merge(tool.RuntimeProperties())
673+
}
674+
675+
cmd := configuration.ExpandPropsInString(pattern)
676+
if cmdArgs, err := properties.SplitQuotedString(cmd, `"'`, true); err != nil {
677+
statuses = append(statuses, status.New(codes.Internal, err.Error()))
678+
} else if d, err := discovery.New(discoveryID, cmdArgs...); err != nil {
679+
statuses = append(statuses, status.New(codes.Internal, err.Error()))
680+
} else {
681+
pm.discoveryManager.Add(d)
682+
}
683+
}
684+
685+
return statuses
686+
}

arduino/cores/packagemanager/loader_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ package packagemanager
1818
import (
1919
"testing"
2020

21+
"github.com/arduino/go-paths-helper"
2122
"github.com/arduino/go-properties-orderedmap"
2223
"github.com/stretchr/testify/require"
24+
semver "go.bug.st/relaxed-semver"
2325
)
2426

2527
func TestVidPidConvertionToPluggableDiscovery(t *testing.T) {
@@ -103,3 +105,73 @@ arduino_zero_native.pid.3=0x024d
103105
"upload_port.3.pid": "0x024d",
104106
}`, zero4.Dump())
105107
}
108+
109+
func TestLoadDiscoveries(t *testing.T) {
110+
// Create all the necessary data to load discoveries
111+
fakePath := paths.New("fake-path")
112+
packageManager := NewPackageManager(fakePath, fakePath, fakePath, fakePath)
113+
pack := packageManager.Packages.GetOrCreatePackage("arduino")
114+
// ble-discovery tool
115+
tool := pack.GetOrCreateTool("arduino:ble-discovery")
116+
toolRelease := tool.GetOrCreateRelease(semver.ParseRelaxed("1.0.0"))
117+
// We set this to fake the tool is installed
118+
toolRelease.InstallDir = fakePath
119+
tool.GetOrCreateRelease(semver.ParseRelaxed("0.1.0"))
120+
121+
// serial-discovery tool
122+
tool = pack.GetOrCreateTool("arduino:serial-discovery")
123+
tool.GetOrCreateRelease(semver.ParseRelaxed("1.0.0"))
124+
toolRelease = tool.GetOrCreateRelease(semver.ParseRelaxed("0.1.0"))
125+
// We set this to fake the tool is installed
126+
toolRelease.InstallDir = fakePath
127+
128+
platform := pack.GetOrCreatePlatform("avr")
129+
release := platform.GetOrCreateRelease(semver.MustParse("1.0.0"))
130+
131+
release.Properties = properties.NewFromHashmap(map[string]string{
132+
"discovery.required": "arduino:ble-discovery",
133+
})
134+
135+
discoveries, err := packageManager.LoadDiscoveries(release)
136+
require.Len(t, discoveries, 1)
137+
require.NoError(t, err)
138+
require.Equal(t, discoveries[0].GetID(), "arduino:ble-discovery")
139+
140+
release.Properties = properties.NewFromHashmap(map[string]string{
141+
"discovery.required.0": "arduino:ble-discovery",
142+
"discovery.required.1": "arduino:serial-discovery",
143+
})
144+
145+
discoveries, err = packageManager.LoadDiscoveries(release)
146+
require.Len(t, discoveries, 2)
147+
require.NoError(t, err)
148+
require.Equal(t, discoveries[0].GetID(), "arduino:ble-discovery")
149+
require.Equal(t, discoveries[1].GetID(), "arduino:serial-discovery")
150+
151+
release.Properties = properties.NewFromHashmap(map[string]string{
152+
"discovery.required.0": "arduino:ble-discovery",
153+
"discovery.required.1": "arduino:serial-discovery",
154+
"discovery.teensy.pattern": "\"{runtime.tools.teensy_ports.path}/hardware/tools/teensy_ports\" -J2",
155+
})
156+
157+
discoveries, err = packageManager.LoadDiscoveries(release)
158+
require.Len(t, discoveries, 3)
159+
require.NoError(t, err)
160+
require.Equal(t, discoveries[0].GetID(), "arduino:ble-discovery")
161+
require.Equal(t, discoveries[1].GetID(), "arduino:serial-discovery")
162+
require.Equal(t, discoveries[2].GetID(), "teensy")
163+
164+
release.Properties = properties.NewFromHashmap(map[string]string{
165+
"discovery.required": "arduino:some-discovery",
166+
"discovery.required.0": "arduino:ble-discovery",
167+
"discovery.required.1": "arduino:serial-discovery",
168+
"discovery.teensy.pattern": "\"{runtime.tools.teensy_ports.path}/hardware/tools/teensy_ports\" -J2",
169+
})
170+
171+
discoveries, err = packageManager.LoadDiscoveries(release)
172+
require.Len(t, discoveries, 3)
173+
require.NoError(t, err)
174+
require.Equal(t, discoveries[0].GetID(), "arduino:ble-discovery")
175+
require.Equal(t, discoveries[1].GetID(), "arduino:serial-discovery")
176+
require.Equal(t, discoveries[2].GetID(), "teensy")
177+
}

arduino/cores/packagemanager/package_manager.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,39 @@ func (pm *PackageManager) InstalledBoards() []*cores.Board {
439439
return boards
440440
}
441441

442+
func (pm *PackageManager) FindToolsRequiredFromPlatformRelease(platform *cores.PlatformRelease) ([]*cores.ToolRelease, error) {
443+
pm.Log.Infof("Searching tools required for platform %s", platform)
444+
445+
// maps "PACKAGER:TOOL" => ToolRelease
446+
foundTools := map[string]*cores.ToolRelease{}
447+
// A Platform may not specify required tools (because it's a platform that comes from a
448+
// user/hardware dir without a package_index.json) then add all available tools
449+
for _, targetPackage := range pm.Packages {
450+
for _, tool := range targetPackage.Tools {
451+
rel := tool.GetLatestInstalled()
452+
if rel != nil {
453+
foundTools[rel.Tool.Name] = rel
454+
}
455+
}
456+
}
457+
// replace the default tools above with the specific required by the current platform
458+
requiredTools := []*cores.ToolRelease{}
459+
platform.ToolDependencies.Sort()
460+
for _, toolDep := range platform.ToolDependencies {
461+
pm.Log.WithField("tool", toolDep).Infof("Required tool")
462+
tool := pm.FindToolDependency(toolDep)
463+
if tool == nil {
464+
return nil, fmt.Errorf("tool release not found: %s", toolDep)
465+
}
466+
requiredTools = append(requiredTools, tool)
467+
delete(foundTools, tool.Tool.Name)
468+
}
469+
for _, toolRel := range foundTools {
470+
requiredTools = append(requiredTools, toolRel)
471+
}
472+
return requiredTools, nil
473+
}
474+
442475
// GetTool searches for tool in all packages and platforms.
443476
func (pm *PackageManager) GetTool(toolID string) *cores.Tool {
444477
split := strings.Split(toolID, ":")

0 commit comments

Comments
 (0)