Skip to content

Commit b544181

Browse files
authored
Added library dependency support (#351)
* 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
1 parent 96b7093 commit b544181

26 files changed

+205163
-362
lines changed

Diff for: arduino/libraries/librariesindex/index.go

+59
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type Library struct {
4545
type Release struct {
4646
Author string
4747
Version *semver.Version
48+
Dependencies []semver.Dependency
4849
Maintainer string
4950
Sentence string
5051
Paragraph string
@@ -57,6 +58,37 @@ type Release struct {
5758
Library *Library `json:"-"`
5859
}
5960

61+
// GetName returns the name of this library.
62+
func (r *Release) GetName() string {
63+
return r.Library.Name
64+
}
65+
66+
// GetVersion returns the version of this library.
67+
func (r *Release) GetVersion() *semver.Version {
68+
return r.Version
69+
}
70+
71+
// GetDependencies returns the dependencies of this library.
72+
func (r *Release) GetDependencies() []semver.Dependency {
73+
return r.Dependencies
74+
}
75+
76+
// Dependency is a library dependency
77+
type Dependency struct {
78+
Name string
79+
VersionConstraint semver.Constraint
80+
}
81+
82+
// GetName returns the name of the dependency
83+
func (r *Dependency) GetName() string {
84+
return r.Name
85+
}
86+
87+
// GetConstraint returns the version Constraint of the dependecy
88+
func (r *Dependency) GetConstraint() semver.Constraint {
89+
return r.VersionConstraint
90+
}
91+
6092
func (r *Release) String() string {
6193
return r.Library.Name + "@" + r.Version.String()
6294
}
@@ -94,6 +126,33 @@ func (idx *Index) FindLibraryUpdate(lib *libraries.Library) *Release {
94126
return nil
95127
}
96128

129+
// ResolveDependencies returns the dependencies of a library release.
130+
func (idx *Index) ResolveDependencies(lib *Release) []*Release {
131+
// Box lib index *Release to be digested by dep-resolver
132+
// (TODO: There is a better use of golang interfaces to avoid this?)
133+
allReleases := map[string]semver.Releases{}
134+
for _, indexLib := range idx.Libraries {
135+
releases := semver.Releases{}
136+
for _, indexLibRelease := range indexLib.Releases {
137+
releases = append(releases, indexLibRelease)
138+
}
139+
allReleases[indexLib.Name] = releases
140+
}
141+
142+
// Perform lib resolution
143+
archive := &semver.Archive{
144+
Releases: allReleases,
145+
}
146+
deps := archive.Resolve(lib)
147+
148+
// Unbox resolved deps back into *Release
149+
res := []*Release{}
150+
for _, dep := range deps {
151+
res = append(res, dep.(*Release))
152+
}
153+
return res
154+
}
155+
97156
// Versions returns an array of all versions available of the library
98157
func (library *Library) Versions() []*semver.Version {
99158
res := []*semver.Version{}

Diff for: arduino/libraries/librariesindex/index_test.go

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* This file is part of arduino-cli.
3+
*
4+
* Copyright 2018 ARDUINO SA (http://www.arduino.cc/)
5+
*
6+
* This software is released under the GNU General Public License version 3,
7+
* which covers the main part of arduino-cli.
8+
* The terms of this license can be found at:
9+
* https://www.gnu.org/licenses/gpl-3.0.en.html
10+
*
11+
* You can be released from the requirements of the above licenses by purchasing
12+
* a commercial license. Buying such a license is mandatory if you want to modify or
13+
* otherwise use the software for commercial activities involving the Arduino
14+
* software without disclosing the source code of your own applications. To purchase
15+
* a commercial license, send an email to [email protected].
16+
*/
17+
18+
package librariesindex
19+
20+
import (
21+
"fmt"
22+
"testing"
23+
24+
"github.com/arduino/arduino-cli/arduino/libraries"
25+
"github.com/arduino/go-paths-helper"
26+
"github.com/stretchr/testify/require"
27+
semver "go.bug.st/relaxed-semver"
28+
)
29+
30+
func TestIndexer(t *testing.T) {
31+
fail1, err := LoadIndex(paths.New("testdata/inexistent"))
32+
require.Error(t, err)
33+
require.Nil(t, fail1)
34+
35+
fail2, err := LoadIndex(paths.New("testdata/invalid.json"))
36+
require.Error(t, err)
37+
require.Nil(t, fail2)
38+
39+
index, err := LoadIndex(paths.New("testdata/library_index.json"))
40+
require.NoError(t, err)
41+
require.Equal(t, 2380, len(index.Libraries), "parsed libraries count")
42+
43+
alp := index.Libraries["Arduino Low Power"]
44+
require.NotNil(t, alp)
45+
require.Equal(t, 4, len(alp.Releases))
46+
require.Equal(t, "Arduino Low [email protected]", alp.Latest.String())
47+
require.Len(t, alp.Latest.Dependencies, 1)
48+
require.Equal(t, "RTCZero", alp.Latest.Dependencies[0].GetName())
49+
require.Equal(t, "", alp.Latest.Dependencies[0].GetConstraint().String())
50+
require.Equal(t, "[1.0.0 1.1.0 1.2.0 1.2.1]", fmt.Sprintf("%v", alp.Versions()))
51+
52+
rtc100ref := &Reference{Name: "RTCZero", Version: semver.MustParse("1.0.0")}
53+
require.Equal(t, "[email protected]", rtc100ref.String())
54+
rtc100 := index.FindRelease(rtc100ref)
55+
require.NotNil(t, rtc100)
56+
require.Equal(t, "[email protected]", rtc100.String())
57+
58+
rtcLatestRef := &Reference{Name: "RTCZero"}
59+
require.Equal(t, "RTCZero", rtcLatestRef.String())
60+
rtcLatest := index.FindRelease(rtcLatestRef)
61+
require.NotNil(t, rtcLatest)
62+
require.Equal(t, "[email protected]", rtcLatest.String())
63+
64+
rtcInexistent := index.FindRelease(&Reference{
65+
Name: "RTCZero",
66+
Version: semver.MustParse("0.0.0-blah"),
67+
})
68+
require.Nil(t, rtcInexistent)
69+
70+
rtcInexistent = index.FindRelease(&Reference{
71+
Name: "RTCZero-blah",
72+
})
73+
require.Nil(t, rtcInexistent)
74+
75+
rtc := index.FindIndexedLibrary(&libraries.Library{Name: "RTCZero"})
76+
require.NotNil(t, rtc)
77+
require.Equal(t, "RTCZero", rtc.Name)
78+
79+
rtcUpdate := index.FindLibraryUpdate(&libraries.Library{Name: "RTCZero", Version: semver.MustParse("1.0.0")})
80+
require.NotNil(t, rtcUpdate)
81+
require.Equal(t, "[email protected]", rtcUpdate.String())
82+
83+
rtcNoUpdate := index.FindLibraryUpdate(&libraries.Library{Name: "RTCZero", Version: semver.MustParse("3.0.0")})
84+
require.Nil(t, rtcNoUpdate)
85+
86+
rtcInexistent2 := index.FindLibraryUpdate(&libraries.Library{Name: "RTCZero-blah", Version: semver.MustParse("1.0.0")})
87+
require.Nil(t, rtcInexistent2)
88+
89+
resolve1 := index.ResolveDependencies(alp.Releases["1.2.1"])
90+
require.Len(t, resolve1, 2)
91+
require.Contains(t, resolve1, alp.Releases["1.2.1"])
92+
require.Contains(t, resolve1, rtc.Releases["1.6.0"])
93+
94+
oauth010 := index.FindRelease(&Reference{Name: "Arduino_OAuth", Version: semver.MustParse("0.1.0")})
95+
require.NotNil(t, oauth010)
96+
require.Equal(t, "[email protected]", oauth010.String())
97+
eccx133 := index.FindRelease(&Reference{Name: "ArduinoECCX08", Version: semver.MustParse("1.3.3")})
98+
require.NotNil(t, eccx133)
99+
require.Equal(t, "[email protected]", eccx133.String())
100+
bear130 := index.FindRelease(&Reference{Name: "ArduinoBearSSL", Version: semver.MustParse("1.3.0")})
101+
require.NotNil(t, bear130)
102+
require.Equal(t, "[email protected]", bear130.String())
103+
http040 := index.FindRelease(&Reference{Name: "ArduinoHttpClient", Version: semver.MustParse("0.4.0")})
104+
require.NotNil(t, http040)
105+
require.Equal(t, "[email protected]", http040.String())
106+
107+
resolve2 := index.ResolveDependencies(oauth010)
108+
require.Len(t, resolve2, 4)
109+
require.Contains(t, resolve2, oauth010)
110+
require.Contains(t, resolve2, eccx133)
111+
require.Contains(t, resolve2, bear130)
112+
require.Contains(t, resolve2, http040)
113+
}

Diff for: arduino/libraries/librariesindex/json.go

+47-17
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,36 @@ import (
2121
"encoding/json"
2222
"fmt"
2323

24+
"github.com/arduino/arduino-cli/arduino/resources"
2425
"github.com/arduino/go-paths-helper"
2526
semver "go.bug.st/relaxed-semver"
26-
27-
"github.com/arduino/arduino-cli/arduino/resources"
2827
)
2928

3029
type indexJSON struct {
3130
Libraries []indexRelease `json:"libraries"`
3231
}
3332

3433
type indexRelease struct {
35-
Name string `json:"name,required"`
36-
Version *semver.Version `json:"version,required"`
37-
Author string `json:"author"`
38-
Maintainer string `json:"maintainer"`
39-
Sentence string `json:"sentence"`
40-
Paragraph string `json:"paragraph"`
41-
Website string `json:"website"`
42-
Category string `json:"category"`
43-
Architectures []string `json:"architectures"`
44-
Types []string `json:"types"`
45-
URL string `json:"url"`
46-
ArchiveFileName string `json:"archiveFileName"`
47-
Size int64 `json:"size"`
48-
Checksum string `json:"checksum"`
34+
Name string `json:"name,required"`
35+
Version *semver.Version `json:"version,required"`
36+
Author string `json:"author"`
37+
Maintainer string `json:"maintainer"`
38+
Sentence string `json:"sentence"`
39+
Paragraph string `json:"paragraph"`
40+
Website string `json:"website"`
41+
Category string `json:"category"`
42+
Architectures []string `json:"architectures"`
43+
Types []string `json:"types"`
44+
URL string `json:"url"`
45+
ArchiveFileName string `json:"archiveFileName"`
46+
Size int64 `json:"size"`
47+
Checksum string `json:"checksum"`
48+
Dependencies []*indexDependency `json:"dependencies,omitempty"`
49+
}
50+
51+
type indexDependency struct {
52+
Name string `json:"name"`
53+
Version string `json:"version,omitempty"`
4954
}
5055

5156
// LoadIndex reads a library_index.json and create the corresponding Index
@@ -104,10 +109,35 @@ func (indexLib *indexRelease) extractReleaseIn(library *Library) {
104109
Checksum: indexLib.Checksum,
105110
CachePath: "libraries",
106111
},
107-
Library: library,
112+
Library: library,
113+
Dependencies: indexLib.extractDependencies(),
108114
}
109115
library.Releases[indexLib.Version.String()] = release
110116
if library.Latest == nil || library.Latest.Version.LessThan(release.Version) {
111117
library.Latest = release
112118
}
113119
}
120+
121+
func (indexLib *indexRelease) extractDependencies() []semver.Dependency {
122+
res := []semver.Dependency{}
123+
if indexLib.Dependencies == nil || len(indexLib.Dependencies) == 0 {
124+
return res
125+
}
126+
for _, indexDep := range indexLib.Dependencies {
127+
res = append(res, indexDep.extractDependency())
128+
}
129+
return res
130+
}
131+
132+
func (indexDep *indexDependency) extractDependency() *Dependency {
133+
var constraint semver.Constraint
134+
if c, err := semver.ParseConstraint(indexDep.Version); err == nil {
135+
constraint = c
136+
} else {
137+
// FIXME: report invalid constraint
138+
}
139+
return &Dependency{
140+
Name: indexDep.Name,
141+
VersionConstraint: constraint,
142+
}
143+
}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "invalid",

0 commit comments

Comments
 (0)