Skip to content

[breaking] Fix: lib list --fqbn and lib examples --fqbn do not show platform bundled lib when lib of same name is installed globally #2113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 7 additions & 15 deletions arduino/libraries/libraries.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,13 @@ func (library *Library) IsArchitectureIndependent() bool {
return library.IsOptimizedForArchitecture("*") || library.Architectures == nil || len(library.Architectures) == 0
}

// 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)
}

// SourceDir represents a source dir of a library
type SourceDir struct {
Dir *paths.Path
Expand All @@ -205,21 +212,6 @@ func (library *Library) SourceDirs() []SourceDir {
return dirs
}

// LocationPriorityFor returns a number representing the location priority for the given library
// using the given platform and referenced-platform. Higher value means higher priority.
func (library *Library) LocationPriorityFor(platformRelease, refPlatformRelease *cores.PlatformRelease) int {
if library.Location == IDEBuiltIn {
return 1
} else if library.ContainerPlatform == refPlatformRelease {
return 2
} else if library.ContainerPlatform == platformRelease {
return 3
} else if library.Location == User {
return 4
}
return 0
}

// DeclaredHeaders returns the C++ headers that the library declares in library.properties
func (library *Library) DeclaredHeaders() []string {
if library.declaredHeaders == nil {
Expand Down
7 changes: 5 additions & 2 deletions arduino/libraries/librariesresolver/cpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (resolver *Cpp) ResolveFor(header, architecture string) *libraries.Library
var found libraries.List
var foundPriority int
for _, lib := range resolver.headers[header] {
libPriority := computePriority(lib, header, architecture)
libPriority := ComputePriority(lib, header, architecture)
msg := " discarded"
if found == nil || foundPriority < libPriority {
found = libraries.List{}
Expand Down Expand Up @@ -164,7 +164,10 @@ func simplify(name string) string {
return name
}

func computePriority(lib *libraries.Library, header, arch string) int {
// 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 {
header = strings.TrimSuffix(header, filepath.Ext(header))
header = simplify(header)
name := simplify(lib.Name)
Expand Down
14 changes: 7 additions & 7 deletions arduino/libraries/librariesresolver/cpp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ func TestClosestMatchWithTotallyDifferentNames(t *testing.T) {
}

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", "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")
require.True(t, r1 > r2)
require.True(t, r2 > r3)
require.True(t, r3 > r4)
Expand Down
16 changes: 6 additions & 10 deletions commands/lib/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/arduino/arduino-cli/arduino/libraries"
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
"github.com/arduino/arduino-cli/arduino/libraries/librariesresolver"
"github.com/arduino/arduino-cli/commands"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
)
Expand All @@ -49,7 +50,7 @@ func LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.Library
nameFilter := strings.ToLower(req.GetName())

var allLibs []*installedLib
if f := req.GetFqbn(); f != "" {
if fqbnString := req.GetFqbn(); fqbnString != "" {
allLibs = listLibraries(lm, req.GetUpdatable(), true)
fqbn, err := cores.ParseFQBN(req.GetFqbn())
if err != nil {
Expand All @@ -69,22 +70,17 @@ func LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.Library
}
}
if latest, has := filteredRes[lib.Library.Name]; has {
if latest.Library.LocationPriorityFor(boardPlatform, refBoardPlatform) >= lib.Library.LocationPriorityFor(boardPlatform, refBoardPlatform) {
latestPriority := librariesresolver.ComputePriority(latest.Library, "", fqbn.PlatformArch)
libPriority := librariesresolver.ComputePriority(lib.Library, "", fqbn.PlatformArch)
if latestPriority >= libPriority {
// Pick library with the best priority
continue
}
}

// Check if library is compatible with board specified by FBQN
compatible := false
for _, arch := range lib.Library.Architectures {
compatible = (arch == fqbn.PlatformArch || arch == "*")
if compatible {
break
}
}
lib.Library.CompatibleWith = map[string]bool{
f: compatible,
fqbnString: lib.Library.IsCompatibleWith(fqbn.PlatformArch),
}

filteredRes[lib.Library.Name] = lib
Expand Down
6 changes: 6 additions & 0 deletions docs/UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Here you can find a list of migration guides to handle breaking changes between

## 0.32.0

### `arduino-cli` doesn't lookup anymore in the current directory for configuration file.

Configuration file lookup in current working directory and its parents is dropped. The command line flag `--config-file`
must be specified to use an alternative configuration file from the one in the data directory.

Expand Down Expand Up @@ -49,6 +51,10 @@ $ arduino-cli outdated --format json
}
```

### golang API: method `github.com/arduino/arduino-cli/arduino/libraries/Library.LocationPriorityFor` removed

That method was outdated and must not be used.

## 0.31.0

### Added `post_install` script support for tools
Expand Down
10 changes: 3 additions & 7 deletions internal/cli/lib/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ not listed, they can be listed by adding the --all flag.`),
Example: " " + os.Args[0] + " lib list",
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
runListCommand(args, all, updatable)
instance := instance.CreateAndInit()
logrus.Info("Executing `arduino-cli lib list`")
List(instance, args, all, updatable)
},
}
listCommand.Flags().BoolVar(&all, "all", false, tr("Include built-in libraries (from platforms and IDE) in listing."))
Expand All @@ -54,12 +56,6 @@ not listed, they can be listed by adding the --all flag.`),
return listCommand
}

func runListCommand(args []string, all bool, updatable bool) {
instance := instance.CreateAndInit()
logrus.Info("Executing `arduino-cli lib list`")
List(instance, args, all, updatable)
}

// List gets and prints a list of installed libraries.
func List(instance *rpc.Instance, args []string, all bool, updatable bool) {
installedLibs := GetList(instance, args, all, updatable)
Expand Down
40 changes: 40 additions & 0 deletions internal/integrationtest/lib/lib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1547,3 +1547,43 @@ func TestLibQueryParameters(t *testing.T) {
require.Contains(t, string(stdout),
"Starting download \x1b[36murl\x1b[0m=\"https://downloads.arduino.cc/libraries/github.com/firmata/Firmata-2.5.9.zip?query=upgrade-builtin\"\n")
}

func TestLibBundlesWhenLibWithTheSameNameIsInstalledGlobally(t *testing.T) {
env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t)
defer env.CleanUp()

// See: https://github.com/arduino/arduino-cli/issues/1566
_, _, err := cli.Run("core", "install", "arduino:[email protected]")
require.NoError(t, err)
{
stdout, _, err := cli.Run("lib", "list", "--all", "--fqbn", "arduino:samd:mkrzero", "USBHost", "--format", "json")
require.NoError(t, err)
j := requirejson.Parse(t, stdout)
j.Query(`.[0].library.name`).MustEqual(`"USBHost"`)
j.Query(`.[0].library.compatible_with."arduino:samd:mkrzero"`).MustEqual(`true`)
}
_, _, err = cli.Run("lib", "install", "[email protected]")
require.NoError(t, err)
{
// Check that the architecture-specific library is still listed
stdout, _, err := cli.Run("lib", "list", "--all", "--fqbn", "arduino:samd:mkrzero", "USBHost", "--format", "json")
require.NoError(t, err)
j := requirejson.Parse(t, stdout)
j.Query(`.[0].library.name`).MustEqual(`"USBHost"`)
j.Query(`.[0].library.compatible_with."arduino:samd:mkrzero"`).MustEqual(`true`)
}

// See: https://github.com/arduino/arduino-cli/issues/1656
{
_, _, err = cli.Run("core", "update-index", "--additional-urls", "https://arduino.esp8266.com/stable/package_esp8266com_index.json")
require.NoError(t, err)
_, _, err = cli.Run("core", "install", "--additional-urls", "https://arduino.esp8266.com/stable/package_esp8266com_index.json", "esp8266:[email protected]")
require.NoError(t, err)
_, _, err = cli.Run("lib", "install", "[email protected]")
require.NoError(t, err)
stdout, _, err := cli.Run("lib", "examples", "--fqbn", "esp8266:esp8266:generic", "ArduinoOTA", "--format", "json")
require.NoError(t, err)
requirejson.Parse(t, stdout).Query(`.[].library.examples[0]`).MustContain(`"BasicOTA"`)
requirejson.Parse(t, stdout).Query(`.[].library.examples[1]`).MustContain(`"OTALeds"`)
}
}