Skip to content

Commit b9599e1

Browse files
cmaglieumbynos
andauthored
[breaking] Fix behaviour of lib commands when a library is installed multiple times (#1878)
* Removed LibraryAlternatives object in favor of libraries.List * Use RealName instead of (dir) Name as key in librarymanager.Library map * Refactored some librarymanager functions to query libraries list * Simplified librariesmanager.Install It does not require installLocation since it can be obtained with libPath.Parent() * Added checks for multiple library installation * Added test to check that the cli is able to handle multiple lib installations * Renamed fields in Library structure: Name->CanonicalName; RealName->Name * Use Name instead of CanonicalName in LibraryList This fixes also `lib list` and `lib examples`. * Use `Name` field where appropriate instead of `CanonicalName` * Updated documentation * Update arduino/libraries/librarieslist.go Co-authored-by: Umberto Baldi <[email protected]> * Improved integration test Co-authored-by: Umberto Baldi <[email protected]>
1 parent a3f6574 commit b9599e1

File tree

22 files changed

+418
-260
lines changed

22 files changed

+418
-260
lines changed

Diff for: arduino/errors.go

+24
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
"github.com/arduino/arduino-cli/i18n"
2323
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
24+
"github.com/arduino/go-paths-helper"
2425
"google.golang.org/grpc/codes"
2526
"google.golang.org/grpc/status"
2627
)
@@ -810,3 +811,26 @@ func (e *MultiplePlatformsError) Error() string {
810811
func (e *MultiplePlatformsError) ToRPCStatus() *status.Status {
811812
return status.New(codes.InvalidArgument, e.Error())
812813
}
814+
815+
// MultipleLibraryInstallDetected is returned when the user request an
816+
// operation on a library but multiple libraries with the same name
817+
// (in library.properties) are detected.
818+
type MultipleLibraryInstallDetected struct {
819+
LibName string
820+
LibsDir paths.PathList
821+
Message string
822+
}
823+
824+
func (e *MultipleLibraryInstallDetected) Error() string {
825+
res := tr("The library %s has multiple installations:", e.LibName) + "\n"
826+
for _, lib := range e.LibsDir {
827+
res += fmt.Sprintf("- %s\n", lib)
828+
}
829+
res += e.Message
830+
return res
831+
}
832+
833+
// ToRPCStatus converts the error into a *status.Status
834+
func (e *MultipleLibraryInstallDetected) ToRPCStatus() *status.Status {
835+
return status.New(codes.InvalidArgument, e.Error())
836+
}

Diff for: arduino/libraries/libraries.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ type Library struct {
6363
Types []string `json:"types,omitempty"`
6464

6565
InstallDir *paths.Path
66+
CanonicalName string
6667
SourceDir *paths.Path
6768
UtilityDir *paths.Path
6869
Location LibraryLocation
6970
ContainerPlatform *cores.PlatformRelease `json:""`
7071
Layout LibraryLayout
71-
RealName string
7272
DotALinkage bool
7373
Precompiled bool
7474
PrecompiledWithSources bool
@@ -132,7 +132,7 @@ func (library *Library) ToRPCLibrary() (*rpc.Library, error) {
132132
Location: library.Location.ToRPCLibraryLocation(),
133133
ContainerPlatform: platformOrEmpty(library.ContainerPlatform),
134134
Layout: library.Layout.ToRPCLibraryLayout(),
135-
RealName: library.RealName,
135+
RealName: library.Name,
136136
DotALinkage: library.DotALinkage,
137137
Precompiled: library.Precompiled,
138138
LdFlags: library.LDflags,

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func (idx *Index) FindRelease(ref *Reference) *Release {
125125
// FindIndexedLibrary search an indexed library that matches the provided
126126
// installed library or nil if not found
127127
func (idx *Index) FindIndexedLibrary(lib *libraries.Library) *Library {
128-
return idx.Libraries[lib.RealName]
128+
return idx.Libraries[lib.Name]
129129
}
130130

131131
// FindLibraryUpdate check if an installed library may be updated using

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -72,22 +72,22 @@ func TestIndexer(t *testing.T) {
7272
})
7373
require.Nil(t, rtcInexistent)
7474

75-
rtc := index.FindIndexedLibrary(&libraries.Library{RealName: "RTCZero"})
75+
rtc := index.FindIndexedLibrary(&libraries.Library{Name: "RTCZero"})
7676
require.NotNil(t, rtc)
7777
require.Equal(t, "RTCZero", rtc.Name)
7878

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

83-
rtcUpdateNoVersion := index.FindLibraryUpdate(&libraries.Library{RealName: "RTCZero", Version: nil})
83+
rtcUpdateNoVersion := index.FindLibraryUpdate(&libraries.Library{Name: "RTCZero", Version: nil})
8484
require.NotNil(t, rtcUpdateNoVersion)
8585
require.Equal(t, "[email protected]", rtcUpdateNoVersion.String())
8686

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

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

9393
resolve1 := index.ResolveDependencies(alp.Releases["1.2.1"])

Diff for: arduino/libraries/librarieslist.go

+28
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ package libraries
1717

1818
import (
1919
"sort"
20+
21+
semver "go.bug.st/relaxed-semver"
2022
)
2123

2224
// List is a list of Libraries
@@ -39,6 +41,16 @@ func (list *List) Add(libs ...*Library) {
3941
}
4042
}
4143

44+
// Remove removes the library from the list
45+
func (list *List) Remove(library *Library) {
46+
for i, lib := range *list {
47+
if lib == library {
48+
*list = append((*list)[:i], (*list)[i+1:]...)
49+
return
50+
}
51+
}
52+
}
53+
4254
// FindByName returns the first library in the list that match
4355
// the specified name or nil if not found
4456
func (list *List) FindByName(name string) *Library {
@@ -50,6 +62,22 @@ func (list *List) FindByName(name string) *Library {
5062
return nil
5163
}
5264

65+
// FilterByVersionAndInstallLocation returns the libraries matching the provided version and install location. If version
66+
// is nil all version are matched.
67+
func (list *List) FilterByVersionAndInstallLocation(version *semver.Version, installLocation LibraryLocation) List {
68+
var found List
69+
for _, lib := range *list {
70+
if lib.Location != installLocation {
71+
continue
72+
}
73+
if version != nil && !lib.Version.Equal(version) {
74+
continue
75+
}
76+
found.Add(lib)
77+
}
78+
return found
79+
}
80+
5381
// SortByName sorts the libraries by name
5482
func (list *List) SortByName() {
5583
sort.Slice(*list, func(i, j int) bool {

Diff for: arduino/libraries/librariesmanager/install.go

+41-30
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"os"
2323
"strings"
2424

25+
"github.com/arduino/arduino-cli/arduino"
2526
"github.com/arduino/arduino-cli/arduino/globals"
2627
"github.com/arduino/arduino-cli/arduino/libraries"
2728
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
@@ -49,45 +50,51 @@ var (
4950
// install path, where the library should be installed and the possible library that is already
5051
// installed on the same folder and it's going to be replaced by the new one.
5152
func (lm *LibrariesManager) InstallPrerequisiteCheck(indexLibrary *librariesindex.Release, installLocation libraries.LibraryLocation) (*paths.Path, *libraries.Library, error) {
52-
saneName := utils.SanitizeName(indexLibrary.Library.Name)
53+
installDir := lm.getLibrariesDir(installLocation)
54+
if installDir == nil {
55+
if installLocation == libraries.User {
56+
return nil, nil, fmt.Errorf(tr("User directory not set"))
57+
}
58+
return nil, nil, fmt.Errorf(tr("Builtin libraries directory not set"))
59+
}
5360

54-
var replaced *libraries.Library
55-
if installedLibs, have := lm.Libraries[saneName]; have {
56-
for _, installedLib := range installedLibs.Alternatives {
57-
if installedLib.Location != installLocation {
58-
continue
59-
}
60-
if installedLib.Version != nil && installedLib.Version.Equal(indexLibrary.Version) {
61-
return installedLib.InstallDir, nil, ErrAlreadyInstalled
62-
}
63-
replaced = installedLib
61+
name := indexLibrary.Library.Name
62+
libs := lm.FindByReference(&librariesindex.Reference{Name: name}, installLocation)
63+
for _, lib := range libs {
64+
if lib.Version != nil && lib.Version.Equal(indexLibrary.Version) {
65+
return lib.InstallDir, nil, ErrAlreadyInstalled
6466
}
6567
}
6668

67-
libsDir := lm.getLibrariesDir(installLocation)
68-
if libsDir == nil {
69-
if installLocation == libraries.User {
70-
return nil, nil, fmt.Errorf(tr("User directory not set"))
69+
if len(libs) > 1 {
70+
libsDir := paths.NewPathList()
71+
for _, lib := range libs {
72+
libsDir.Add(lib.InstallDir)
73+
}
74+
return nil, nil, &arduino.MultipleLibraryInstallDetected{
75+
LibName: name,
76+
LibsDir: libsDir,
77+
Message: tr("Automatic library install can't be performed in this case, please manually remove all duplicates and retry."),
7178
}
72-
return nil, nil, fmt.Errorf(tr("Builtin libraries directory not set"))
7379
}
7480

75-
libPath := libsDir.Join(saneName)
76-
if replaced != nil && replaced.InstallDir.EquivalentTo(libPath) {
81+
var replaced *libraries.Library
82+
if len(libs) == 1 {
83+
replaced = libs[0]
84+
}
7785

86+
libPath := installDir.Join(utils.SanitizeName(indexLibrary.Library.Name))
87+
if replaced != nil && replaced.InstallDir.EquivalentTo(libPath) {
88+
return libPath, replaced, nil
7889
} else if libPath.IsDir() {
7990
return nil, nil, fmt.Errorf(tr("destination dir %s already exists, cannot install"), libPath)
8091
}
8192
return libPath, replaced, nil
8293
}
8394

8495
// Install installs a library on the specified path.
85-
func (lm *LibrariesManager) Install(indexLibrary *librariesindex.Release, libPath *paths.Path, installLocation libraries.LibraryLocation) error {
86-
libsDir := lm.getLibrariesDir(installLocation)
87-
if libsDir == nil {
88-
return fmt.Errorf(tr("User directory not set"))
89-
}
90-
return indexLibrary.Resource.Install(lm.DownloadsDir, libsDir, libPath)
96+
func (lm *LibrariesManager) Install(indexLibrary *librariesindex.Release, libPath *paths.Path) error {
97+
return indexLibrary.Resource.Install(lm.DownloadsDir, libPath.Parent(), libPath)
9198
}
9299

93100
// Uninstall removes a Library
@@ -99,7 +106,9 @@ func (lm *LibrariesManager) Uninstall(lib *libraries.Library) error {
99106
return fmt.Errorf(tr("removing lib directory: %s"), err)
100107
}
101108

102-
lm.Libraries[lib.Name].Remove(lib)
109+
alternatives := lm.Libraries[lib.Name]
110+
alternatives.Remove(lib)
111+
lm.Libraries[lib.Name] = alternatives
103112
return nil
104113
}
105114

@@ -189,8 +198,8 @@ func (lm *LibrariesManager) InstallZipLib(ctx context.Context, archivePath strin
189198

190199
// InstallGitLib installs a library hosted on a git repository on the specified path.
191200
func (lm *LibrariesManager) InstallGitLib(gitURL string, overwrite bool) error {
192-
libsDir := lm.getLibrariesDir(libraries.User)
193-
if libsDir == nil {
201+
installDir := lm.getLibrariesDir(libraries.User)
202+
if installDir == nil {
194203
return fmt.Errorf(tr("User directory not set"))
195204
}
196205

@@ -202,10 +211,9 @@ func (lm *LibrariesManager) InstallGitLib(gitURL string, overwrite bool) error {
202211
return err
203212
}
204213

205-
installPath := libsDir.Join(libraryName)
206-
207214
// Deletes libraries folder if already installed
208-
if _, ok := lm.Libraries[libraryName]; ok {
215+
installPath := installDir.Join(libraryName)
216+
if installPath.IsDir() {
209217
if !overwrite {
210218
return fmt.Errorf(tr("library %s already installed"), libraryName)
211219
}
@@ -215,6 +223,9 @@ func (lm *LibrariesManager) InstallGitLib(gitURL string, overwrite bool) error {
215223
Trace("Deleting library")
216224
installPath.RemoveAll()
217225
}
226+
if installPath.Exist() {
227+
return fmt.Errorf(tr("could not create directory %s: a file with the same name exists!", installPath))
228+
}
218229

219230
logrus.
220231
WithField("library name", libraryName).

0 commit comments

Comments
 (0)