Skip to content

Commit

Permalink
Added library dependency support (#351)
Browse files Browse the repository at this point in the history
* Added infrastructure to handle 'dependency' field in library index

* Added globals.ParseLibraryReferenceArgs method

* Added check for missing library version/name in args

* Added check for missing core/arch/version in core arg parsing

* 100% test coverage for librariesindexer module

* Added grpc call LibraryResolveDependencies (WIP)

This is just the body of the call, no implementation yet.

* Use semver.Dependency interface to represent a library dependency

* Added library-dep resolution function in core modules

* Added implementation for LibraryResolveDependency grpc call

* Implementation of deps install in 'lib install' command

* Added no-deps flag in lib install command

* Added lib deps command
  • Loading branch information
cmaglie authored Nov 27, 2019
1 parent 96b7093 commit b544181
Show file tree
Hide file tree
Showing 26 changed files with 205,163 additions and 362 deletions.
59 changes: 59 additions & 0 deletions arduino/libraries/librariesindex/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type Library struct {
type Release struct {
Author string
Version *semver.Version
Dependencies []semver.Dependency
Maintainer string
Sentence string
Paragraph string
Expand All @@ -57,6 +58,37 @@ type Release struct {
Library *Library `json:"-"`
}

// GetName returns the name of this library.
func (r *Release) GetName() string {
return r.Library.Name
}

// GetVersion returns the version of this library.
func (r *Release) GetVersion() *semver.Version {
return r.Version
}

// GetDependencies returns the dependencies of this library.
func (r *Release) GetDependencies() []semver.Dependency {
return r.Dependencies
}

// Dependency is a library dependency
type Dependency struct {
Name string
VersionConstraint semver.Constraint
}

// GetName returns the name of the dependency
func (r *Dependency) GetName() string {
return r.Name
}

// GetConstraint returns the version Constraint of the dependecy
func (r *Dependency) GetConstraint() semver.Constraint {
return r.VersionConstraint
}

func (r *Release) String() string {
return r.Library.Name + "@" + r.Version.String()
}
Expand Down Expand Up @@ -94,6 +126,33 @@ func (idx *Index) FindLibraryUpdate(lib *libraries.Library) *Release {
return nil
}

// ResolveDependencies returns the dependencies of a library release.
func (idx *Index) ResolveDependencies(lib *Release) []*Release {
// Box lib index *Release to be digested by dep-resolver
// (TODO: There is a better use of golang interfaces to avoid this?)
allReleases := map[string]semver.Releases{}
for _, indexLib := range idx.Libraries {
releases := semver.Releases{}
for _, indexLibRelease := range indexLib.Releases {
releases = append(releases, indexLibRelease)
}
allReleases[indexLib.Name] = releases
}

// Perform lib resolution
archive := &semver.Archive{
Releases: allReleases,
}
deps := archive.Resolve(lib)

// Unbox resolved deps back into *Release
res := []*Release{}
for _, dep := range deps {
res = append(res, dep.(*Release))
}
return res
}

// Versions returns an array of all versions available of the library
func (library *Library) Versions() []*semver.Version {
res := []*semver.Version{}
Expand Down
113 changes: 113 additions & 0 deletions arduino/libraries/librariesindex/index_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* This file is part of arduino-cli.
*
* Copyright 2018 ARDUINO SA (http://www.arduino.cc/)
*
* This software is released under the GNU General Public License version 3,
* which covers the main part of arduino-cli.
* The terms of this license can be found at:
* https://www.gnu.org/licenses/gpl-3.0.en.html
*
* You can be released from the requirements of the above licenses by purchasing
* a commercial license. Buying such a license is mandatory if you want to modify or
* otherwise use the software for commercial activities involving the Arduino
* software without disclosing the source code of your own applications. To purchase
* a commercial license, send an email to [email protected].
*/

package librariesindex

import (
"fmt"
"testing"

"github.com/arduino/arduino-cli/arduino/libraries"
"github.com/arduino/go-paths-helper"
"github.com/stretchr/testify/require"
semver "go.bug.st/relaxed-semver"
)

func TestIndexer(t *testing.T) {
fail1, err := LoadIndex(paths.New("testdata/inexistent"))
require.Error(t, err)
require.Nil(t, fail1)

fail2, err := LoadIndex(paths.New("testdata/invalid.json"))
require.Error(t, err)
require.Nil(t, fail2)

index, err := LoadIndex(paths.New("testdata/library_index.json"))
require.NoError(t, err)
require.Equal(t, 2380, len(index.Libraries), "parsed libraries count")

alp := index.Libraries["Arduino Low Power"]
require.NotNil(t, alp)
require.Equal(t, 4, len(alp.Releases))
require.Equal(t, "Arduino Low [email protected]", alp.Latest.String())
require.Len(t, alp.Latest.Dependencies, 1)
require.Equal(t, "RTCZero", alp.Latest.Dependencies[0].GetName())
require.Equal(t, "", alp.Latest.Dependencies[0].GetConstraint().String())
require.Equal(t, "[1.0.0 1.1.0 1.2.0 1.2.1]", fmt.Sprintf("%v", alp.Versions()))

rtc100ref := &Reference{Name: "RTCZero", Version: semver.MustParse("1.0.0")}
require.Equal(t, "[email protected]", rtc100ref.String())
rtc100 := index.FindRelease(rtc100ref)
require.NotNil(t, rtc100)
require.Equal(t, "[email protected]", rtc100.String())

rtcLatestRef := &Reference{Name: "RTCZero"}
require.Equal(t, "RTCZero", rtcLatestRef.String())
rtcLatest := index.FindRelease(rtcLatestRef)
require.NotNil(t, rtcLatest)
require.Equal(t, "[email protected]", rtcLatest.String())

rtcInexistent := index.FindRelease(&Reference{
Name: "RTCZero",
Version: semver.MustParse("0.0.0-blah"),
})
require.Nil(t, rtcInexistent)

rtcInexistent = index.FindRelease(&Reference{
Name: "RTCZero-blah",
})
require.Nil(t, rtcInexistent)

rtc := index.FindIndexedLibrary(&libraries.Library{Name: "RTCZero"})
require.NotNil(t, rtc)
require.Equal(t, "RTCZero", rtc.Name)

rtcUpdate := index.FindLibraryUpdate(&libraries.Library{Name: "RTCZero", Version: semver.MustParse("1.0.0")})
require.NotNil(t, rtcUpdate)
require.Equal(t, "[email protected]", rtcUpdate.String())

rtcNoUpdate := index.FindLibraryUpdate(&libraries.Library{Name: "RTCZero", Version: semver.MustParse("3.0.0")})
require.Nil(t, rtcNoUpdate)

rtcInexistent2 := index.FindLibraryUpdate(&libraries.Library{Name: "RTCZero-blah", Version: semver.MustParse("1.0.0")})
require.Nil(t, rtcInexistent2)

resolve1 := index.ResolveDependencies(alp.Releases["1.2.1"])
require.Len(t, resolve1, 2)
require.Contains(t, resolve1, alp.Releases["1.2.1"])
require.Contains(t, resolve1, rtc.Releases["1.6.0"])

oauth010 := index.FindRelease(&Reference{Name: "Arduino_OAuth", Version: semver.MustParse("0.1.0")})
require.NotNil(t, oauth010)
require.Equal(t, "[email protected]", oauth010.String())
eccx133 := index.FindRelease(&Reference{Name: "ArduinoECCX08", Version: semver.MustParse("1.3.3")})
require.NotNil(t, eccx133)
require.Equal(t, "[email protected]", eccx133.String())
bear130 := index.FindRelease(&Reference{Name: "ArduinoBearSSL", Version: semver.MustParse("1.3.0")})
require.NotNil(t, bear130)
require.Equal(t, "[email protected]", bear130.String())
http040 := index.FindRelease(&Reference{Name: "ArduinoHttpClient", Version: semver.MustParse("0.4.0")})
require.NotNil(t, http040)
require.Equal(t, "[email protected]", http040.String())

resolve2 := index.ResolveDependencies(oauth010)
require.Len(t, resolve2, 4)
require.Contains(t, resolve2, oauth010)
require.Contains(t, resolve2, eccx133)
require.Contains(t, resolve2, bear130)
require.Contains(t, resolve2, http040)
}
64 changes: 47 additions & 17 deletions arduino/libraries/librariesindex/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,36 @@ import (
"encoding/json"
"fmt"

"github.com/arduino/arduino-cli/arduino/resources"
"github.com/arduino/go-paths-helper"
semver "go.bug.st/relaxed-semver"

"github.com/arduino/arduino-cli/arduino/resources"
)

type indexJSON struct {
Libraries []indexRelease `json:"libraries"`
}

type indexRelease struct {
Name string `json:"name,required"`
Version *semver.Version `json:"version,required"`
Author string `json:"author"`
Maintainer string `json:"maintainer"`
Sentence string `json:"sentence"`
Paragraph string `json:"paragraph"`
Website string `json:"website"`
Category string `json:"category"`
Architectures []string `json:"architectures"`
Types []string `json:"types"`
URL string `json:"url"`
ArchiveFileName string `json:"archiveFileName"`
Size int64 `json:"size"`
Checksum string `json:"checksum"`
Name string `json:"name,required"`
Version *semver.Version `json:"version,required"`
Author string `json:"author"`
Maintainer string `json:"maintainer"`
Sentence string `json:"sentence"`
Paragraph string `json:"paragraph"`
Website string `json:"website"`
Category string `json:"category"`
Architectures []string `json:"architectures"`
Types []string `json:"types"`
URL string `json:"url"`
ArchiveFileName string `json:"archiveFileName"`
Size int64 `json:"size"`
Checksum string `json:"checksum"`
Dependencies []*indexDependency `json:"dependencies,omitempty"`
}

type indexDependency struct {
Name string `json:"name"`
Version string `json:"version,omitempty"`
}

// LoadIndex reads a library_index.json and create the corresponding Index
Expand Down Expand Up @@ -104,10 +109,35 @@ func (indexLib *indexRelease) extractReleaseIn(library *Library) {
Checksum: indexLib.Checksum,
CachePath: "libraries",
},
Library: library,
Library: library,
Dependencies: indexLib.extractDependencies(),
}
library.Releases[indexLib.Version.String()] = release
if library.Latest == nil || library.Latest.Version.LessThan(release.Version) {
library.Latest = release
}
}

func (indexLib *indexRelease) extractDependencies() []semver.Dependency {
res := []semver.Dependency{}
if indexLib.Dependencies == nil || len(indexLib.Dependencies) == 0 {
return res
}
for _, indexDep := range indexLib.Dependencies {
res = append(res, indexDep.extractDependency())
}
return res
}

func (indexDep *indexDependency) extractDependency() *Dependency {
var constraint semver.Constraint
if c, err := semver.ParseConstraint(indexDep.Version); err == nil {
constraint = c
} else {
// FIXME: report invalid constraint
}
return &Dependency{
Name: indexDep.Name,
VersionConstraint: constraint,
}
}
1 change: 1 addition & 0 deletions arduino/libraries/librariesindex/testdata/invalid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "invalid",
Loading

0 comments on commit b544181

Please sign in to comment.