diff --git a/internal/arduino/libraries/libraries.go b/internal/arduino/libraries/libraries.go index 9ffa1e27eea..7c2cb39fdca 100644 --- a/internal/arduino/libraries/libraries.go +++ b/internal/arduino/libraries/libraries.go @@ -16,8 +16,11 @@ package libraries import ( + "encoding/binary" "errors" "fmt" + "io" + "path/filepath" "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/globals" @@ -90,6 +93,379 @@ func (library *Library) String() string { return library.Name + "@" + library.Version.String() } +func (library *Library) MarshalBinary(out io.Writer, prefix *paths.Path) error { + writeString := func(in string) error { + inBytes := []byte(in) + if err := binary.Write(out, binary.NativeEndian, uint16(len(inBytes))); err != nil { + return err + } + _, err := out.Write(inBytes) + return err + } + writeStringArray := func(in []string) error { + if err := binary.Write(out, binary.NativeEndian, uint16(len(in))); err != nil { + return err + } + for _, i := range in { + if err := writeString(i); err != nil { + return err + } + } + return nil + } + writeMap := func(in map[string]bool) error { + if err := binary.Write(out, binary.NativeEndian, uint16(len(in))); err != nil { + return err + } + for k, v := range in { + if err := writeString(k); err != nil { + return err + } + if err := binary.Write(out, binary.NativeEndian, v); err != nil { + return err + } + } + return nil + } + writeProperties := func(in *properties.Map) error { + var keys []string + if in != nil { + keys = in.Keys() + } + if err := binary.Write(out, binary.NativeEndian, uint16(len(keys))); err != nil { + return err + } + for _, k := range keys { + v := in.Get(k) + if err := writeString(k); err != nil { + return err + } + if err := writeString(v); err != nil { + return err + } + } + return nil + } + writePath := func(in *paths.Path) error { + if in == nil { + return writeString("") + } else if inside, err := in.IsInsideDir(prefix); err != nil { + return err + } else if !inside { + return writeString(in.String()) + } else if p, err := in.RelFrom(prefix); err != nil { + return err + } else { + return writeString(p.String()) + } + } + writePathList := func(in []*paths.Path) error { + if err := binary.Write(out, binary.NativeEndian, uint16(len(in))); err != nil { + return err + } + for _, p := range in { + if err := writePath(p); err != nil { + return err + } + } + return nil + } + if err := writeString(library.Name); err != nil { + return err + } + if err := writeString(library.Author); err != nil { + return err + } + if err := writeString(library.Maintainer); err != nil { + return err + } + if err := writeString(library.Sentence); err != nil { + return err + } + if err := writeString(library.Paragraph); err != nil { + return err + } + if err := writeString(library.Website); err != nil { + return err + } + if err := writeString(library.Category); err != nil { + return err + } + if err := writeStringArray(library.Architectures); err != nil { + return err + } + if err := writeStringArray(library.Types); err != nil { + return err + } + if err := writePath(library.InstallDir); err != nil { + return err + } + if err := writeString(library.DirName); err != nil { + return err + } + if err := writePath(library.SourceDir); err != nil { + return err + } + if err := writePath(library.UtilityDir); err != nil { + return err + } + if err := binary.Write(out, binary.NativeEndian, int32(library.Location)); err != nil { + return err + } + // library.ContainerPlatform *cores.PlatformRelease `json:""` + if err := binary.Write(out, binary.NativeEndian, int32(library.Layout)); err != nil { + return err + } + if err := binary.Write(out, binary.NativeEndian, library.DotALinkage); err != nil { + return err + } + if err := binary.Write(out, binary.NativeEndian, library.Precompiled); err != nil { + return err + } + if err := binary.Write(out, binary.NativeEndian, library.PrecompiledWithSources); err != nil { + return err + } + if err := writeString(library.LDflags); err != nil { + return err + } + if err := binary.Write(out, binary.NativeEndian, library.IsLegacy); err != nil { + return err + } + if err := binary.Write(out, binary.NativeEndian, library.InDevelopment); err != nil { + return err + } + if err := writeString(library.Version.String()); err != nil { + return err + } + if err := writeString(library.License); err != nil { + return err + } + if err := writePathList(library.Examples); err != nil { + return err + } + if err := writeStringArray(library.declaredHeaders); err != nil { + return err + } + if err := writeStringArray(library.sourceHeaders); err != nil { + return err + } + if err := writeMap(library.CompatibleWith); err != nil { + return err + } + if err := writeProperties(library.Properties); err != nil { + return err + } + + return nil +} + +func (library *Library) UnmarshalBinary(in io.Reader, prefix *paths.Path) error { + readString := func() (string, error) { + var len uint16 + if err := binary.Read(in, binary.NativeEndian, &len); err != nil { + return "", err + } + res := make([]byte, len) + if _, err := in.Read(res); err != nil { + return "", err + } + return string(res), nil + } + readStringArray := func() ([]string, error) { + var len uint16 + if err := binary.Read(in, binary.NativeEndian, &len); err != nil { + return nil, err + } + if len == 0 { + return nil, nil + } + res := make([]string, len) + for i := range res { + var err error + res[i], err = readString() + if err != nil { + return nil, err + } + } + return res, nil + } + readMap := func() (map[string]bool, error) { + var len uint16 + if err := binary.Read(in, binary.NativeEndian, &len); err != nil { + return nil, err + } + res := map[string]bool{} + for range len { + k, err := readString() + if err != nil { + return nil, err + } + var v bool + if err := binary.Read(in, binary.NativeEndian, &v); err != nil { + return nil, err + } + res[k] = v + } + return res, nil + } + readPath := func() (*paths.Path, error) { + if p, err := readString(); err != nil { + return nil, err + } else if p == "" { + return nil, nil + } else if filepath.IsAbs(p) { + return paths.New(p), nil + } else { + return prefix.Join(p), nil + } + } + readPathList := func() (paths.PathList, error) { + var len uint16 + if err := binary.Read(in, binary.NativeEndian, &len); err != nil { + return nil, err + } + list := paths.NewPathList() + for range len { + if p, err := readPath(); err != nil { + return nil, err + } else { + list.Add(p) + } + } + return list, nil + } + readProperties := func() (*properties.Map, error) { + var len uint16 + if err := binary.Read(in, binary.NativeEndian, &len); err != nil { + return nil, err + } + props := properties.NewMap() + for range len { + if k, err := readString(); err != nil { + return nil, err + } else if v, err := readString(); err != nil { + return nil, err + } else { + props.Set(k, v) + } + } + return props, nil + } + var err error + library.Name, err = readString() + if err != nil { + return err + } + library.Author, err = readString() + if err != nil { + return err + } + library.Maintainer, err = readString() + if err != nil { + return err + } + library.Sentence, err = readString() + if err != nil { + return err + } + library.Paragraph, err = readString() + if err != nil { + return err + } + library.Website, err = readString() + if err != nil { + return err + } + library.Category, err = readString() + if err != nil { + return err + } + library.Architectures, err = readStringArray() + if err != nil { + return err + } + library.Types, err = readStringArray() + if err != nil { + return err + } + library.InstallDir, err = readPath() + if err != nil { + return err + } + library.DirName, err = readString() + if err != nil { + return err + } + library.SourceDir, err = readPath() + if err != nil { + return err + } + library.UtilityDir, err = readPath() + if err != nil { + return err + } + var location int32 + if err := binary.Read(in, binary.NativeEndian, &location); err != nil { + return err + } + library.Location = LibraryLocation(location) + // library.ContainerPlatform *cores.PlatformRelease `json:""` + var layout int32 + if err := binary.Read(in, binary.NativeEndian, &layout); err != nil { + return err + } + library.Layout = LibraryLayout(layout) + if err := binary.Read(in, binary.NativeEndian, &library.DotALinkage); err != nil { + return err + } + if err := binary.Read(in, binary.NativeEndian, &library.Precompiled); err != nil { + return err + } + if err := binary.Read(in, binary.NativeEndian, &library.PrecompiledWithSources); err != nil { + return err + } + library.LDflags, err = readString() + if err != nil { + return err + } + if err := binary.Read(in, binary.NativeEndian, &library.IsLegacy); err != nil { + return err + } + if err := binary.Read(in, binary.NativeEndian, &library.InDevelopment); err != nil { + return err + } + version, err := readString() + if err != nil { + return err + } + library.Version = semver.MustParse(version) + library.License, err = readString() + if err != nil { + return err + } + library.Examples, err = readPathList() + if err != nil { + return err + } + library.declaredHeaders, err = readStringArray() + if err != nil { + return err + } + library.sourceHeaders, err = readStringArray() + if err != nil { + return err + } + library.CompatibleWith, err = readMap() + if err != nil { + return err + } + library.Properties, err = readProperties() + if err != nil { + return err + } + return nil +} + // ToRPCLibrary converts this library into an rpc.Library func (library *Library) ToRPCLibrary() (*rpc.Library, error) { pathOrEmpty := func(p *paths.Path) string { diff --git a/internal/arduino/libraries/librarieslist.go b/internal/arduino/libraries/librarieslist.go index 84062203b76..3043ed32f72 100644 --- a/internal/arduino/libraries/librarieslist.go +++ b/internal/arduino/libraries/librarieslist.go @@ -16,8 +16,13 @@ package libraries import ( + "encoding/binary" + "errors" + "fmt" + "io" "sort" + "github.com/arduino/go-paths-helper" semver "go.bug.st/relaxed-semver" ) @@ -41,6 +46,49 @@ func (list *List) Add(libs ...*Library) { } } +func (list *List) binaryMagicNumber() uint32 { + return 0xAD000001 +} + +func (list *List) UnmarshalBinary(in io.Reader, prefix *paths.Path) error { + var magic uint32 + if err := binary.Read(in, binary.NativeEndian, &magic); err != nil { + return err + } + if magic != list.binaryMagicNumber() { + return errors.New("invalid cache version") + } + var n int32 + if err := binary.Read(in, binary.NativeEndian, &n); err != nil { + return err + } + res := make([]*Library, n) + for i := range res { + var lib Library + if err := lib.UnmarshalBinary(in, prefix); err != nil { + return err + } + res[i] = &lib + } + *list = res + return nil +} + +func (list *List) MarshalBinary(out io.Writer, prefix *paths.Path) error { + if err := binary.Write(out, binary.NativeEndian, list.binaryMagicNumber()); err != nil { + return err + } + if err := binary.Write(out, binary.NativeEndian, int32(len(*list))); err != nil { + return err + } + for _, lib := range *list { + if err := lib.MarshalBinary(out, prefix); err != nil { + return fmt.Errorf("could not encode lib data of %s: %w", lib.InstallDir.String(), err) + } + } + return nil +} + // Remove removes the given library from the list func (list *List) Remove(libraryToRemove *Library) { for i, lib := range *list { diff --git a/internal/arduino/libraries/librariesmanager/librariesmanager.go b/internal/arduino/libraries/librariesmanager/librariesmanager.go index 457d20b32ff..53875e48cc3 100644 --- a/internal/arduino/libraries/librariesmanager/librariesmanager.go +++ b/internal/arduino/libraries/librariesmanager/librariesmanager.go @@ -201,30 +201,72 @@ func (lm *LibrariesManager) loadLibrariesFromDir(librariesDir *LibrariesDir) []* librariesDir.scanned = true - var libDirs paths.PathList - if librariesDir.IsSingleLibrary { - libDirs.Add(librariesDir.Path) - } else { - d, err := librariesDir.Path.ReadDir() - if os.IsNotExist(err) { - return statuses - } + var loadedLibs libraries.List + if cacheFilePath := librariesDir.Path.Join("libraries-loader-cache"); cacheFilePath.Exist() { + logrus.WithField("file", cacheFilePath).Info("Using library cache") + + // Load lib cache + cache, err := cacheFilePath.Open() if err != nil { - s := status.New(codes.FailedPrecondition, i18n.Tr("reading dir %[1]s: %[2]s", librariesDir.Path, err)) + s := status.Newf(codes.FailedPrecondition, "reading lib cache %[1]s: %[2]s", cacheFilePath, err) return append(statuses, s) } - d.FilterDirs() - d.FilterOutHiddenFiles() - libDirs = d - } + defer cache.Close() - for _, libDir := range libDirs { - library, err := libraries.Load(libDir, librariesDir.Location) - if err != nil { - s := status.New(codes.Internal, i18n.Tr("loading library from %[1]s: %[2]s", libDir, err)) - statuses = append(statuses, s) - continue + if err := loadedLibs.UnmarshalBinary(cache, librariesDir.Path); err != nil { + s := status.Newf(codes.FailedPrecondition, "reading lib cache %[1]s: %[2]s", cacheFilePath, err) + return append(statuses, s) } + } else { + var libDirs paths.PathList + if librariesDir.IsSingleLibrary { + libDirs.Add(librariesDir.Path) + } else { + d, err := librariesDir.Path.ReadDir() + if os.IsNotExist(err) { + return statuses + } + if err != nil { + s := status.Newf(codes.FailedPrecondition, i18n.Tr("reading dir %[1]s: %[2]s", librariesDir.Path, err)) + return append(statuses, s) + } + d.FilterDirs() + d.FilterOutHiddenFiles() + libDirs = d + } + + for _, libDir := range libDirs { + library, err := libraries.Load(libDir, librariesDir.Location) + if err != nil { + s := status.Newf(codes.Internal, i18n.Tr("loading library from %[1]s: %[2]s", libDir, err)) + statuses = append(statuses, s) + continue + } + loadedLibs = append(loadedLibs, library) + } + + // Preload source files and header + for _, lib := range loadedLibs { + lib.SourceHeaders() + } + if librariesDir.Location != libraries.Unmanaged { + // Write lib cache + cache, err := cacheFilePath.Create() + if err != nil { + s := status.Newf(codes.FailedPrecondition, "creating lib cache %[1]s: %[2]s", cacheFilePath, err) + return append(statuses, s) + } + err = loadedLibs.MarshalBinary(cache, librariesDir.Path) + cache.Close() + if err != nil { + cacheFilePath.Remove() + s := status.Newf(codes.FailedPrecondition, "writing lib cache %[1]s: %[2]s", cacheFilePath, err) + return append(statuses, s) + } + } + } + + for _, library := range loadedLibs { library.ContainerPlatform = librariesDir.PlatformRelease alternatives := lm.libraries[library.Name] alternatives.Add(library) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy