Skip to content

Added 'lib examples' command #905

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 9 commits into from
Aug 31, 2020
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
16 changes: 16 additions & 0 deletions arduino/libraries/libraries.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type Library struct {
Version *semver.Version
License string
Properties *properties.Map
Examples paths.PathList
}

func (library *Library) String() string {
Expand Down Expand Up @@ -137,3 +138,18 @@ 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
}
2 changes: 1 addition & 1 deletion arduino/libraries/libraries_location.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type LibraryLocation int
// The enumeration is listed in ascending order of priority
const (
// IDEBuiltIn are libraries bundled in the IDE
IDEBuiltIn = iota
IDEBuiltIn LibraryLocation = iota
// PlatformBuiltIn are libraries bundled in a PlatformRelease
PlatformBuiltIn
// ReferencedPlatformBuiltIn are libraries bundled in a PlatformRelease referenced for build
Expand Down
54 changes: 54 additions & 0 deletions arduino/libraries/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/arduino/go-paths-helper"
properties "github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors"
semver "go.bug.st/relaxed-semver"
)

Expand Down Expand Up @@ -94,6 +95,9 @@ func makeNewLibrary(libraryDir *paths.Path, location LibraryLocation) (*Library,
library.Version = v
}

if err := addExamples(library); err != nil {
return nil, errors.Errorf("scanning examples: %s", err)
}
library.Name = libraryDir.Base()
library.RealName = strings.TrimSpace(libProperties.Get("name"))
library.Author = strings.TrimSpace(libProperties.Get("author"))
Expand Down Expand Up @@ -122,6 +126,56 @@ func makeLegacyLibrary(path *paths.Path, location LibraryLocation) (*Library, er
IsLegacy: true,
Version: semver.MustParse(""),
}
if err := addExamples(library); err != nil {
return nil, errors.Errorf("scanning examples: %s", err)
}
addUtilityDirectory(library)
return library, nil
}

func addExamples(lib *Library) error {
files, err := lib.InstallDir.ReadDir()
if err != nil {
return err
}
examples := paths.NewPathList()
for _, file := range files {
name := strings.ToLower(file.Base())
if name != "example" && name != "examples" {
continue
}
if !file.IsDir() {
continue
}
if err := addExamplesToPathList(file, &examples); err != nil {
return err
}
break
}

lib.Examples = examples
return nil
}

func addExamplesToPathList(examplesPath *paths.Path, list *paths.PathList) error {
files, err := examplesPath.ReadDir()
if err != nil {
return err
}
for _, file := range files {
if isExample(file) {
list.Add(file)
} else if file.IsDir() {
if err := addExamplesToPathList(file, list); err != nil {
return err
}
}
}
return nil
}

// isExample returns true if examplePath contains an example
func isExample(examplePath *paths.Path) bool {
mainIno := examplePath.Join(examplePath.Base() + ".ino")
return mainIno.Exist() && mainIno.IsNotDir()
}
132 changes: 132 additions & 0 deletions cli/lib/examples.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].

package lib

import (
"fmt"
"os"
"sort"
"strings"

"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/cli/feedback"
"github.com/arduino/arduino-cli/cli/instance"
"github.com/arduino/arduino-cli/commands/lib"
rpc "github.com/arduino/arduino-cli/rpc/commands"
"github.com/arduino/go-paths-helper"
"github.com/fatih/color"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)

func initExamplesCommand() *cobra.Command {
examplesCommand := &cobra.Command{
Use: "examples [LIBRARY_NAME]",
Short: "Shows the list of the examples for libraries.",
Long: "Shows the list of the examples for libraries. A name may be given as argument to search a specific library.",
Example: " " + os.Args[0] + " lib examples Wire",
Args: cobra.MaximumNArgs(1),
Run: runExamplesCommand,
}
examplesCommand.Flags().StringVarP(&examplesFlags.fqbn, "fqbn", "b", "", "Show libraries for the specified board FQBN.")
return examplesCommand
}

var examplesFlags struct {
fqbn string
}

