Skip to content

Commit

Permalink
[skip-changelog] refactoring: Added LibrariesManager.Clone() / Auto…
Browse files Browse the repository at this point in the history
…-scan libs on `LibrariesManager.Build()` (#2491)

* Added LibrariesManager.Clone() / Auto-scan libraries on LibrariesManager.Build()

* Ensure AddLibrariesDir do not share input parameters

* Fixed wrong loop... ooops
  • Loading branch information
cmaglie authored Jan 11, 2024
1 parent 2972ed8 commit 51573f2
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 54 deletions.
21 changes: 8 additions & 13 deletions commands/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
for _, pack := range pme.GetPackages() {
for _, platform := range pack.Platforms {
if platformRelease := pme.GetInstalledPlatformRelease(platform); platformRelease != nil {
lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
PlatformRelease: platformRelease,
Path: platformRelease.GetLibrariesDir(),
Location: libraries.PlatformBuiltIn,
Expand Down Expand Up @@ -336,14 +336,14 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
if profile == nil {
// Add directories of libraries bundled with IDE
if bundledLibsDir := configuration.IDEBuiltinLibrariesDir(configuration.Settings); bundledLibsDir != nil {
lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
Path: bundledLibsDir,
Location: libraries.IDEBuiltIn,
})
}

// Add libraries directory from config file
lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
Path: configuration.LibrariesDir(configuration.Settings),
Location: libraries.User,
})
Expand Down Expand Up @@ -383,23 +383,18 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
taskCallback(&rpc.TaskProgress{Completed: true})
}

lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
Path: libRoot,
Location: libraries.User,
})
}
}

lm := lmb.Build()
lm, libsLoadingWarnings := lmb.Build()
_ = instances.SetLibraryManager(instance, lm) // should never fail

{
lmi, release := lm.NewInstaller()
for _, status := range lmi.RescanLibraries() {
logrus.WithError(status.Err()).Warnf("Error loading library")
// TODO: report as warning: responseError(err)
}
release()
for _, status := range libsLoadingWarnings {
logrus.WithError(status.Err()).Warnf("Error loading library")
// TODO: report as warning: responseError(err)
}

// Refreshes the locale used, this will change the
Expand Down
7 changes: 5 additions & 2 deletions commands/internal/instances/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,12 @@ func Create(dataDir, packagesDir, downloadsDir *paths.Path, extraUserAgent ...st
}
tempDir := dataDir.Join("tmp")

pm := packagemanager.NewBuilder(dataDir, packagesDir, downloadsDir, tempDir, userAgent).Build()
lm, _ := librariesmanager.NewBuilder().Build()

instance := &coreInstance{
pm: packagemanager.NewBuilder(dataDir, packagesDir, downloadsDir, tempDir, userAgent).Build(),
lm: librariesmanager.NewBuilder().Build(),
pm: pm,
lm: lm,
li: librariesindex.EmptyIndex,
}

Expand Down
36 changes: 16 additions & 20 deletions internal/arduino/builder/internal/detector/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ func LibrariesLoader(
if useCachedLibrariesResolution {
// Since we are using the cached libraries resolution
// the library manager is not needed.
lm = librariesmanager.NewBuilder().Build()
lm, _ = librariesmanager.NewBuilder().Build()
}
if librariesManager == nil {
lmb := librariesmanager.NewBuilder()
Expand All @@ -608,20 +608,20 @@ func LibrariesLoader(
if err := builtInLibrariesFolders.ToAbs(); err != nil {
return nil, nil, nil, err
}
lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
Path: builtInLibrariesFolders,
Location: libraries.IDEBuiltIn,
})
}

if actualPlatform != targetPlatform {
lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
PlatformRelease: actualPlatform,
Path: actualPlatform.GetLibrariesDir(),
Location: libraries.ReferencedPlatformBuiltIn,
})
}
lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
PlatformRelease: targetPlatform,
Path: targetPlatform.GetLibrariesDir(),
Location: libraries.PlatformBuiltIn,
Expand All @@ -632,35 +632,31 @@ func LibrariesLoader(
return nil, nil, nil, err
}
for _, folder := range librariesFolders {
lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
Path: folder,
Location: libraries.User, // XXX: Should be libraries.Unmanaged?
})
}

for _, dir := range libraryDirs {
lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
Path: dir,
Location: libraries.Unmanaged,
IsSingleLibrary: true,
})
}

lm = lmb.Build()

{
lmi, release := lm.NewInstaller()
for _, status := range lmi.RescanLibraries() {
// With the refactoring of the initialization step of the CLI we changed how
// errors are returned when loading platforms and libraries, that meant returning a list of
// errors instead of a single one to enhance the experience for the user.
// I have no intention right now to start a refactoring of the legacy package too, so
// here's this shitty solution for now.
// When we're gonna refactor the legacy package this will be gone.
verboseOut.Write([]byte(status.Message()))
}
release()
newLm, libsLoadingWarnings := lmb.Build()
for _, status := range libsLoadingWarnings {
// With the refactoring of the initialization step of the CLI we changed how
// errors are returned when loading platforms and libraries, that meant returning a list of
// errors instead of a single one to enhance the experience for the user.
// I have no intention right now to start a refactoring of the legacy package too, so
// here's this shitty solution for now.
// When we're gonna refactor the legacy package this will be gone.
verboseOut.Write([]byte(status.Message()))
}
lm = newLm
}

