diff --git a/arduino/cores/cores.go b/arduino/cores/cores.go index 724dac7c64a..495163ce417 100644 --- a/arduino/cores/cores.go +++ b/arduino/cores/cores.go @@ -17,6 +17,7 @@ package cores import ( "encoding/json" + "fmt" "sort" "strings" @@ -47,7 +48,8 @@ type PlatformRelease struct { Resource *resources.DownloadResource Version *semver.Version BoardsManifest []*BoardManifest - Dependencies ToolDependencies // The Dependency entries to load tools. + ToolDependencies ToolDependencies + DiscoveryDependencies DiscoveryDependencies Help PlatformReleaseHelp `json:"-"` Platform *Platform `json:"-"` Properties *properties.Map `json:"-"` @@ -113,6 +115,30 @@ func (dep *ToolDependency) String() string { return dep.ToolPackager + ":" + dep.ToolName + "@" + dep.ToolVersion.String() } +// DiscoveryDependencies is a list of DiscoveryDependency +type DiscoveryDependencies []*DiscoveryDependency + +// Sort the DiscoveryDependencies by name. +func (d DiscoveryDependencies) Sort() { + sort.Slice(d, func(i, j int) bool { + if d[i].Packager != d[j].Packager { + return d[i].Packager < d[j].Packager + } + return d[i].Name < d[j].Name + }) +} + +// DiscoveryDependency identifies a specific discovery, version is omitted +// since the latest version will always be used +type DiscoveryDependency struct { + Name string + Packager string +} + +func (d *DiscoveryDependency) String() string { + return fmt.Sprintf("%s:%s", d.Packager, d.Name) +} + // GetOrCreateRelease returns the specified release corresponding the provided version, // or creates a new one if not found. func (platform *Platform) GetOrCreateRelease(version *semver.Version) *PlatformRelease { @@ -224,13 +250,21 @@ func (release *PlatformRelease) GetOrCreateBoard(boardID string) *Board { // RequiresToolRelease returns true if the PlatformRelease requires the // toolReleased passed as parameter func (release *PlatformRelease) RequiresToolRelease(toolRelease *ToolRelease) bool { - for _, toolDep := range release.Dependencies { + for _, toolDep := range release.ToolDependencies { if toolDep.ToolName == toolRelease.Tool.Name && toolDep.ToolPackager == toolRelease.Tool.Package.Name && toolDep.ToolVersion.Equal(toolRelease.Version) { return true } } + for _, discovery := range release.DiscoveryDependencies { + if discovery.Name == toolRelease.Tool.Name && + discovery.Packager == toolRelease.Tool.Package.Name && + // We always want the latest discovery version available + toolRelease.Version.Equal(toolRelease.Tool.LatestRelease().Version) { + return true + } + } return false } diff --git a/arduino/cores/cores_test.go b/arduino/cores/cores_test.go index 6df901ae230..25241b1914a 100644 --- a/arduino/cores/cores_test.go +++ b/arduino/cores/cores_test.go @@ -28,7 +28,7 @@ func TestRequiresToolRelease(t *testing.T) { toolDependencyPackager := "arduino" release := PlatformRelease{ - Dependencies: ToolDependencies{ + ToolDependencies: ToolDependencies{ { ToolName: toolDependencyName, ToolVersion: semver.ParseRelaxed(toolDependencyVersion), @@ -55,3 +55,40 @@ func TestRequiresToolRelease(t *testing.T) { toolRelease.Version = semver.ParseRelaxed(toolDependencyVersion) require.True(t, release.RequiresToolRelease(toolRelease)) } + +func TestRequiresToolReleaseDiscovery(t *testing.T) { + toolDependencyName := "ble-discovery" + toolDependencyPackager := "arduino" + + release := PlatformRelease{ + DiscoveryDependencies: DiscoveryDependencies{ + { + Name: toolDependencyName, + Packager: toolDependencyPackager, + }, + }, + } + + toolRelease := &ToolRelease{ + Version: semver.ParseRelaxed("0.1.0"), + Tool: &Tool{ + Name: toolDependencyName + "not", + Releases: map[string]*ToolRelease{ + "1.0.0": {Version: semver.ParseRelaxed("1.0.0")}, + "0.1.0": {Version: semver.ParseRelaxed("0.1.0")}, + "0.0.1": {Version: semver.ParseRelaxed("0.0.1")}, + }, + Package: &Package{ + Name: toolDependencyPackager + "not", + }, + }, + } + + require.False(t, release.RequiresToolRelease(toolRelease)) + toolRelease.Tool.Name = toolDependencyName + require.False(t, release.RequiresToolRelease(toolRelease)) + toolRelease.Tool.Package.Name = toolDependencyPackager + require.False(t, release.RequiresToolRelease(toolRelease)) + toolRelease.Version = semver.ParseRelaxed("1.0.0") + require.True(t, release.RequiresToolRelease(toolRelease)) +} diff --git a/arduino/cores/packageindex/index.go b/arduino/cores/packageindex/index.go index 8297075ee5d..c718ed6ace2 100644 --- a/arduino/cores/packageindex/index.go +++ b/arduino/cores/packageindex/index.go @@ -47,18 +47,19 @@ type indexPackage struct { // indexPlatformRelease represents a single Core Platform from package_index.json file. type indexPlatformRelease struct { - Name string `json:"name,required"` - Architecture string `json:"architecture"` - Version *semver.Version `json:"version,required"` - Deprecated bool `json:"deprecated"` - Category string `json:"category"` - URL string `json:"url"` - ArchiveFileName string `json:"archiveFileName,required"` - Checksum string `json:"checksum,required"` - Size json.Number `json:"size,required"` - Boards []indexBoard `json:"boards"` - Help indexHelp `json:"help,omitempty"` - ToolDependencies []indexToolDependency `json:"toolsDependencies,required"` + Name string `json:"name,required"` + Architecture string `json:"architecture"` + Version *semver.Version `json:"version,required"` + Deprecated bool `json:"deprecated"` + Category string `json:"category"` + URL string `json:"url"` + ArchiveFileName string `json:"archiveFileName,required"` + Checksum string `json:"checksum,required"` + Size json.Number `json:"size,required"` + Boards []indexBoard `json:"boards"` + Help indexHelp `json:"help,omitempty"` + ToolDependencies []indexToolDependency `json:"toolsDependencies"` + DiscoveryDependencies []indexDiscoveryDependency `json:"discoveryDependencies"` } // indexToolDependency represents a single dependency of a core from a tool. @@ -68,6 +69,12 @@ type indexToolDependency struct { Version *semver.RelaxedVersion `json:"version,required"` } +// indexDiscoveryDependency represents a single dependency of a core from a pluggable discovery tool. +type indexDiscoveryDependency struct { + Packager string `json:"packager"` + Name string `json:"name"` +} + // indexToolRelease represents a single Tool from package_index.json file. type indexToolRelease struct { Name string `json:"name,required"` @@ -126,7 +133,7 @@ func IndexFromPlatformRelease(pr *cores.PlatformRelease) Index { } tools := []indexToolDependency{} - for _, t := range pr.Dependencies { + for _, t := range pr.ToolDependencies { tools = append(tools, indexToolDependency{ Packager: t.ToolPackager, Name: t.ToolName, @@ -134,6 +141,14 @@ func IndexFromPlatformRelease(pr *cores.PlatformRelease) Index { }) } + discoveries := []indexDiscoveryDependency{} + for _, d := range pr.DiscoveryDependencies { + discoveries = append(discoveries, indexDiscoveryDependency{ + Packager: d.Packager, + Name: d.Name, + }) + } + packageTools := []*indexToolRelease{} for name, tool := range pr.Platform.Package.Tools { for _, toolRelease := range tool.Releases { @@ -165,18 +180,19 @@ func IndexFromPlatformRelease(pr *cores.PlatformRelease) Index { URL: pr.Platform.Package.URL, Email: pr.Platform.Package.Email, Platforms: []*indexPlatformRelease{{ - Name: pr.Platform.Name, - Architecture: pr.Platform.Architecture, - Version: pr.Version, - Deprecated: pr.Platform.Deprecated, - Category: pr.Platform.Category, - URL: pr.Resource.URL, - ArchiveFileName: pr.Resource.ArchiveFileName, - Checksum: pr.Resource.Checksum, - Size: json.Number(fmt.Sprintf("%d", pr.Resource.Size)), - Boards: boards, - Help: indexHelp{Online: pr.Help.Online}, - ToolDependencies: tools, + Name: pr.Platform.Name, + Architecture: pr.Platform.Architecture, + Version: pr.Version, + Deprecated: pr.Platform.Deprecated, + Category: pr.Platform.Category, + URL: pr.Resource.URL, + ArchiveFileName: pr.Resource.ArchiveFileName, + Checksum: pr.Resource.Checksum, + Size: json.Number(fmt.Sprintf("%d", pr.Resource.Size)), + Boards: boards, + Help: indexHelp{Online: pr.Help.Online}, + ToolDependencies: tools, + DiscoveryDependencies: discoveries, }}, Tools: packageTools, Help: indexHelp{Online: pr.Platform.Package.Help.Online}, @@ -230,24 +246,32 @@ func (inPlatformRelease indexPlatformRelease) extractPlatformIn(outPackage *core } outPlatformRelease.Help = cores.PlatformReleaseHelp{Online: inPlatformRelease.Help.Online} outPlatformRelease.BoardsManifest = inPlatformRelease.extractBoardsManifest() - if deps, err := inPlatformRelease.extractDeps(); err == nil { - outPlatformRelease.Dependencies = deps - } else { - return fmt.Errorf("invalid tool dependencies: %s", err) - } + outPlatformRelease.ToolDependencies = inPlatformRelease.extractToolDependencies() + outPlatformRelease.DiscoveryDependencies = inPlatformRelease.extractDiscoveryDependencies() return nil } -func (inPlatformRelease indexPlatformRelease) extractDeps() (cores.ToolDependencies, error) { - ret := make(cores.ToolDependencies, len(inPlatformRelease.ToolDependencies)) - for i, dep := range inPlatformRelease.ToolDependencies { - ret[i] = &cores.ToolDependency{ - ToolName: dep.Name, - ToolVersion: dep.Version, - ToolPackager: dep.Packager, +func (inPlatformRelease indexPlatformRelease) extractToolDependencies() cores.ToolDependencies { + res := make(cores.ToolDependencies, len(inPlatformRelease.ToolDependencies)) + for i, tool := range inPlatformRelease.ToolDependencies { + res[i] = &cores.ToolDependency{ + ToolName: tool.Name, + ToolVersion: tool.Version, + ToolPackager: tool.Packager, + } + } + return res +} + +func (inPlatformRelease indexPlatformRelease) extractDiscoveryDependencies() cores.DiscoveryDependencies { + res := make(cores.DiscoveryDependencies, len(inPlatformRelease.DiscoveryDependencies)) + for i, discovery := range inPlatformRelease.DiscoveryDependencies { + res[i] = &cores.DiscoveryDependency{ + Name: discovery.Name, + Packager: discovery.Packager, } } - return ret, nil + return res } func (inPlatformRelease indexPlatformRelease) extractBoardsManifest() []*cores.BoardManifest { diff --git a/arduino/cores/packageindex/index_test.go b/arduino/cores/packageindex/index_test.go index c39c5304d27..e981c0830be 100644 --- a/arduino/cores/packageindex/index_test.go +++ b/arduino/cores/packageindex/index_test.go @@ -54,7 +54,7 @@ func TestIndexFromPlatformRelease(t *testing.T) { {Name: "Arduino/Genuino Uno"}, {Name: "Arduino Uno WiFi"}, }, - Dependencies: cores.ToolDependencies{ + ToolDependencies: cores.ToolDependencies{ { ToolPackager: "arduino", ToolName: "avr-gcc", @@ -71,7 +71,16 @@ func TestIndexFromPlatformRelease(t *testing.T) { ToolVersion: semver.ParseRelaxed("1.2.1"), }, }, - + DiscoveryDependencies: cores.DiscoveryDependencies{ + { + Packager: "arduino", + Name: "ble-discovery", + }, + { + Packager: "arduino", + Name: "serial-discovery", + }, + }, Platform: &cores.Platform{ Name: "Arduino AVR Boards", Architecture: "avr", @@ -85,6 +94,109 @@ func TestIndexFromPlatformRelease(t *testing.T) { Email: "packages@arduino.cc", Help: cores.PackageHelp{Online: "http://www.arduino.cc/en/Reference/HomePage"}, Tools: map[string]*cores.Tool{ + "serial-discovery": { + Name: "serial-discovery", + Releases: map[string]*cores.ToolRelease{ + "1.0.0": { + Version: semver.ParseRelaxed("1.0.0"), + Flavors: []*cores.Flavor{ + { + OS: "arm-linux-gnueabihf", + Resource: &resources.DownloadResource{ + URL: "some-serial-discovery-1.0.0-url", + ArchiveFileName: "serial-discovery-1.0.0.tar.bz2", + Checksum: "SHA-256:some-serial-discovery-1.0.0-sha", + Size: 201341, + }, + }, + { + OS: "i686-mingw32", + Resource: &resources.DownloadResource{ + URL: "some-serial-discovery-1.0.0-other-url", + ArchiveFileName: "serial-discovery-1.0.0.tar.gz", + Checksum: "SHA-256:some-serial-discovery-1.0.0-other-sha", + Size: 222918, + }, + }, + }, + }, + "0.1.0": { + Version: semver.ParseRelaxed("0.1.0"), + Flavors: []*cores.Flavor{ + { + OS: "arm-linux-gnueabihf", + Resource: &resources.DownloadResource{ + URL: "some-serial-discovery-0.1.0-url", + ArchiveFileName: "serial-discovery-0.1.0.tar.bz2", + Checksum: "SHA-256:some-serial-discovery-0.1.0-sha", + Size: 201341, + }, + }, + { + OS: "i686-mingw32", + Resource: &resources.DownloadResource{ + URL: "some-serial-discovery-0.1.0-other-url", + ArchiveFileName: "serial-discovery-0.1.0.tar.gz", + Checksum: "SHA-256:some-serial-discovery-0.1.0-other-sha", + Size: 222918, + }, + }, + }, + }, + }, + }, + "ble-discovery": { + Name: "ble-discovery", + Releases: map[string]*cores.ToolRelease{ + "1.0.0": { + Version: semver.ParseRelaxed("1.0.0"), + Flavors: []*cores.Flavor{ + { + OS: "arm-linux-gnueabihf", + Resource: &resources.DownloadResource{ + URL: "some-ble-discovery-1.0.0-url", + ArchiveFileName: "ble-discovery-1.0.0.tar.bz2", + Checksum: "SHA-256:some-ble-discovery-1.0.0-sha", + Size: 201341, + }, + }, + { + OS: "i686-mingw32", + Resource: &resources.DownloadResource{ + URL: "some-ble-discovery-1.0.0-other-url", + ArchiveFileName: "ble-discovery-1.0.0.tar.gz", + Checksum: "SHA-256:some-ble-discovery-1.0.0-other-sha", + Size: 222918, + }, + }, + }, + }, + "0.1.0": { + Version: semver.ParseRelaxed("0.1.0"), + Flavors: []*cores.Flavor{ + { + OS: "arm-linux-gnueabihf", + Resource: &resources.DownloadResource{ + URL: "some-ble-discovery-0.1.0-url", + ArchiveFileName: "ble-discovery-0.1.0.tar.bz2", + Checksum: "SHA-256:some-ble-discovery-0.1.0-sha", + Size: 201341, + }, + }, + + { + OS: "i686-mingw32", + Resource: &resources.DownloadResource{ + URL: "some-ble-discovery-0.1.0-other-url", + ArchiveFileName: "ble-discovery-0.1.0.tar.gz", + Checksum: "SHA-256:some-ble-discovery-0.1.0-other-sha", + Size: 222918, + }, + }, + }, + }, + }, + }, "bossac": { Name: "bossac", Releases: map[string]*cores.ToolRelease{ @@ -233,8 +345,98 @@ func TestIndexFromPlatformRelease(t *testing.T) { Version: semver.ParseRelaxed("1.2.1"), }, }, + DiscoveryDependencies: []indexDiscoveryDependency{ + { + Packager: "arduino", + Name: "ble-discovery", + }, + { + Packager: "arduino", + Name: "serial-discovery", + }, + }, }}, Tools: []*indexToolRelease{ + { + Name: "serial-discovery", + Version: semver.ParseRelaxed("1.0.0"), + Systems: []indexToolReleaseFlavour{ + { + OS: "arm-linux-gnueabihf", + URL: "some-serial-discovery-1.0.0-url", + ArchiveFileName: "serial-discovery-1.0.0.tar.bz2", + Checksum: "SHA-256:some-serial-discovery-1.0.0-sha", + Size: "201341", + }, + { + OS: "i686-mingw32", + URL: "some-serial-discovery-1.0.0-other-url", + ArchiveFileName: "serial-discovery-1.0.0.tar.gz", + Checksum: "SHA-256:some-serial-discovery-1.0.0-other-sha", + Size: "222918", + }, + }, + }, + { + Name: "serial-discovery", + Version: semver.ParseRelaxed("0.1.0"), + Systems: []indexToolReleaseFlavour{ + { + OS: "arm-linux-gnueabihf", + URL: "some-serial-discovery-0.1.0-url", + ArchiveFileName: "serial-discovery-0.1.0.tar.bz2", + Checksum: "SHA-256:some-serial-discovery-0.1.0-sha", + Size: "201341", + }, + { + OS: "i686-mingw32", + URL: "some-serial-discovery-0.1.0-other-url", + ArchiveFileName: "serial-discovery-0.1.0.tar.gz", + Checksum: "SHA-256:some-serial-discovery-0.1.0-other-sha", + Size: "222918", + }, + }, + }, + { + Name: "ble-discovery", + Version: semver.ParseRelaxed("1.0.0"), + Systems: []indexToolReleaseFlavour{ + { + OS: "arm-linux-gnueabihf", + URL: "some-ble-discovery-1.0.0-url", + ArchiveFileName: "ble-discovery-1.0.0.tar.bz2", + Checksum: "SHA-256:some-ble-discovery-1.0.0-sha", + Size: "201341", + }, + { + OS: "i686-mingw32", + URL: "some-ble-discovery-1.0.0-other-url", + ArchiveFileName: "ble-discovery-1.0.0.tar.gz", + Checksum: "SHA-256:some-ble-discovery-1.0.0-other-sha", + Size: "222918", + }, + }, + }, + { + Name: "ble-discovery", + Version: semver.ParseRelaxed("0.1.0"), + Systems: []indexToolReleaseFlavour{ + { + OS: "arm-linux-gnueabihf", + URL: "some-ble-discovery-0.1.0-url", + ArchiveFileName: "ble-discovery-0.1.0.tar.bz2", + Checksum: "SHA-256:some-ble-discovery-0.1.0-sha", + Size: "201341", + }, + { + OS: "i686-mingw32", + URL: "some-ble-discovery-0.1.0-other-url", + ArchiveFileName: "ble-discovery-0.1.0.tar.gz", + Checksum: "SHA-256:some-ble-discovery-0.1.0-other-sha", + Size: "222918", + }, + }, + }, { Name: "bossac", Version: semver.ParseRelaxed("1.6.1-arduino"), @@ -349,6 +551,7 @@ func TestIndexFromPlatformRelease(t *testing.T) { require.Equal(t, expectedPlatform.Size, indexPlatform.Size) require.ElementsMatch(t, expectedPlatform.Boards, indexPlatform.Boards) require.ElementsMatch(t, expectedPlatform.ToolDependencies, indexPlatform.ToolDependencies) + require.ElementsMatch(t, expectedPlatform.DiscoveryDependencies, indexPlatform.DiscoveryDependencies) } } } diff --git a/arduino/cores/packagemanager/download.go b/arduino/cores/packagemanager/download.go index 8e6a9e7c4b6..c6dc0bc1d9d 100644 --- a/arduino/cores/packagemanager/download.go +++ b/arduino/cores/packagemanager/download.go @@ -91,11 +91,19 @@ func (pm *PackageManager) FindPlatformReleaseDependencies(item *PlatformReferenc } // replaces "latest" with latest version too - toolDeps, err := pm.Packages.GetDepsOfPlatformRelease(release) + toolDeps, err := pm.Packages.GetPlatformReleaseToolDependencies(release) if err != nil { return nil, nil, fmt.Errorf("getting tool dependencies for platform %s: %s", release.String(), err) } - return release, toolDeps, nil + + // discovery dependencies differ from normal tool since we always want to use the latest \ + // available version for the platform package + discoveryDependencies, err := pm.Packages.GetPlatformReleaseDiscoveryDependencies(release) + if err != nil { + return nil, nil, fmt.Errorf("getting discovery dependencies for platform %s: %s", release.String(), err) + } + + return release, append(toolDeps, discoveryDependencies...), nil } // DownloadToolRelease downloads a ToolRelease. If the tool is already downloaded a nil Downloader diff --git a/arduino/cores/packagemanager/package_manager.go b/arduino/cores/packagemanager/package_manager.go index 2442890e7ff..8f77d74c953 100644 --- a/arduino/cores/packagemanager/package_manager.go +++ b/arduino/cores/packagemanager/package_manager.go @@ -452,8 +452,8 @@ func (pm *PackageManager) FindToolsRequiredForBoard(board *cores.Board) ([]*core // replace the default tools above with the specific required by the current platform requiredTools := []*cores.ToolRelease{} - platform.Dependencies.Sort() - for _, toolDep := range platform.Dependencies { + platform.ToolDependencies.Sort() + for _, toolDep := range platform.ToolDependencies { pm.Log.WithField("tool", toolDep).Infof("Required tool") tool := pm.FindToolDependency(toolDep) if tool == nil { diff --git a/arduino/cores/status.go b/arduino/cores/status.go index 9c469abf5ca..ef8d998a053 100644 --- a/arduino/cores/status.go +++ b/arduino/cores/status.go @@ -76,13 +76,13 @@ func (packages Packages) Names() []string { return res } -// GetDepsOfPlatformRelease returns the deps of a specified release of a core. -func (packages Packages) GetDepsOfPlatformRelease(release *PlatformRelease) ([]*ToolRelease, error) { +// GetPlatformReleaseToolDependencies returns the tool releases needed by the specified PlatformRelease +func (packages Packages) GetPlatformReleaseToolDependencies(release *PlatformRelease) ([]*ToolRelease, error) { if release == nil { return nil, errors.New("release cannot be nil") } ret := []*ToolRelease{} - for _, dep := range release.Dependencies { + for _, dep := range release.ToolDependencies { pkg, exists := packages[dep.ToolPackager] if !exists { return nil, fmt.Errorf("package %s not found", dep.ToolPackager) @@ -100,6 +100,33 @@ func (packages Packages) GetDepsOfPlatformRelease(release *PlatformRelease) ([]* return ret, nil } +// GetPlatformReleaseDiscoveryDependencies returns the discovery releases needed by the specified PlatformRelease +func (packages Packages) GetPlatformReleaseDiscoveryDependencies(release *PlatformRelease) ([]*ToolRelease, error) { + if release == nil { + return nil, fmt.Errorf("release cannot be nil") + } + + res := []*ToolRelease{} + for _, discovery := range release.DiscoveryDependencies { + pkg, exists := packages[discovery.Packager] + if !exists { + return nil, fmt.Errorf("package %s not found", discovery.Packager) + } + tool, exists := pkg.Tools[discovery.Name] + if !exists { + return nil, fmt.Errorf("tool %s not found", discovery.Name) + } + + // We always want to use the latest available release for discoveries + latestRelease := tool.LatestRelease() + if latestRelease == nil { + return nil, fmt.Errorf("can't find latest release of %s", discovery.Name) + } + res = append(res, latestRelease) + } + return res, nil +} + // GetOrCreatePlatform returns the Platform object with the specified architecture // or creates a new one if not found func (targetPackage *Package) GetOrCreatePlatform(architecture string) *Platform { diff --git a/commands/board/details.go b/commands/board/details.go index a42c821ea90..edbbc9b1302 100644 --- a/commands/board/details.go +++ b/commands/board/details.go @@ -115,7 +115,7 @@ func Details(ctx context.Context, req *rpc.BoardDetailsRequest) (*rpc.BoardDetai } details.ToolsDependencies = []*rpc.ToolsDependencies{} - for _, tool := range boardPlatform.Dependencies { + for _, tool := range boardPlatform.ToolDependencies { toolRelease := pm.FindToolDependency(tool) var systems []*rpc.Systems if toolRelease != nil {