func runExamplesCommand(cmd *cobra.Command, args []string) {
instance := instance.CreateInstanceIgnorePlatformIndexErrors()
logrus.Info("Show examples for library")

name := ""
if len(args) > 0 {
name = args[0]
}

res, err := lib.LibraryList(context.Background(), &rpc.LibraryListReq{
Instance: instance,
All: true,
Name: name,
Fqbn: examplesFlags.fqbn,
})
if err != nil {
feedback.Errorf("Error getting libraries info: %v", err)
os.Exit(errorcodes.ErrGeneric)
}

found := []*libraryExamples{}
for _, lib := range res.GetInstalledLibrary() {
found = append(found, &libraryExamples{
Library: lib.Library,
Examples: lib.Library.Examples,
})
}

feedback.PrintResult(libraryExamplesResult{found})
logrus.Info("Done")
}

// output from this command requires special formatting, let's create a dedicated
// feedback.Result implementation

type libraryExamples struct {
Library *rpc.Library `json:"library"`
Examples []string `json:"examples"`
}

type libraryExamplesResult struct {
Examples []*libraryExamples
}

func (ir libraryExamplesResult) Data() interface{} {
return ir.Examples
}

func (ir libraryExamplesResult) String() string {
if ir.Examples == nil || len(ir.Examples) == 0 {
return "No libraries found."
}

sort.Slice(ir.Examples, func(i, j int) bool {
return strings.ToLower(ir.Examples[i].Library.Name) < strings.ToLower(ir.Examples[j].Library.Name)
})

res := []string{}
for _, lib := range ir.Examples {
name := lib.Library.Name
if lib.Library.ContainerPlatform != "" {
name += " (" + lib.Library.GetContainerPlatform() + ")"
} else if lib.Library.Location != rpc.LibraryLocation_user {
name += " (" + lib.Library.GetLocation().String() + ")"
}
r := fmt.Sprintf("Examples for library %s\n", color.GreenString("%s", name))
sort.Slice(lib.Examples, func(i, j int) bool {
return strings.ToLower(lib.Examples[i]) < strings.ToLower(lib.Examples[j])
})
for _, example := range lib.Examples {
examplePath := paths.New(example)
r += fmt.Sprintf(" - %s%s\n",
color.New(color.Faint).Sprintf("%s%c", examplePath.Parent(), os.PathSeparator),
examplePath.Base())
}
res = append(res, r)
}

return strings.Join(res, "\n")
}
1 change: 1 addition & 0 deletions cli/lib/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func NewCommand() *cobra.Command {
libCommand.AddCommand(initDownloadCommand())
libCommand.AddCommand(initInstallCommand())
libCommand.AddCommand(initListCommand())
libCommand.AddCommand(initExamplesCommand())
libCommand.AddCommand(initSearchCommand())
libCommand.AddCommand(initUninstallCommand())
libCommand.AddCommand(initUpgradeCommand())
Expand Down
26 changes: 22 additions & 4 deletions cli/lib/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package lib

import (
"os"
"sort"
"strings"

"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/cli/feedback"
Expand All @@ -31,31 +33,43 @@ import (

func initListCommand() *cobra.Command {
listCommand := &cobra.Command{
Use: "list",
Short: "Shows a list of all installed libraries.",
Long: "Shows a list of all installed libraries.",
Use: "list [LIBNAME]",
Short: "Shows a list of installed libraries.",
Long: "Shows a list of installed libraries.\n\n" +
"If the LIBNAME parameter is specified the listing is limited to that specific\n" +
"library. By default the libraries provided as built-in by platforms/core are\n" +
"not listed, they can be listed by adding the --all flag.",
Example: " " + os.Args[0] + " lib list",
Args: cobra.NoArgs,
Args: cobra.MaximumNArgs(1),
Run: runListCommand,
}
listCommand.Flags().BoolVar(&listFlags.all, "all", false, "Include built-in libraries (from platforms and IDE) in listing.")
listCommand.Flags().StringVarP(&listFlags.fqbn, "fqbn", "b", "", "Show libraries for the specified board FQBN.")
listCommand.Flags().BoolVar(&listFlags.updatable, "updatable", false, "List updatable libraries.")
return listCommand
}

var listFlags struct {
all bool
updatable bool
fqbn string
}

func runListCommand(cmd *cobra.Command, args []string) {
instance := instance.CreateInstanceIgnorePlatformIndexErrors()
logrus.Info("Listing")

name := ""
if len(args) > 0 {
name = args[0]
}

res, err := lib.LibraryList(context.Background(), &rpc.LibraryListReq{
Instance: instance,
All: listFlags.all,
Updatable: listFlags.updatable,
Name: name,
Fqbn: listFlags.fqbn,
})
if err != nil {
feedback.Errorf("Error listing Libraries: %v", err)
Expand Down Expand Up @@ -88,6 +102,10 @@ func (ir installedResult) String() string {
if ir.installedLibs == nil || len(ir.installedLibs) == 0 {
return "No libraries installed."
}
sort.Slice(ir.installedLibs, func(i, j int) bool {
return strings.ToLower(ir.installedLibs[i].Library.Name) < strings.ToLower(ir.installedLibs[j].Library.Name) ||
strings.ToLower(ir.installedLibs[i].Library.ContainerPlatform) < strings.ToLower(ir.installedLibs[j].Library.ContainerPlatform)
})

t := table.New()
t.SetHeader("Name", "Installed", "Available", "Location", "Description")
Expand Down
50 changes: 50 additions & 0 deletions commands/lib/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ package lib

import (
"context"
"errors"
"fmt"
"strings"

"github.com/arduino/arduino-cli/arduino/cores"
"github.com/arduino/arduino-cli/arduino/libraries"
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
Expand All @@ -32,12 +36,57 @@ type installedLib struct {

// LibraryList FIXMEDOC
func LibraryList(ctx context.Context, req *rpc.LibraryListReq) (*rpc.LibraryListResp, error) {
pm := commands.GetPackageManager(req.GetInstance().GetId())
if pm == nil {
return nil, errors.New("invalid instance")
}

lm := commands.GetLibraryManager(req.GetInstance().GetId())
if lm == nil {
return nil, errors.New("invalid instance")
}

nameFilter := strings.ToLower(req.GetName())

instaledLib := []*rpc.InstalledLibrary{}
res := listLibraries(lm, req.GetUpdatable(), req.GetAll())
if len(res) > 0 {
if f := req.GetFqbn(); f != "" {
fqbn, err := cores.ParseFQBN(req.GetFqbn())
if err != nil {
return nil, fmt.Errorf("parsing fqbn: %s", err)
}
_, boardPlatform, _, _, refBoardPlatform, err := pm.ResolveFQBN(fqbn)
if err != nil {
return nil, fmt.Errorf("loading board data: %s", err)
}

filteredRes := map[string]*installedLib{}
for _, lib := range res {
if cp := lib.Library.ContainerPlatform; cp != nil {
if cp != boardPlatform && cp != refBoardPlatform {
// Filter all libraries from extraneous platforms
continue
}
}
if latest, has := filteredRes[lib.Library.Name]; has {
if latest.Library.LocationPriorityFor(boardPlatform, refBoardPlatform) >= lib.Library.LocationPriorityFor(boardPlatform, refBoardPlatform) {
continue
}
}
filteredRes[lib.Library.Name] = lib
}

res = []*installedLib{}
for _, lib := range filteredRes {
res = append(res, lib)
}
}

for _, lib := range res {
if nameFilter != "" && strings.ToLower(lib.Library.Name) != nameFilter {
continue
}
libtmp := GetOutputLibrary(lib.Library)
release := GetOutputRelease(lib.Available)
instaledLib = append(instaledLib, &rpc.InstalledLibrary{
Expand Down Expand Up @@ -117,6 +166,7 @@ func GetOutputLibrary(lib *libraries.Library) *rpc.Library {
IsLegacy: lib.IsLegacy,
Version: lib.Version.String(),
License: lib.License,
Examples: lib.Examples.AsStrings(),
}
}

Expand Down
Loading