allLibs := lm.FindAllInstalled()
Expand Down
42 changes: 29 additions & 13 deletions internal/arduino/libraries/librariesmanager/librariesmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type LibrariesDir struct {
Location libraries.LibraryLocation
PlatformRelease *cores.PlatformRelease
IsSingleLibrary bool // true if Path points directly to a library instad of a dir of libraries
scanned bool
}

var tr = i18n.Tr
Expand Down Expand Up @@ -95,14 +96,19 @@ func NewBuilder() *Builder {
}
}

// NewBuilder creates a Builder with the same configuration of this
// LibrariesManager. A "commit" function callback is returned: calling
// this function will write the new configuration into this LibrariesManager.
func (lm *LibrariesManager) NewBuilder() (*Builder, func()) {
// Clone creates a Builder starting with a copy of the same configuration
// of this LibrariesManager. At the moment of the Build() only the added
// libraries directories will be scanned, keeping the existing directories
// "cached" to optimize scan. If you need to do a full rescan you must use
// the RescanLibraries method of the Installer.
func (lm *LibrariesManager) Clone() *Builder {
lmb := NewBuilder()
return lmb, func() {
lmb.BuildIntoExistingLibrariesManager(lm)
lmb.librariesDir = append(lmb.librariesDir, lm.librariesDir...)
for libName, libAlternatives := range lm.libraries {
// TODO: Maybe we should deep clone libAlternatives...
lmb.libraries[libName] = append(lmb.libraries[libName], libAlternatives...)
}
return lmb
}

// NewExplorer returns a new Explorer. The returned function must be called
Expand All @@ -120,10 +126,18 @@ func (lm *LibrariesManager) NewInstaller() (*Installer, func()) {
}

// Build builds a new LibrariesManager.
func (lmb *Builder) Build() *LibrariesManager {
func (lmb *Builder) Build() (*LibrariesManager, []*status.Status) {
var statuses []*status.Status
res := &LibrariesManager{}
for _, dir := range lmb.librariesDir {
if !dir.scanned {
if errs := lmb.loadLibrariesFromDir(dir); len(errs) > 0 {
statuses = append(statuses, errs...)
}
}
}
lmb.BuildIntoExistingLibrariesManager(res)
return res
return res, statuses
}

// BuildIntoExistingLibrariesManager will overwrite the given LibrariesManager instead
Expand All @@ -138,7 +152,7 @@ func (lmb *Builder) BuildIntoExistingLibrariesManager(old *LibrariesManager) {
// AddLibrariesDir adds path to the list of directories
// to scan when searching for libraries. If a path is already
// in the list it is ignored.
func (lmb *Builder) AddLibrariesDir(libDir *LibrariesDir) {
func (lmb *Builder) AddLibrariesDir(libDir LibrariesDir) {
if libDir.Path == nil {
return
}
Expand All @@ -151,7 +165,7 @@ func (lmb *Builder) AddLibrariesDir(libDir *LibrariesDir) {
WithField("location", libDir.Location.String()).
WithField("isSingleLibrary", libDir.IsSingleLibrary).
Info("Adding libraries dir")
lmb.librariesDir = append(lmb.librariesDir, libDir)
lmb.librariesDir = append(lmb.librariesDir, &libDir)
}

// RescanLibraries reload all installed libraries in the system.
Expand Down Expand Up @@ -184,9 +198,11 @@ func (lm *LibrariesManager) getLibrariesDir(installLocation libraries.LibraryLoc

// loadLibrariesFromDir loads all libraries in the given directory. Returns
// nil if the directory doesn't exists.
func (lmi *Installer) loadLibrariesFromDir(librariesDir *LibrariesDir) []*status.Status {
func (lm *LibrariesManager) loadLibrariesFromDir(librariesDir *LibrariesDir) []*status.Status {
statuses := []*status.Status{}

librariesDir.scanned = true

var libDirs paths.PathList
if librariesDir.IsSingleLibrary {
libDirs.Add(librariesDir.Path)
Expand All @@ -212,9 +228,9 @@ func (lmi *Installer) loadLibrariesFromDir(librariesDir *LibrariesDir) []*status
continue
}
library.ContainerPlatform = librariesDir.PlatformRelease
alternatives := lmi.libraries[library.Name]
alternatives := lm.libraries[library.Name]
alternatives.Add(library)
lmi.libraries[library.Name] = alternatives
lm.libraries[library.Name] = alternatives
}

return statuses
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,25 @@ import (
"github.com/stretchr/testify/require"
)

func Test_RescanLibrariesCallClear(t *testing.T) {
func TestLibrariesBuilderScanCloneRescan(t *testing.T) {
lmb := NewBuilder()
lmb.libraries["testLibA"] = libraries.List{}
lmb.libraries["testLibB"] = libraries.List{}
lm := lmb.Build()
lm, warns := lmb.Build()
require.Empty(t, warns)
require.Len(t, lm.libraries, 2)

// Cloning should keep existing libraries
lm2, warns2 := lm.Clone().Build()
require.Empty(t, warns2)
require.Len(t, lm2.libraries, 2)

// Full rescan should update libs
{
lmi, release := lm.NewInstaller()
lmi.RescanLibraries()
lmi2, release := lm2.NewInstaller()
lmi2.RescanLibraries()
release()
}

require.Len(t, lm.libraries, 0)
require.Len(t, lm.libraries, 2) // Ensure deep-coping worked as expected...
require.Len(t, lm2.libraries, 0)
}

0 comments on commit 51573f2

Please sign in to comment.