diff --git a/arduino/cores/fqbn.go b/arduino/cores/fqbn.go index 35f48c2207e..a6159c600c6 100644 --- a/arduino/cores/fqbn.go +++ b/arduino/cores/fqbn.go @@ -110,3 +110,40 @@ func (fqbn *FQBN) Match(target *FQBN) bool { func (fqbn *FQBN) StringWithoutConfig() string { return fqbn.Package + ":" + fqbn.PlatformArch + ":" + fqbn.BoardID } + +// FQBNMatcher contains a pattern to match an FQBN +type FQBNMatcher struct { + Package string + PlatformArch string + BoardID string +} + +// ParseFQBNMatcher parse a formula for an FQBN pattern and returns the corresponding +// FQBNMatcher. In the formula is allowed the glob char `*`. The formula must contains +// the triple `PACKAGE:ARCHITECTURE:BOARDID`, some exaples are: +// - `arduino:avr:uno` +// - `*:avr:*` +// - `arduino:avr:mega*` +func ParseFQBNMatcher(formula string) (*FQBNMatcher, error) { + parts := strings.Split(strings.TrimSpace(formula), ":") + if len(parts) < 3 || len(parts) > 4 { + return nil, fmt.Errorf("invalid formula: %s", formula) + } + return &FQBNMatcher{ + Package: parts[0], + PlatformArch: parts[1], + BoardID: parts[2], + }, nil +} + +// Match checks if this FQBNMatcher matches the given fqbn +func (m *FQBNMatcher) Match(fqbn *FQBN) bool { + // TODO: allow in-fix syntax like `*name` + return (m.Package == fqbn.Package || m.Package == "*") && + (m.PlatformArch == fqbn.PlatformArch || m.PlatformArch == "*") && + (m.BoardID == fqbn.BoardID || m.BoardID == "*") +} + +func (m *FQBNMatcher) String() string { + return m.Package + "." + m.PlatformArch + ":" + m.BoardID +} diff --git a/arduino/libraries/libraries.go b/arduino/libraries/libraries.go index 186142360c6..4f308abcaa1 100644 --- a/arduino/libraries/libraries.go +++ b/arduino/libraries/libraries.go @@ -59,6 +59,7 @@ type Library struct { Website string Category string Architectures []string + SupportedFQBN []*cores.FQBNMatcher Types []string `json:"types,omitempty"` @@ -182,10 +183,21 @@ func (library *Library) IsArchitectureIndependent() bool { } // IsCompatibleWith returns true if the library declares compatibility with -// the given architecture. If this function returns false, the library may still -// be compatible with the given architecture, but it's not explicitly declared. -func (library *Library) IsCompatibleWith(arch string) bool { - return library.IsArchitectureIndependent() || library.IsOptimizedForArchitecture(arch) +// the given FQBN. If this function returns false, the library may still +// be compatible with the given FQBN, but it's not explicitly declared. +func (library *Library) IsCompatibleWith(fqbn *cores.FQBN) bool { + // If the library does not specify compatibility use "architecture" field + if len(library.SupportedFQBN) == 0 { + return library.IsArchitectureIndependent() || library.IsOptimizedForArchitecture(fqbn.PlatformArch) + } + + // otherwise check if the given FQBN is supported + for _, supported := range library.SupportedFQBN { + if supported.Match(fqbn) { + return true + } + } + return false } // SourceDir represents a source dir of a library diff --git a/arduino/libraries/librariesresolver/cpp.go b/arduino/libraries/librariesresolver/cpp.go index 63da69f2772..f51b35acfbf 100644 --- a/arduino/libraries/librariesresolver/cpp.go +++ b/arduino/libraries/librariesresolver/cpp.go @@ -118,12 +118,12 @@ func (resolver *Cpp) AlternativesFor(header string) libraries.List { // ResolveFor finds the most suitable library for the specified combination of // header and architecture. If no libraries provides the requested header, nil is returned -func (resolver *Cpp) ResolveFor(header, architecture string) *libraries.Library { - logrus.Infof("Resolving include %s for arch %s", header, architecture) +func (resolver *Cpp) ResolveFor(header string, fqbn *cores.FQBN) *libraries.Library { + logrus.Infof("Resolving include %s for %s", header, fqbn) var found libraries.List var foundPriority int for _, lib := range resolver.headers[header] { - libPriority := ComputePriority(lib, header, architecture) + libPriority := ComputePriority(lib, header, fqbn) msg := " discarded" if found == nil || foundPriority < libPriority { found = libraries.List{} @@ -167,7 +167,7 @@ func simplify(name string) string { // ComputePriority returns an integer value representing the priority of the library // for the specified header and architecture. The higher the value, the higher the // priority. -func ComputePriority(lib *libraries.Library, header, arch string) int { +func ComputePriority(lib *libraries.Library, header string, fqbn *cores.FQBN) int { header = strings.TrimSuffix(header, filepath.Ext(header)) header = simplify(header) name := simplify(lib.Name) @@ -175,8 +175,15 @@ func ComputePriority(lib *libraries.Library, header, arch string) int { priority := 0 - // Bonus for core-optimized libraries - if lib.IsOptimizedForArchitecture(arch) { + if lib.IsCompatibleWith(fqbn) { + // Bonus for board-optimized libraries + + // give a bonus for libraries that declares specific compatibliity with a board. + // (it is more important than Location but less important than Name) + priority += 1020 + } else if lib.IsOptimizedForArchitecture(fqbn.PlatformArch) { + // Bonus for core-optimized libraries + // give a slightly better bonus for libraries that have specific optimization // (it is more important than Location but less important than Name) priority += 1010 diff --git a/arduino/libraries/librariesresolver/cpp_test.go b/arduino/libraries/librariesresolver/cpp_test.go index e89ac8305a7..331b1e7b486 100644 --- a/arduino/libraries/librariesresolver/cpp_test.go +++ b/arduino/libraries/librariesresolver/cpp_test.go @@ -18,6 +18,7 @@ package librariesresolver import ( "testing" + "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/libraries" "github.com/stretchr/testify/require" ) @@ -31,12 +32,17 @@ var l6 = &libraries.Library{Name: "Calculus Unified Lib", Location: libraries.Us var l7 = &libraries.Library{Name: "AnotherLib", Location: libraries.User} var bundleServo = &libraries.Library{Name: "Servo", Location: libraries.IDEBuiltIn, Architectures: []string{"avr", "sam", "samd"}} +func mustParseFQBN(fqbn string) *cores.FQBN { + res, _ := cores.ParseFQBN(fqbn) + return res +} + func runResolver(include string, arch string, libs ...*libraries.Library) *libraries.Library { libraryList := libraries.List{} libraryList.Add(libs...) resolver := NewCppResolver() resolver.headers[include] = libraryList - return resolver.ResolveFor(include, arch) + return resolver.ResolveFor(include, mustParseFQBN("x:"+arch+":y")) } func TestArchitecturePriority(t *testing.T) { @@ -96,19 +102,19 @@ func TestClosestMatchWithTotallyDifferentNames(t *testing.T) { libraryList.Add(l7) resolver := NewCppResolver() resolver.headers["XYZ.h"] = libraryList - res := resolver.ResolveFor("XYZ.h", "xyz") + res := resolver.ResolveFor("XYZ.h", mustParseFQBN("arduino:xyz:uno")) require.NotNil(t, res) require.Equal(t, l7, res, "selected library") } func TestCppHeaderPriority(t *testing.T) { - r1 := ComputePriority(l1, "calculus_lib.h", "avr") - r2 := ComputePriority(l2, "calculus_lib.h", "avr") - r3 := ComputePriority(l3, "calculus_lib.h", "avr") - r4 := ComputePriority(l4, "calculus_lib.h", "avr") - r5 := ComputePriority(l5, "calculus_lib.h", "avr") - r6 := ComputePriority(l6, "calculus_lib.h", "avr") - r7 := ComputePriority(l7, "calculus_lib.h", "avr") + r1 := ComputePriority(l1, "calculus_lib.h", mustParseFQBN("arduino:avr:uno")) + r2 := ComputePriority(l2, "calculus_lib.h", mustParseFQBN("arduino:avr:uno")) + r3 := ComputePriority(l3, "calculus_lib.h", mustParseFQBN("arduino:avr:uno")) + r4 := ComputePriority(l4, "calculus_lib.h", mustParseFQBN("arduino:avr:uno")) + r5 := ComputePriority(l5, "calculus_lib.h", mustParseFQBN("arduino:avr:uno")) + r6 := ComputePriority(l6, "calculus_lib.h", mustParseFQBN("arduino:avr:uno")) + r7 := ComputePriority(l7, "calculus_lib.h", mustParseFQBN("arduino:avr:uno")) require.True(t, r1 > r2) require.True(t, r2 > r3) require.True(t, r3 > r4) @@ -122,7 +128,7 @@ func TestCppHeaderResolverWithNilResult(t *testing.T) { libraryList := libraries.List{} libraryList.Add(l1) resolver.headers["aaa.h"] = libraryList - require.Nil(t, resolver.ResolveFor("bbb.h", "avr")) + require.Nil(t, resolver.ResolveFor("bbb.h", mustParseFQBN("arduino:avr:uno"))) } func TestCppHeaderResolver(t *testing.T) { @@ -133,7 +139,7 @@ func TestCppHeaderResolver(t *testing.T) { librarylist.Add(lib) } resolver.headers[header] = librarylist - return resolver.ResolveFor(header, "avr").Name + return resolver.ResolveFor(header, mustParseFQBN("arduino:avr:uno")).Name } require.Equal(t, "Calculus Lib", resolve("calculus_lib.h", l1, l2, l3, l4, l5, l6, l7)) require.Equal(t, "Calculus Lib-master", resolve("calculus_lib.h", l2, l3, l4, l5, l6, l7)) @@ -150,11 +156,11 @@ func TestCppHeaderResolverWithLibrariesInStrangeDirectoryNames(t *testing.T) { librarylist.Add(&libraries.Library{DirName: "onewire_2_3_4", Name: "OneWire", Architectures: []string{"*"}}) librarylist.Add(&libraries.Library{DirName: "onewireng_2_3_4", Name: "OneWireNg", Architectures: []string{"avr"}}) resolver.headers["OneWire.h"] = librarylist - require.Equal(t, "onewire_2_3_4", resolver.ResolveFor("OneWire.h", "avr").DirName) + require.Equal(t, "onewire_2_3_4", resolver.ResolveFor("OneWire.h", mustParseFQBN("arduino:avr:uno")).DirName) librarylist2 := libraries.List{} librarylist2.Add(&libraries.Library{DirName: "OneWire", Name: "OneWire", Architectures: []string{"*"}}) librarylist2.Add(&libraries.Library{DirName: "onewire_2_3_4", Name: "OneWire", Architectures: []string{"avr"}}) resolver.headers["OneWire.h"] = librarylist2 - require.Equal(t, "OneWire", resolver.ResolveFor("OneWire.h", "avr").DirName) + require.Equal(t, "OneWire", resolver.ResolveFor("OneWire.h", mustParseFQBN("arduino:avr:uno")).DirName) } diff --git a/arduino/libraries/loader.go b/arduino/libraries/loader.go index ec3d1dc164a..49fb0e8c5c2 100644 --- a/arduino/libraries/loader.go +++ b/arduino/libraries/loader.go @@ -19,6 +19,7 @@ import ( "fmt" "strings" + "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/globals" "github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/go-paths-helper" @@ -82,7 +83,15 @@ func makeNewLibrary(libraryDir *paths.Path, location LibraryLocation) (*Library, libProperties.Set("architectures", "*") } library.Architectures = commaSeparatedToList(libProperties.Get("architectures")) - + if supported := libProperties.Get("supported"); supported != "" { + for _, formula := range strings.Split(supported, ",") { + constraint, err := cores.ParseFQBNMatcher(formula) + if err != nil { + return nil, errors.New(tr("invalid value '%[1]s': %[2]s", formula, err)) + } + library.SupportedFQBN = append(library.SupportedFQBN, constraint) + } + } libProperties.Set("category", strings.TrimSpace(libProperties.Get("category"))) if !ValidCategories[libProperties.Get("category")] { libProperties.Set("category", "Uncategorized") diff --git a/commands/lib/list.go b/commands/lib/list.go index eb226c145c7..25852f01250 100644 --- a/commands/lib/list.go +++ b/commands/lib/list.go @@ -70,8 +70,8 @@ func LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.Library } } if latest, has := filteredRes[lib.Library.Name]; has { - latestPriority := librariesresolver.ComputePriority(latest.Library, "", fqbn.PlatformArch) - libPriority := librariesresolver.ComputePriority(lib.Library, "", fqbn.PlatformArch) + latestPriority := librariesresolver.ComputePriority(latest.Library, "", fqbn) + libPriority := librariesresolver.ComputePriority(lib.Library, "", fqbn) if latestPriority >= libPriority { // Pick library with the best priority continue @@ -80,7 +80,7 @@ func LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.Library // Check if library is compatible with board specified by FBQN lib.Library.CompatibleWith = map[string]bool{ - fqbnString: lib.Library.IsCompatibleWith(fqbn.PlatformArch), + fqbnString: lib.Library.IsCompatibleWith(fqbn), } filteredRes[lib.Library.Name] = lib diff --git a/internal/cli/lib/list.go b/internal/cli/lib/list.go index ea644367db5..279ca971010 100644 --- a/internal/cli/lib/list.go +++ b/internal/cli/lib/list.go @@ -67,12 +67,7 @@ func List(instance *rpc.Instance, args []string, all bool, updatable bool) { } // GetList returns a list of installed libraries. -func GetList( - instance *rpc.Instance, - args []string, - all bool, - updatable bool, -) []*rpc.InstalledLibrary { +func GetList(instance *rpc.Instance, args []string, all bool, updatable bool) []*rpc.InstalledLibrary { name := "" if len(args) > 0 { name = args[0] diff --git a/legacy/builder/resolve_library.go b/legacy/builder/resolve_library.go index 3cb3c07aee3..195f02b6948 100644 --- a/legacy/builder/resolve_library.go +++ b/legacy/builder/resolve_library.go @@ -34,7 +34,7 @@ func ResolveLibrary(ctx *types.Context, header string) *libraries.Library { ctx.Info(fmt.Sprintf(" -> %s: %s", tr("candidates"), candidates)) } - if candidates == nil || len(candidates) == 0 { + if len(candidates) == 0 { return nil } @@ -44,7 +44,7 @@ func ResolveLibrary(ctx *types.Context, header string) *libraries.Library { } } - selected := resolver.ResolveFor(header, ctx.TargetPlatform.Platform.Architecture) + selected := resolver.ResolveFor(header, ctx.FQBN) if alreadyImported := importedLibraries.FindByName(selected.Name); alreadyImported != nil { // Certain libraries might have the same name but be different. // This usually happens when the user includes two or more custom libraries that have 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