From bfa02dd3fbb4dced081b6e387685bdbcad9822ce Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Jan 2019 15:20:50 +0100 Subject: [PATCH 01/12] Use PlatformRelease.IsInstalled() method when appropriate --- arduino/cores/cores.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arduino/cores/cores.go b/arduino/cores/cores.go index c7037b10681..eb831be452d 100644 --- a/arduino/cores/cores.go +++ b/arduino/cores/cores.go @@ -20,11 +20,11 @@ package cores import ( "strings" - "github.com/arduino/go-paths-helper" + paths "github.com/arduino/go-paths-helper" "github.com/arduino/arduino-cli/arduino/resources" - "github.com/arduino/go-properties-orderedmap" - "go.bug.st/relaxed-semver" + properties "github.com/arduino/go-properties-orderedmap" + semver "go.bug.st/relaxed-semver" ) // Platform represents a platform package. @@ -158,7 +158,7 @@ func (platform *Platform) latestReleaseVersion() *semver.Version { func (platform *Platform) GetAllInstalled() []*PlatformRelease { res := []*PlatformRelease{} for _, release := range platform.Releases { - if release.InstallDir != nil { + if release.IsInstalled() { res = append(res, release) } } From 50ab64f8eab938151b3e0d607a5eff5723207374 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Jan 2019 15:22:48 +0100 Subject: [PATCH 02/12] Added methods to cycle on all installed platforms or boards --- .../cores/packagemanager/package_manager.go | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/arduino/cores/packagemanager/package_manager.go b/arduino/cores/packagemanager/package_manager.go index a8b455aed6c..3e50c3a3cbf 100644 --- a/arduino/cores/packagemanager/package_manager.go +++ b/arduino/cores/packagemanager/package_manager.go @@ -26,10 +26,10 @@ import ( "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores/packageindex" - "github.com/arduino/go-paths-helper" + paths "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" "github.com/sirupsen/logrus" - "go.bug.st/relaxed-semver" + semver "go.bug.st/relaxed-semver" ) // PackageManager defines the superior oracle which understands all about @@ -354,6 +354,36 @@ func (pm *PackageManager) GetAllInstalledToolsReleases() []*cores.ToolRelease { return tools } +// InstalledPlatformReleases returns all installed PlatformReleases. This function is +// useful to range all PlatformReleases in for loops. +func (pm *PackageManager) InstalledPlatformReleases() []*cores.PlatformRelease { + platforms := []*cores.PlatformRelease{} + for _, targetPackage := range pm.packages.Packages { + for _, platform := range targetPackage.Platforms { + for _, release := range platform.GetAllInstalled() { + platforms = append(platforms, release) + } + } + } + return platforms +} + +// InstalledBoards returns all installed Boards. This function is useful to range +// all Boards in for loops. +func (pm *PackageManager) InstalledBoards() []*cores.Board { + boards := []*cores.Board{} + for _, targetPackage := range pm.packages.Packages { + for _, platform := range targetPackage.Platforms { + for _, release := range platform.GetAllInstalled() { + for _, board := range release.Boards { + boards = append(boards, board) + } + } + } + } + return boards +} + func (pm *PackageManager) FindToolsRequiredForBoard(board *cores.Board) ([]*cores.ToolRelease, error) { pm.Log.Infof("Searching tools required for board %s", board) From 09b4da6d423959298b917a1729d99796d2435f64 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Jan 2019 16:42:03 +0100 Subject: [PATCH 03/12] board list: reduced timeout to 1s --- commands/board/list.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/commands/board/list.go b/commands/board/list.go index 8d604153a18..1ae2bd1de66 100644 --- a/commands/board/list.go +++ b/commands/board/list.go @@ -35,13 +35,13 @@ func initListCommand() *cobra.Command { Use: "list", Short: "List connected boards.", Long: "Detects and displays a list of connected boards to the current computer.", - Example: " " + commands.AppName + " board list --timeout 10s", + Example: " " + commands.AppName + " board list", Args: cobra.NoArgs, Run: runListCommand, } - listCommand.Flags().StringVar(&listFlags.timeout, "timeout", "5s", - "The timeout of the search of connected devices, try to high it if your board is not found (e.g. to 10s).") + listCommand.Flags().StringVar(&listFlags.timeout, "timeout", "1s", + "The timeout of the search of connected devices, try to increase it if your board is not found (e.g. to 10s).") return listCommand } From 7662cfad339efbdd3b5d06e688d960adb48b650b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 2 Jan 2019 16:16:32 +0100 Subject: [PATCH 04/12] discovery: first implementation (WIP) --- arduino/discovery/discovery.go | 97 ++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 arduino/discovery/discovery.go diff --git a/arduino/discovery/discovery.go b/arduino/discovery/discovery.go new file mode 100644 index 00000000000..55ffb385e05 --- /dev/null +++ b/arduino/discovery/discovery.go @@ -0,0 +1,97 @@ +// +// This file is part of arduino-cli. +// +// Copyright 2018 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 license@arduino.cc. +// + +package discovery + +import ( + "encoding/json" + "fmt" + "io" + + properties "github.com/arduino/go-properties-orderedmap" + + "github.com/arduino/arduino-cli/executils" +) + +// Discovery is an instance of a discovery tool +type Discovery struct { + in io.WriteCloser + out io.ReadCloser + outJSON *json.Decoder +} + +// BoardPort is a generic port descriptor +type BoardPort struct { + Address string `json:"address"` + Label string `json:"label"` + Prefs *properties.Map `json:"prefs"` + IdentificationPrefs *properties.Map `json:"identificationPrefs"` + Protocol string `json:"protocol"` + ProtocolLabel string `json:"protocolLabel"` +} + +type eventJSON struct { + EventType string `json:"eventType,required"` + Ports []*BoardPort `json:"ports"` +} + +// NewFromCommandLine creates a new Discovery object +func NewFromCommandLine(args ...string) (*Discovery, error) { + cmd, err := executils.Command(args) + if err != nil { + return nil, fmt.Errorf("creating discovery process: %s", err) + } + disc := &Discovery{} + if in, err := cmd.StdinPipe(); err == nil { + disc.in = in + } else { + return nil, fmt.Errorf("creating stdin pipe for discovery: %s", err) + } + if out, err := cmd.StdoutPipe(); err == nil { + disc.out = out + disc.outJSON = json.NewDecoder(disc.out) + } else { + return nil, fmt.Errorf("creating stdout pipe for discovery: %s", err) + } + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf("starting discovery: %s", err) + } + return disc, nil +} + +// List retrieve the port list from this discovery +func (d *Discovery) List() ([]*BoardPort, error) { + if _, err := d.in.Write([]byte("LIST\n")); err != nil { + return nil, fmt.Errorf("sending LIST command to discovery: %s", err) + } + var event eventJSON + if err := d.outJSON.Decode(&event); err != nil { + return nil, fmt.Errorf("decoding LIST command: %s", err) + } + return event.Ports, nil +} + +// Close stops the Discovery and free the resources +func (d *Discovery) Close() error { + if err := d.in.Close(); err != nil { + return fmt.Errorf("closing stdin pipe: %s", err) + } + if err := d.out.Close(); err != nil { + return fmt.Errorf("closing stdout pipe: %s", err) + } + return nil +} From 289f6d535e5d1160d82c36959a102c3f0aa88f8b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 2 Jan 2019 19:05:42 +0100 Subject: [PATCH 05/12] discovery: Do not start process on Discovery creation Added a Start method for that and now Close will also kill the running process. --- arduino/discovery/discovery.go | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/arduino/discovery/discovery.go b/arduino/discovery/discovery.go index 55ffb385e05..9ea26804a39 100644 --- a/arduino/discovery/discovery.go +++ b/arduino/discovery/discovery.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "io" + "os/exec" properties "github.com/arduino/go-properties-orderedmap" @@ -32,6 +33,7 @@ type Discovery struct { in io.WriteCloser out io.ReadCloser outJSON *json.Decoder + cmd *exec.Cmd } // BoardPort is a generic port descriptor @@ -56,21 +58,27 @@ func NewFromCommandLine(args ...string) (*Discovery, error) { return nil, fmt.Errorf("creating discovery process: %s", err) } disc := &Discovery{} - if in, err := cmd.StdinPipe(); err == nil { - disc.in = in + disc.cmd = cmd + return disc, nil +} + +// Start starts the specified discovery +func (d *Discovery) Start() error { + if in, err := d.cmd.StdinPipe(); err == nil { + d.in = in } else { - return nil, fmt.Errorf("creating stdin pipe for discovery: %s", err) + return fmt.Errorf("creating stdin pipe for discovery: %s", err) } - if out, err := cmd.StdoutPipe(); err == nil { - disc.out = out - disc.outJSON = json.NewDecoder(disc.out) + if out, err := d.cmd.StdoutPipe(); err == nil { + d.out = out + d.outJSON = json.NewDecoder(d.out) } else { - return nil, fmt.Errorf("creating stdout pipe for discovery: %s", err) + return fmt.Errorf("creating stdout pipe for discovery: %s", err) } - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("starting discovery: %s", err) + if err := d.cmd.Start(); err != nil { + return fmt.Errorf("starting discovery process: %s", err) } - return disc, nil + return nil } // List retrieve the port list from this discovery @@ -87,11 +95,15 @@ func (d *Discovery) List() ([]*BoardPort, error) { // Close stops the Discovery and free the resources func (d *Discovery) Close() error { + // TODO: Send QUIT for safe close or terminate process after a small timeout if err := d.in.Close(); err != nil { return fmt.Errorf("closing stdin pipe: %s", err) } if err := d.out.Close(); err != nil { return fmt.Errorf("closing stdout pipe: %s", err) } + if d.cmd != nil { + d.cmd.Process.Kill() + } return nil } From e50f3913ec2824e425f1dd9dadc59daa61292b2c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 2 Jan 2019 19:07:24 +0100 Subject: [PATCH 06/12] discovery: added timeout on LIST command --- arduino/discovery/discovery.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arduino/discovery/discovery.go b/arduino/discovery/discovery.go index 9ea26804a39..8e56ece99ed 100644 --- a/arduino/discovery/discovery.go +++ b/arduino/discovery/discovery.go @@ -22,6 +22,9 @@ import ( "fmt" "io" "os/exec" + "time" + + "github.com/arduino/arduino-cli/arduino/cores/packagemanager" properties "github.com/arduino/go-properties-orderedmap" @@ -87,9 +90,23 @@ func (d *Discovery) List() ([]*BoardPort, error) { return nil, fmt.Errorf("sending LIST command to discovery: %s", err) } var event eventJSON + done := make(chan bool) + timeout := false + go func() { + select { + case <-done: + case <-time.After(5 * time.Second): + timeout = true + d.Close() + } + }() if err := d.outJSON.Decode(&event); err != nil { + if timeout { + return nil, fmt.Errorf("decoding LIST command: timeout") + } return nil, fmt.Errorf("decoding LIST command: %s", err) } + done <- true return event.Ports, nil } From 4a8456475c407018b46e297b764953744ca657d5 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Jan 2019 15:25:48 +0100 Subject: [PATCH 07/12] discovery: Timeout is now configurable --- arduino/discovery/discovery.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arduino/discovery/discovery.go b/arduino/discovery/discovery.go index 8e56ece99ed..033487f0b53 100644 --- a/arduino/discovery/discovery.go +++ b/arduino/discovery/discovery.go @@ -37,6 +37,7 @@ type Discovery struct { out io.ReadCloser outJSON *json.Decoder cmd *exec.Cmd + Timeout time.Duration } // BoardPort is a generic port descriptor @@ -60,7 +61,7 @@ func NewFromCommandLine(args ...string) (*Discovery, error) { if err != nil { return nil, fmt.Errorf("creating discovery process: %s", err) } - disc := &Discovery{} + disc := &Discovery{Timeout: time.Second} disc.cmd = cmd return disc, nil } @@ -95,7 +96,7 @@ func (d *Discovery) List() ([]*BoardPort, error) { go func() { select { case <-done: - case <-time.After(5 * time.Second): + case <-time.After(d.Timeout): timeout = true d.Close() } From e95a6da6aa99b0f4bc583dd7aa130829044c292c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Jan 2019 16:41:06 +0100 Subject: [PATCH 08/12] discovery: Added method to retrieve all installed discoveries --- arduino/discovery/discovery.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arduino/discovery/discovery.go b/arduino/discovery/discovery.go index 033487f0b53..46925599e5e 100644 --- a/arduino/discovery/discovery.go +++ b/arduino/discovery/discovery.go @@ -125,3 +125,28 @@ func (d *Discovery) Close() error { } return nil } + +// ExtractDiscoveriesFromPlatforms returns all Discovery from all the installed platforms. +func ExtractDiscoveriesFromPlatforms(pm *packagemanager.PackageManager) map[string]*Discovery { + res := map[string]*Discovery{} + + for _, platformRelease := range pm.InstalledPlatformReleases() { + discoveries := platformRelease.Properties.SubTree("discovery").FirstLevelOf() + + for name, props := range discoveries { + if pattern, has := props.GetOk("pattern"); has { + props.Merge(platformRelease.Properties) + cmdLine := props.ExpandPropsInString(pattern) + if cmdArgs, err := properties.SplitQuotedString(cmdLine, `"`, false); err != nil { + // TODO + } else if disc, err := NewFromCommandLine(cmdArgs...); err != nil { + // TODO + } else { + res[name] = disc + } + } + } + } + + return res +} From 093b044b31ee7eba23649074ecaf5a11fb938d87 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Jan 2019 16:42:54 +0100 Subject: [PATCH 09/12] discovery: Added method to identify boards from BoardPort identification properties --- arduino/cores/packagemanager/identify.go | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 arduino/cores/packagemanager/identify.go diff --git a/arduino/cores/packagemanager/identify.go b/arduino/cores/packagemanager/identify.go new file mode 100644 index 00000000000..834cd95d00e --- /dev/null +++ b/arduino/cores/packagemanager/identify.go @@ -0,0 +1,65 @@ +/* + * This file is part of arduino-cli. + * + * Copyright 2018 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 license@arduino.cc. + */ + +package packagemanager + +import ( + "fmt" + + "github.com/arduino/arduino-cli/arduino/cores" + properties "github.com/arduino/go-properties-orderedmap" +) + +// IdentifyBoard returns a list of baords matching the provided identification properties. +func (pm *PackageManager) IdentifyBoard(idProps *properties.Map) []*cores.Board { + if idProps.Size() == 0 { + return []*cores.Board{} + } + + checkSuffix := func(props *properties.Map, s string) (checked bool, found bool) { + for k, v1 := range idProps.AsMap() { + v2, ok := props.GetOk(k + s) + if !ok { + return false, false + } + if v1 != v2 { + return true, false + } + } + return false, true + } + + foundBoards := []*cores.Board{} + for _, board := range pm.InstalledBoards() { + if _, found := checkSuffix(board.Properties, ""); found { + foundBoards = append(foundBoards, board) + continue + } + id := 0 + for { + again, found := checkSuffix(board.Properties, fmt.Sprintf(".%d", id)) + if found { + foundBoards = append(foundBoards, board) + } + if !again { + break + } + id++ + } + } + return foundBoards +} From 916bc3e65c800069f1922aa4e972c131ad086144 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Jan 2019 16:48:06 +0100 Subject: [PATCH 10/12] board list command now uses pluggable-discoveries --- commands/board/list.go | 182 ++++++++++++++++++++++++----------------- 1 file changed, 107 insertions(+), 75 deletions(-) diff --git a/commands/board/list.go b/commands/board/list.go index 1ae2bd1de66..422d72cb64e 100644 --- a/commands/board/list.go +++ b/commands/board/list.go @@ -18,15 +18,18 @@ package board import ( + "encoding/json" "fmt" + "os" + "sort" "time" - "github.com/arduino/arduino-cli/arduino/cores/packagemanager" + "github.com/arduino/arduino-cli/output" + + "github.com/arduino/arduino-cli/arduino/discovery" "github.com/arduino/arduino-cli/commands" + "github.com/arduino/arduino-cli/commands/core" "github.com/arduino/arduino-cli/common/formatter" - "github.com/arduino/arduino-cli/common/formatter/output" - "github.com/arduino/board-discovery" - "github.com/codeclysm/cc" "github.com/spf13/cobra" ) @@ -50,95 +53,124 @@ var listFlags struct { } // runListCommand detects and lists the connected arduino boards -// (either via serial or network ports). func runListCommand(cmd *cobra.Command, args []string) { pm := commands.InitPackageManager() - monitor := discovery.New(time.Millisecond) - monitor.Start() - duration, err := time.ParseDuration(listFlags.timeout) + timeout, err := time.ParseDuration(listFlags.timeout) if err != nil { - duration = time.Second * 5 + formatter.PrintError(err, "Invalid timeout.") + os.Exit(commands.ErrBadArgument) } - if formatter.IsCurrentFormat("text") { - stoppable := cc.Run(func(stop chan struct{}) { - for { - select { - case <-stop: - fmt.Print("\r \r") - return - default: - fmt.Print("\rDiscovering. ") - time.Sleep(time.Millisecond * 500) - fmt.Print("\rDiscovering.. ") - time.Sleep(time.Millisecond * 500) - fmt.Print("\rDiscovering...") - time.Sleep(time.Millisecond * 500) - } - } - }) - fmt.Print("\r") - time.Sleep(duration) - stoppable.Stop() - <-stoppable.Stopped - } else { - time.Sleep(duration) + discoveries := discovery.ExtractDiscoveriesFromPlatforms(pm) + + res := []*detectedPort{} + for discName, disc := range discoveries { + disc.Timeout = timeout + disc.Start() + defer disc.Close() + + ports, err := disc.List() + if err != nil { + fmt.Printf("Error getting port list from discovery %s: %s\n", discName, err) + continue + } + for _, port := range ports { + b := detectedBoards{} + for _, board := range pm.IdentifyBoard(port.IdentificationPrefs) { + b = append(b, &detectedBoard{ + Name: board.Name(), + FQBN: board.FQBN(), + }) + } + p := &detectedPort{ + Address: port.Address, + Protocol: port.Protocol, + ProtocolLabel: port.ProtocolLabel, + Boards: b, + } + res = append(res, p) + } } + output.Emit(&detectedPorts{ + Ports: res, + }) +} - formatter.Print(NewBoardList(pm, monitor)) +type detectedPorts struct { + Ports []*detectedPort `json:"ports"` +} - //monitor.Stop() //If called will slow like 1sec the program to close after print, with the same result (tested). - // it closes ungracefully, but at the end of the command we can't have races. +type detectedPort struct { + Address string `json:"address"` + Protocol string `json:"protocol"` + ProtocolLabel string `json:"protocol_label"` + Boards detectedBoards `json:"boards"` } -// NewBoardList returns a new board list by adding discovered boards from the board list and a monitor. -func NewBoardList(pm *packagemanager.PackageManager, monitor *discovery.Monitor) *output.AttachedBoardList { - if monitor == nil { - return nil - } +type detectedBoards []*detectedBoard + +type detectedBoard struct { + Name string `json:"name"` + FQBN string `json:"fqbn"` +} - serialDevices := monitor.Serial() - networkDevices := monitor.Network() - ret := &output.AttachedBoardList{ - SerialBoards: make([]output.SerialBoardListItem, 0, len(serialDevices)), - NetworkBoards: make([]output.NetworkBoardListItem, 0, len(networkDevices)), +func (b detectedBoards) Less(i, j int) bool { + x := b[i] + y := b[j] + if x.Name < y.Name { + return true } + return x.FQBN < y.FQBN +} - for _, item := range serialDevices { - boards := pm.FindBoardsWithVidPid(item.VendorID, item.ProductID) - if len(boards) == 0 { - ret.SerialBoards = append(ret.SerialBoards, output.SerialBoardListItem{ - Name: "unknown", - Port: item.Port, - UsbID: fmt.Sprintf("%s:%s - %s", item.VendorID[2:], item.ProductID[2:], item.SerialNumber), - }) - continue - } +func (p detectedPorts) Less(i, j int) bool { + x := p.Ports[i] + y := p.Ports[j] + if x.Protocol < y.Protocol { + return true + } + if x.Address < y.Address { + return true + } + return false +} - board := boards[0] - ret.SerialBoards = append(ret.SerialBoards, output.SerialBoardListItem{ - Name: board.Name(), - Fqbn: board.FQBN(), - Port: item.Port, - UsbID: fmt.Sprintf("%s:%s - %s", item.VendorID[2:], item.ProductID[2:], item.SerialNumber), - }) +func (p detectedPorts) EmitJSON() string { + d, err := json.MarshalIndent(p, "", " ") + if err != nil { + formatter.PrintError(err, "Error encoding json") + os.Exit(commands.ErrGeneric) } + return string(d) +} - for _, item := range networkDevices { - boards := pm.FindBoardsWithID(item.Name) - if len(boards) == 0 { - // skip it if not recognized - continue +func (p detectedPorts) EmitTerminal() string { + sort.Slice(p.Ports, p.Less) + table := output.NewTable() + table.SetHeader("Port", "Type", "Board Name", "FQBN") + for _, port := range p.Ports { + address := port.Protocol + "://" + port.Address + if port.Protocol == "serial" { + address = port.Address + } + protocol := port.ProtocolLabel + if len(port.Boards) > 0 { + sort.Slice(port.Boards, port.Boards.Less) + for _, b := range port.Boards { + board := b.Name + fqbn := b.FQBN + table.AddRow(address, protocol, board, fqbn) + // show address and protocol only on the first row + address = "" + protocol = "" + } + } else { + board := "Unknown" + fqbn := "" + table.AddRow(address, protocol, board, fqbn) } - - board := boards[0] - ret.NetworkBoards = append(ret.NetworkBoards, output.NetworkBoardListItem{ - Name: board.Name(), - Fqbn: board.FQBN(), - Location: fmt.Sprintf("%s:%d", item.Address, item.Port), - }) } - return ret + return table.Render() } From 7b6a23be266e8f566422f4303b15918f6148db46 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Jan 2019 16:52:58 +0100 Subject: [PATCH 11/12] discovery: serial-discovery is now downloaded automatically --- commands/board/list.go | 26 ++++++++++++ commands/board/serial.go | 89 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 commands/board/serial.go diff --git a/commands/board/list.go b/commands/board/list.go index 422d72cb64e..34eef176e65 100644 --- a/commands/board/list.go +++ b/commands/board/list.go @@ -62,8 +62,34 @@ func runListCommand(cmd *cobra.Command, args []string) { os.Exit(commands.ErrBadArgument) } + // Check for bultin serial-discovery tool + loadBuiltinSerialDiscoveryMetadata(pm) + serialDiscoveryTool, _ := getBuiltinSerialDiscoveryTool(pm) + if !serialDiscoveryTool.IsInstalled() { + formatter.Print("Downloading and installing missing tool: " + serialDiscoveryTool.String()) + core.DownloadToolRelease(pm, serialDiscoveryTool) + core.InstallToolRelease(pm, serialDiscoveryTool) + + if err := pm.LoadHardware(commands.Config); err != nil { + formatter.PrintError(err, "Could not load hardware packages.") + os.Exit(commands.ErrCoreConfig) + } + serialDiscoveryTool, _ = getBuiltinSerialDiscoveryTool(pm) + if !serialDiscoveryTool.IsInstalled() { + formatter.PrintErrorMessage("Missing serial-discovery tool.") + os.Exit(commands.ErrCoreConfig) + } + } + + serialDiscovery, err := discovery.NewFromCommandLine(serialDiscoveryTool.InstallDir.Join("serial-discovery").String()) + if err != nil { + formatter.PrintError(err, "Error setting up serial-discovery tool.") + os.Exit(commands.ErrCoreConfig) + } + // Find all installed discoveries discoveries := discovery.ExtractDiscoveriesFromPlatforms(pm) + discoveries["serial"] = serialDiscovery res := []*detectedPort{} for discName, disc := range discoveries { diff --git a/commands/board/serial.go b/commands/board/serial.go new file mode 100644 index 00000000000..172dd4a4747 --- /dev/null +++ b/commands/board/serial.go @@ -0,0 +1,89 @@ +// +// This file is part of arduino-cli. +// +// Copyright 2018 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 license@arduino.cc. +// + +package board + +import ( + "github.com/arduino/arduino-cli/arduino/cores" + "github.com/arduino/arduino-cli/arduino/cores/packagemanager" + "github.com/arduino/arduino-cli/arduino/resources" + semver "go.bug.st/relaxed-semver" +) + +var serialDiscoveryVersion = semver.ParseRelaxed("0.5.0") + +func loadBuiltinSerialDiscoveryMetadata(pm *packagemanager.PackageManager) { + builtinPackage := pm.GetPackages().GetOrCreatePackage("builtin") + ctagsTool := builtinPackage.GetOrCreateTool("serial-discovery") + ctagsRel := ctagsTool.GetOrCreateRelease(serialDiscoveryVersion) + ctagsRel.Flavors = []*cores.Flavor{ + // { + // OS: "i686-pc-linux-gnu", + // Resource: &resources.DownloadResource{ + // ArchiveFileName: "serial-discovery-1.0.0-i686-pc-linux-gnu.tar.bz2", + // URL: "https://downloads.arduino.cc/tools/serial-discovery-1.0.0-i686-pc-linux-gnu.tar.bz2", + // Size: , + // Checksum: "SHA-256:", + // CachePath: "tools", + // }, + // }, + { + OS: "x86_64-pc-linux-gnu", + Resource: &resources.DownloadResource{ + ArchiveFileName: "serial-discovery-0.5.0-x86_64-pc-linux-gnu.tar.bz2", + URL: "https://downloads.arduino.cc/tools/serial-discovery-0.5.0-x86_64-pc-linux-gnu.tar.bz2", + Size: 1507380, + Checksum: "SHA-256:473cdd9e9f189cfd507b1f6c312d767513da11ec87cdbff1610153d6285e15ce", + CachePath: "tools", + }, + }, + // { + // OS: "i686-mingw32", + // Resource: &resources.DownloadResource{ + // ArchiveFileName: "serial-discovery-1.0.0-i686-mingw32.zip", + // URL: "https://downloads.arduino.cc/tools/serial-discovery-1.0.0-i686-mingw32.zip", + // Size: , + // Checksum: "SHA-256:", + // CachePath: "tools", + // }, + // }, + // { + // OS: "x86_64-apple-darwin", + // Resource: &resources.DownloadResource{ + // ArchiveFileName: "serial-discovery-1.0.0-x86_64-apple-darwin.zip", + // URL: "https://downloads.arduino.cc/tools/serial-discovery-1.0.0-x86_64-apple-darwin.zip", + // Size: , + // Checksum: "SHA-256:", + // CachePath: "tools", + // }, + // }, + // { + // OS: "arm-linux-gnueabihf", + // Resource: &resources.DownloadResource{ + // ArchiveFileName: "serial-discovery-1.0.0-armv6-linux-gnueabihf.tar.bz2", + // URL: "https://downloads.arduino.cc/tools/serial-discovery-1.0.0-armv6-linux-gnueabihf.tar.bz2", + // Size: , + // Checksum: "SHA-256:", + // CachePath: "tools", + // }, + // }, + } +} + +func getBuiltinSerialDiscoveryTool(pm *packagemanager.PackageManager) (*cores.ToolRelease, error) { + return pm.Package("builtin").Tool("serial-discovery").Release(serialDiscoveryVersion).Get() +} From 74a04db965b54ac0bae0d018eec2d96f375e038f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Jan 2019 17:22:29 +0100 Subject: [PATCH 12/12] Removed direct dependency (to make dep check happy) --- Gopkg.lock | 1 - Gopkg.toml | 4 ---- 2 files changed, 5 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 8e7a2ccc610..f55ad3cc418 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -432,7 +432,6 @@ "github.com/arduino/go-win32-utils", "github.com/bgentry/go-netrc/netrc", "github.com/bouk/monkey", - "github.com/codeclysm/cc", "github.com/codeclysm/extract", "github.com/fatih/color", "github.com/gosuri/uitable", diff --git a/Gopkg.toml b/Gopkg.toml index 012e9ab1530..42a9f1ff0a6 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -51,10 +51,6 @@ branch = "master" name = "github.com/bgentry/go-netrc" -[[constraint]] - name = "github.com/codeclysm/cc" - version = "1.2.1" - [[constraint]] branch = "master" name = "github.com/mitchellh/go-homedir"