diff --git a/arduino/errors.go b/arduino/errors.go index b1de65205dd..35085a9423a 100644 --- a/arduino/errors.go +++ b/arduino/errors.go @@ -19,7 +19,6 @@ import ( "fmt" "strings" - "github.com/arduino/arduino-cli/arduino/discovery" "github.com/arduino/arduino-cli/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "google.golang.org/grpc/codes" @@ -125,16 +124,35 @@ func (e *InvalidVersionError) Unwrap() error { return e.Cause } +// NoBoardsDetectedError is returned when detecting the FQBN of a board +// does not produce any result. +type NoBoardsDetectedError struct { + Port *rpc.Port +} + +func (e *NoBoardsDetectedError) Error() string { + return tr( + "Please specify an FQBN. The board on port %[1]s with protocol %[2]s can't be identified", + e.Port.Address, + e.Port.Protocol, + ) +} + +// ToRPCStatus converts the error into a *status.Status +func (e *NoBoardsDetectedError) ToRPCStatus() *status.Status { + return status.New(codes.InvalidArgument, e.Error()) +} + // MultipleBoardsDetectedError is returned when trying to detect // the FQBN of a board connected to a port fails because that // are multiple possible boards detected. type MultipleBoardsDetectedError struct { - Port *discovery.Port + Port *rpc.Port } func (e *MultipleBoardsDetectedError) Error() string { return tr( - "Please specify an FQBN. Multiple possible ports detected on port %s with protocol %s", + "Please specify an FQBN. Multiple possible boards detected on port %[1]s with protocol %[2]s", e.Port.Address, e.Port.Protocol, ) diff --git a/cli/arguments/discovery_timeout.go b/cli/arguments/discovery_timeout.go new file mode 100644 index 00000000000..140c37fcef8 --- /dev/null +++ b/cli/arguments/discovery_timeout.go @@ -0,0 +1,37 @@ +// This file is part of arduino-cli. +// +// Copyright 2022 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 arguments + +import ( + "time" + + "github.com/spf13/cobra" +) + +// DiscoveryTimeout is the timeout given to discoveries to detect ports. +type DiscoveryTimeout struct { + timeout time.Duration +} + +// AddToCommand adds the flags used to set fqbn to the specified Command +func (d *DiscoveryTimeout) AddToCommand(cmd *cobra.Command) { + cmd.Flags().DurationVar(&d.timeout, "discovery-timeout", time.Second, tr("Max time to wait for port discovery, e.g.: 30s, 1m")) +} + +// Get returns the timeout +func (d *DiscoveryTimeout) Get() time.Duration { + return d.timeout +} diff --git a/cli/arguments/fqbn.go b/cli/arguments/fqbn.go index 75862bce001..509a79bb396 100644 --- a/cli/arguments/fqbn.go +++ b/cli/arguments/fqbn.go @@ -16,8 +16,14 @@ package arguments import ( + "os" "strings" + "github.com/arduino/arduino-cli/arduino" + "github.com/arduino/arduino-cli/arduino/sketch" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" ) @@ -54,3 +60,44 @@ func (f *Fqbn) String() string { func (f *Fqbn) Set(fqbn string) { f.fqbn = fqbn } + +// CalculateFQBNAndPort calculate the FQBN and Port metadata based on +// parameters provided by the user. +// This determine the FQBN based on: +// - the value of the FQBN flag if explicitly specified, otherwise +// - the FQBN value in sketch.json if available, otherwise +// - it tries to autodetect the board connected to the given port flags +// If all above methods fails, it returns the empty string. +// The Port metadata are always returned except if: +// - the port is not found, in this case nil is returned +// - the FQBN autodetection fail, in this case the function prints an error and +// terminates the execution +func CalculateFQBNAndPort(portArgs *Port, fqbnArg *Fqbn, instance *rpc.Instance, sk *sketch.Sketch) (string, *rpc.Port) { + // TODO: REMOVE sketch.Sketch from here + + fqbn := fqbnArg.String() + if fqbn == "" && sk != nil && sk.Metadata != nil { + // If the user didn't specify an FQBN and a sketch.json file is present + // read it from there. + fqbn = sk.Metadata.CPU.Fqbn + } + if fqbn == "" { + if portArgs == nil || portArgs.address == "" { + feedback.Error(&arduino.MissingFQBNError{}) + os.Exit(errorcodes.ErrGeneric) + } + fqbn, port := portArgs.DetectFQBN(instance) + if fqbn == "" { + feedback.Error(&arduino.MissingFQBNError{}) + os.Exit(errorcodes.ErrGeneric) + } + return fqbn, port + } + + port, err := portArgs.GetPort(instance, sk) + if err != nil { + feedback.Errorf(tr("Error getting port metadata: %v", err)) + os.Exit(errorcodes.ErrGeneric) + } + return fqbn, port.ToRPC() +} diff --git a/cli/arguments/port.go b/cli/arguments/port.go index b82db2cd9c2..12e7e28f5c7 100644 --- a/cli/arguments/port.go +++ b/cli/arguments/port.go @@ -21,11 +21,13 @@ import ( "os" "time" + "github.com/arduino/arduino-cli/arduino" "github.com/arduino/arduino-cli/arduino/discovery" "github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/arduino-cli/commands" + "github.com/arduino/arduino-cli/commands/board" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -38,7 +40,7 @@ import ( type Port struct { address string protocol string - timeout time.Duration + timeout DiscoveryTimeout } // AddToCommand adds the flags used to set port and protocol to the specified Command @@ -51,7 +53,7 @@ func (p *Port) AddToCommand(cmd *cobra.Command) { cmd.RegisterFlagCompletionFunc("protocol", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return GetInstalledProtocols(), cobra.ShellCompDirectiveDefault }) - cmd.Flags().DurationVar(&p.timeout, "discovery-timeout", 5*time.Second, tr("Max time to wait for port discovery, e.g.: 30s, 1m")) + p.timeout.AddToCommand(cmd) } // GetPortAddressAndProtocol returns only the port address and the port protocol @@ -72,6 +74,9 @@ func (p *Port) GetPortAddressAndProtocol(instance *rpc.Instance, sk *sketch.Sket // GetPort returns the Port obtained by parsing command line arguments. // The extra metadata for the ports is obtained using the pluggable discoveries. func (p *Port) GetPort(instance *rpc.Instance, sk *sketch.Sketch) (*discovery.Port, error) { + // TODO: REMOVE sketch.Sketch from here + // TODO: REMOVE discovery from here (use board.List instead) + address := p.address protocol := p.protocol @@ -122,7 +127,7 @@ func (p *Port) GetPort(instance *rpc.Instance, sk *sketch.Sketch) (*discovery.Po } }() - deadline := time.After(p.timeout) + deadline := time.After(p.timeout.Get()) for { select { case portEvent := <-eventChan: @@ -149,15 +154,38 @@ func (p *Port) GetPort(instance *rpc.Instance, sk *sketch.Sketch) (*discovery.Po // GetSearchTimeout returns the timeout func (p *Port) GetSearchTimeout() time.Duration { - return p.timeout + return p.timeout.Get() } -// GetDiscoveryPort is a helper function useful to get the port and handle possible errors -func (p *Port) GetDiscoveryPort(instance *rpc.Instance, sk *sketch.Sketch) *discovery.Port { - discoveryPort, err := p.GetPort(instance, sk) +// DetectFQBN tries to identify the board connected to the port and returns the +// discovered Port object together with the FQBN. If the port does not match +// exactly 1 board, +func (p *Port) DetectFQBN(inst *rpc.Instance) (string, *rpc.Port) { + detectedPorts, err := board.List(&rpc.BoardListRequest{ + Instance: inst, + Timeout: p.timeout.Get().Milliseconds(), + }) if err != nil { - feedback.Errorf(tr("Error discovering port: %v"), err) + feedback.Errorf(tr("Error during FQBN detection: %v", err)) os.Exit(errorcodes.ErrGeneric) } - return discoveryPort + for _, detectedPort := range detectedPorts { + port := detectedPort.GetPort() + if p.address != port.GetAddress() { + continue + } + if p.protocol != "" && p.protocol != port.GetProtocol() { + continue + } + if len(detectedPort.MatchingBoards) > 1 { + feedback.Error(&arduino.MultipleBoardsDetectedError{Port: port}) + os.Exit(errorcodes.ErrBadArgument) + } + if len(detectedPort.MatchingBoards) == 0 { + feedback.Error(&arduino.NoBoardsDetectedError{Port: port}) + os.Exit(errorcodes.ErrBadArgument) + } + return detectedPort.MatchingBoards[0].Fqbn, port + } + return "", nil } diff --git a/cli/arguments/sketch.go b/cli/arguments/sketch.go index bb14af47ef2..11a93c1963b 100644 --- a/cli/arguments/sketch.go +++ b/cli/arguments/sketch.go @@ -48,7 +48,7 @@ func InitSketchPath(path string) (sketchPath *paths.Path) { func NewSketch(sketchPath *paths.Path) *sketch.Sketch { sketch, err := sketch.New(sketchPath) if err != nil { - feedback.Errorf(tr("Error creating sketch: %v"), err) + feedback.Errorf(tr("Error opening sketch: %v"), err) os.Exit(errorcodes.ErrGeneric) } return sketch diff --git a/cli/board/list.go b/cli/board/list.go index eeae355ba7a..83f4e4418cd 100644 --- a/cli/board/list.go +++ b/cli/board/list.go @@ -19,9 +19,9 @@ import ( "fmt" "os" "sort" - "time" "github.com/arduino/arduino-cli/arduino/cores" + "github.com/arduino/arduino-cli/cli/arguments" "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/arduino-cli/cli/instance" @@ -33,8 +33,8 @@ import ( ) var ( - timeout time.Duration - watch bool + timeoutArg arguments.DiscoveryTimeout + watch bool ) func initListCommand() *cobra.Command { @@ -42,12 +42,12 @@ func initListCommand() *cobra.Command { Use: "list", Short: tr("List connected boards."), Long: tr("Detects and displays a list of boards connected to the current computer."), - Example: " " + os.Args[0] + " board list --timeout 10s", + Example: " " + os.Args[0] + " board list --discovery-timeout 10s", Args: cobra.NoArgs, Run: runListCommand, } - listCommand.Flags().DurationVar(&timeout, "discovery-timeout", time.Second, tr("Max time to wait for port discovery, e.g.: 30s, 1m")) + timeoutArg.AddToCommand(listCommand) listCommand.Flags().BoolVarP(&watch, "watch", "w", false, tr("Command keeps running and prints list of connected boards whenever there is a change.")) return listCommand @@ -66,7 +66,7 @@ func runListCommand(cmd *cobra.Command, args []string) { ports, err := board.List(&rpc.BoardListRequest{ Instance: inst, - Timeout: timeout.Milliseconds(), + Timeout: timeoutArg.Get().Milliseconds(), }) if err != nil { feedback.Errorf(tr("Error detecting boards: %v"), err) diff --git a/cli/compile/compile.go b/cli/compile/compile.go index 867f451482b..3104ff2aa71 100644 --- a/cli/compile/compile.go +++ b/cli/compile/compile.go @@ -26,8 +26,6 @@ import ( "github.com/arduino/arduino-cli/arduino" "github.com/arduino/arduino-cli/arduino/cores/packagemanager" - "github.com/arduino/arduino-cli/arduino/discovery" - "github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/arduino-cli/cli/arguments" "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/arduino-cli/cli/globals" @@ -47,7 +45,7 @@ import ( ) var ( - fqbn arguments.Fqbn // Fully Qualified Board Name, e.g.: arduino:avr:uno. + fqbnArg arguments.Fqbn // Fully Qualified Board Name, e.g.: arduino:avr:uno. showProperties bool // Show all build preferences used instead of compiling. preprocess bool // Print preprocessed code to stdout. buildCachePath string // Builds of 'core.a' are saved into this path to be cached and reused. @@ -61,7 +59,7 @@ var ( quiet bool // Suppresses almost every output. vidPid string // VID/PID specific build properties. uploadAfterCompile bool // Upload the binary after the compilation. - port arguments.Port // Upload port, e.g.: COM10 or /dev/ttyACM0. + portArgs arguments.Port // Upload port, e.g.: COM10 or /dev/ttyACM0. verify bool // Upload, verify uploaded binary after the upload. exportDir string // The compiled binary is written to this file optimizeForDebug bool // Optimize compile output for debug, not for release @@ -92,7 +90,7 @@ func NewCommand() *cobra.Command { Run: runCompileCommand, } - fqbn.AddToCommand(compileCommand) + fqbnArg.AddToCommand(compileCommand) compileCommand.Flags().BoolVar(&showProperties, "show-properties", false, tr("Show all build properties used instead of compiling.")) compileCommand.Flags().BoolVar(&preprocess, "preprocess", false, tr("Print preprocessed code to stdout instead of compiling.")) compileCommand.Flags().StringVar(&buildCachePath, "build-cache-path", "", tr("Builds of 'core.a' are saved into this path to be cached and reused.")) @@ -114,7 +112,7 @@ func NewCommand() *cobra.Command { compileCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, tr("Optional, turns on verbose mode.")) compileCommand.Flags().BoolVar(&quiet, "quiet", false, tr("Optional, suppresses almost every output.")) compileCommand.Flags().BoolVarP(&uploadAfterCompile, "upload", "u", false, tr("Upload the binary after the compilation.")) - port.AddToCommand(compileCommand) + portArgs.AddToCommand(compileCommand) compileCommand.Flags().BoolVarP(&verify, "verify", "t", false, tr("Verify uploaded binary after the upload.")) compileCommand.Flags().StringVar(&vidPid, "vid-pid", "", tr("When specified, VID/PID specific build properties are used, if board supports them.")) compileCommand.Flags().StringSliceVar(&library, "library", []string{}, @@ -150,6 +148,8 @@ func runCompileCommand(cmd *cobra.Command, args []string) { } sketchPath := arguments.InitSketchPath(path) + sk := arguments.NewSketch(sketchPath) + fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, inst, sk) if keysKeychain != "" || signKey != "" || encryptKey != "" { arguments.CheckFlagsMandatory(cmd, "keys-keychain", "sign-key", "encrypt-key") @@ -172,28 +172,9 @@ func runCompileCommand(cmd *cobra.Command, args []string) { overrides = o.Overrides } - detectedFqbn := fqbn.String() - var sk *sketch.Sketch - var discoveryPort *discovery.Port - // If the user didn't provide an FQBN it might either mean - // that she forgot or that is trying to compile and upload - // using board autodetection. - if detectedFqbn == "" && uploadAfterCompile { - sk = arguments.NewSketch(sketchPath) - discoveryPort = port.GetDiscoveryPort(inst, sk) - rpcPort := discoveryPort.ToRPC() - var err error - pm := commands.GetPackageManager(inst.Id) - detectedFqbn, err = upload.DetectConnectedBoard(pm, rpcPort.Address, rpcPort.Protocol) - if err != nil { - feedback.Errorf(tr("Error during FQBN detection: %v", err)) - os.Exit(errorcodes.ErrGeneric) - } - } - compileRequest := &rpc.CompileRequest{ Instance: inst, - Fqbn: detectedFqbn, + Fqbn: fqbn, SketchPath: sketchPath.String(), ShowProperties: showProperties, Preprocess: preprocess, @@ -227,35 +208,27 @@ func runCompileCommand(cmd *cobra.Command, args []string) { } if compileError == nil && uploadAfterCompile { - if sk == nil { - sk = arguments.NewSketch(sketchPath) - } - if discoveryPort == nil { - discoveryPort = port.GetDiscoveryPort(inst, sk) - } - userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{ Instance: inst, - Fqbn: fqbn.String(), - Address: discoveryPort.Address, - Protocol: discoveryPort.Protocol, + Fqbn: fqbn, + Protocol: port.Protocol, }) if err != nil { - feedback.Errorf(tr("Error during Upload: %v"), err) + feedback.Errorf(tr("Error during Upload: %v", err)) os.Exit(errorcodes.ErrGeneric) } fields := map[string]string{} if len(userFieldRes.UserFields) > 0 { - feedback.Print(tr("Uploading to specified board using %s protocol requires the following info:", discoveryPort.Protocol)) + feedback.Print(tr("Uploading to specified board using %s protocol requires the following info:", port.Protocol)) fields = arguments.AskForUserFields(userFieldRes.UserFields) } uploadRequest := &rpc.UploadRequest{ Instance: inst, - Fqbn: detectedFqbn, + Fqbn: fqbn, SketchPath: sketchPath.String(), - Port: discoveryPort.ToRPC(), + Port: port, Verbose: verbose, Verify: verify, ImportDir: buildPath, diff --git a/cli/debug/debug.go b/cli/debug/debug.go index 465fa0674ce..1dd0d56da25 100644 --- a/cli/debug/debug.go +++ b/cli/debug/debug.go @@ -36,8 +36,8 @@ import ( ) var ( - fqbn arguments.Fqbn - port arguments.Port + fqbnArg arguments.Fqbn + portArgs arguments.Port interpreter string importDir string printInfo bool @@ -56,8 +56,8 @@ func NewCommand() *cobra.Command { Run: runDebugCommand, } - fqbn.AddToCommand(debugCommand) - port.AddToCommand(debugCommand) + fqbnArg.AddToCommand(debugCommand) + portArgs.AddToCommand(debugCommand) programmer.AddToCommand(debugCommand) debugCommand.Flags().StringVar(&interpreter, "interpreter", "console", tr("Debug interpreter e.g.: %s", "console, mi, mi1, mi2, mi3")) debugCommand.Flags().StringVarP(&importDir, "input-dir", "", "", tr("Directory containing binaries for debug.")) @@ -77,13 +77,12 @@ func runDebugCommand(command *cobra.Command, args []string) { sketchPath := arguments.InitSketchPath(path) sk := arguments.NewSketch(sketchPath) - discoveryPort := port.GetDiscoveryPort(instance, sk) - + fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, instance, sk) debugConfigRequested := &dbg.DebugConfigRequest{ Instance: instance, - Fqbn: fqbn.String(), + Fqbn: fqbn, SketchPath: sketchPath.String(), - Port: discoveryPort.ToRPC(), + Port: port, Interpreter: interpreter, ImportDir: importDir, Programmer: programmer.String(), @@ -92,7 +91,7 @@ func runDebugCommand(command *cobra.Command, args []string) { if printInfo { if res, err := debug.GetDebugConfig(context.Background(), debugConfigRequested); err != nil { - feedback.Errorf(tr("Error getting Debug info: %v"), err) + feedback.Errorf(tr("Error getting Debug info: %v", err)) os.Exit(errorcodes.ErrBadArgument) } else { feedback.PrintResult(&debugInfoResult{res}) @@ -105,7 +104,7 @@ func runDebugCommand(command *cobra.Command, args []string) { signal.Notify(ctrlc, os.Interrupt) if _, err := debug.Debug(context.Background(), debugConfigRequested, os.Stdin, os.Stdout, ctrlc); err != nil { - feedback.Errorf(tr("Error during Debug: %v"), err) + feedback.Errorf(tr("Error during Debug: %v", err)) os.Exit(errorcodes.ErrGeneric) } diff --git a/cli/upload/upload.go b/cli/upload/upload.go index f0a77a5d4dc..2cec1380641 100644 --- a/cli/upload/upload.go +++ b/cli/upload/upload.go @@ -39,8 +39,8 @@ import ( ) var ( - fqbn arguments.Fqbn - port arguments.Port + fqbnArg arguments.Fqbn + portArgs arguments.Port verbose bool verify bool importDir string @@ -64,8 +64,8 @@ func NewCommand() *cobra.Command { Run: runUploadCommand, } - fqbn.AddToCommand(uploadCommand) - port.AddToCommand(uploadCommand) + fqbnArg.AddToCommand(uploadCommand) + portArgs.AddToCommand(uploadCommand) uploadCommand.Flags().StringVarP(&importDir, "input-dir", "", "", tr("Directory containing binaries to upload.")) uploadCommand.Flags().StringVarP(&importFile, "input-file", "i", "", tr("Binary file to upload.")) uploadCommand.Flags().BoolVarP(&verify, "verify", "t", false, tr("Verify uploaded binary after the upload.")) @@ -95,24 +95,12 @@ func runUploadCommand(command *cobra.Command, args []string) { feedback.Errorf(tr("Error during Upload: %v"), err) os.Exit(errorcodes.ErrGeneric) } - - discoveryPort, err := port.GetPort(instance, sk) - if err != nil { - feedback.Errorf(tr("Error during Upload: %v"), err) - os.Exit(errorcodes.ErrGeneric) - } - - if fqbn.String() == "" && sk != nil && sk.Metadata != nil { - // If the user didn't specify an FQBN and a sketch.json file is present - // read it from there. - fqbn.Set(sk.Metadata.CPU.Fqbn) - } + fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, instance, sk) userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{ Instance: instance, - Fqbn: fqbn.String(), - Address: discoveryPort.Address, - Protocol: discoveryPort.Protocol, + Fqbn: fqbn, + Protocol: port.Protocol, }) if err != nil { feedback.Errorf(tr("Error during Upload: %v"), err) @@ -143,7 +131,7 @@ func runUploadCommand(command *cobra.Command, args []string) { fields := map[string]string{} if len(userFieldRes.UserFields) > 0 { - feedback.Print(tr("Uploading to specified board using %s protocol requires the following info:", discoveryPort.Protocol)) + feedback.Print(tr("Uploading to specified board using %s protocol requires the following info:", port.Protocol)) fields = arguments.AskForUserFields(userFieldRes.UserFields) } @@ -153,9 +141,9 @@ func runUploadCommand(command *cobra.Command, args []string) { if _, err := upload.Upload(context.Background(), &rpc.UploadRequest{ Instance: instance, - Fqbn: fqbn.String(), + Fqbn: fqbn, SketchPath: path, - Port: discoveryPort.ToRPC(), + Port: port, Verbose: verbose, Verify: verify, ImportFile: importFile, diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 886621d0bc7..cfd85c406ce 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -52,16 +52,7 @@ func SupportedUserFields(ctx context.Context, req *rpc.SupportedUserFieldsReques return nil, &arduino.InvalidInstanceError{} } - reqFQBN := req.GetFqbn() - if reqFQBN == "" { - var err error - reqFQBN, err = DetectConnectedBoard(pm, req.Address, req.Protocol) - if err != nil { - return nil, err - } - } - - fqbn, err := cores.ParseFQBN(reqFQBN) + fqbn, err := cores.ParseFQBN(req.GetFqbn()) if err != nil { return nil, &arduino.InvalidFQBNError{Cause: err} } @@ -86,76 +77,6 @@ func SupportedUserFields(ctx context.Context, req *rpc.SupportedUserFieldsReques }, nil } -// DetectConnectedBoard returns the FQBN of the board connected to specified address and protocol. -// In all other cases, like when more than a possible board is detected return an error. -func DetectConnectedBoard(pm *packagemanager.PackageManager, address, protocol string) (string, error) { - if address == "" { - return "", &arduino.MissingPortAddressError{} - } - if protocol == "" { - return "", &arduino.MissingPortProtocolError{} - } - - dm := pm.DiscoveryManager() - - // Run discoveries if they're not already running - if errs := dm.RunAll(); len(errs) > 0 { - // Some discovery managed to not run, just log the errors, - // we'll fail further down the line. - for _, err := range errs { - logrus.Error(err) - } - } - - if errs := dm.StartAll(); len(errs) > 0 { - // Some discovery managed to not start, just log the errors, - // we'll fail further down the line. - for _, err := range errs { - logrus.Error(err) - } - } - - ports, errs := dm.List() - if len(errs) > 0 { - // Errors at this time are not a big issue, we'll - // fail further down the line if we can't find a - // matching board and tell the user to provide - // an FQBN. - for _, err := range errs { - logrus.Error(err) - } - } - - for _, p := range ports { - if p.Address == address && p.Protocol == protocol { - // Found matching port, let's see if we can detect - // which board is connected to it - boards := pm.IdentifyBoard(p.Properties) - if l := len(boards); l == 1 { - // We found only one board connected, that must - // the one the user want to upload to. - board := boards[0] - logrus. - WithField("board", board.String()). - WithField("address", address). - WithField("protocol", protocol). - Trace("Detected board") - return board.FQBN(), nil - } else if l >= 2 { - // There are multiple boards detected on this port, - // we have no way of knowing which one is the one. - return "", &arduino.MultipleBoardsDetectedError{Port: p} - } else { - // We found a matching port but there's either - // no board connected or we can't recognize it. - // The user must provide an FQBN. - break - } - } - } - return "", &arduino.MissingFQBNError{} -} - // getToolID returns the ID of the tool that supports the action and protocol combination by searching in props. // Returns error if tool cannot be found. func getToolID(props *properties.Map, action, protocol string) (string, error) { @@ -271,18 +192,6 @@ func runProgramAction(pm *packagemanager.PackageManager, logrus.WithField("port", port).Tracef("Upload port") - if fqbnIn == "" && sk != nil && sk.Metadata != nil { - fqbnIn = sk.Metadata.CPU.Fqbn - } - - if fqbnIn == "" { - var err error - fqbnIn, err = DetectConnectedBoard(pm, port.Address, port.Protocol) - if err != nil { - return err - } - } - fqbn, err := cores.ParseFQBN(fqbnIn) if err != nil { return &arduino.InvalidFQBNError{Cause: err} diff --git a/docs/UPGRADING.md b/docs/UPGRADING.md index bca31e0b706..798006ae9e2 100644 --- a/docs/UPGRADING.md +++ b/docs/UPGRADING.md @@ -4,6 +4,39 @@ Here you can find a list of migration guides to handle breaking changes between ## 0.22.0 +### `github.com/arduino/arduino-cli/arduino.MultipleBoardsDetectedError` field changed type + +Now the `Port` field of the error is a `github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1.Port`, usually +imported as `rpc.Port`. The old `discovery.Port` can be converted to the new one using the `.ToRPC()` method. + +### Function `github.com/arduino/arduino-cli/commands/upload.DetectConnectedBoard(...)` has been removed + +Use `github.com/arduino/arduino-cli/commands/board.List(...)` to detect boards. + +### Function `arguments.GetDiscoveryPort(...)` has been removed + +NOTE: the functions in the `arguments` package doesn't have much use outside of the `arduino-cli` so we are considering +to remove them from the public golang API making them `internal`. + +The old function: + +```go +func (p *Port) GetDiscoveryPort(instance *rpc.Instance, sk *sketch.Sketch) *discovery.Port { } +``` + +is now replaced by the more powerful: + +```go +func (p *Port) DetectFQBN(inst *rpc.Instance) (string, *rpc.Port) { } + +func CalculateFQBNAndPort(portArgs *Port, fqbnArg *Fqbn, instance *rpc.Instance, sk *sketch.Sketch) (string, *rpc.Port) { } +``` + +### gRPC: `address` parameter has been removed from `commands.SupportedUserFieldsRequest` + +The parameter is no more needed. Lagacy code will continue to work without modification (the value of the parameter will +be just ignored). + ### The content of package `github.com/arduino/arduino-cli/httpclient` has been moved to a different path In particular: diff --git a/rpc/cc/arduino/cli/commands/v1/upload.pb.go b/rpc/cc/arduino/cli/commands/v1/upload.pb.go index e9544a52876..f151a4288ea 100644 --- a/rpc/cc/arduino/cli/commands/v1/upload.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/upload.pb.go @@ -776,9 +776,6 @@ type SupportedUserFieldsRequest struct { // necessary to pick the right upload tool for the board specified // with the FQBN. Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"` - // If an FQBN is not provided but both protocol and address are - // we can try and detect the FQBN using this information. - Address string `protobuf:"bytes,4,opt,name=address,proto3" json:"address,omitempty"` } func (x *SupportedUserFieldsRequest) Reset() { @@ -834,13 +831,6 @@ func (x *SupportedUserFieldsRequest) GetProtocol() string { return "" } -func (x *SupportedUserFieldsRequest) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - type UserField struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1106,7 +1096,7 @@ var file_cc_arduino_cli_commands_v1_upload_proto_rawDesc = []byte{ 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x0b, 0x70, 0x72, 0x6f, - 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x22, 0xa8, 0x01, 0x0a, 0x1a, 0x53, 0x75, 0x70, + 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x1a, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, @@ -1115,27 +1105,25 @@ var file_cc_arduino_cli_commands_v1_upload_proto_rawDesc = []byte{ 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x22, 0x66, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x12, 0x17, 0x0a, 0x07, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x74, 0x6f, 0x6f, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, - 0x62, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x65, 0x0a, 0x1b, 0x53, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x75, 0x73, - 0x65, 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x25, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, - 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, - 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, - 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x66, 0x0a, 0x09, 0x55, 0x73, 0x65, + 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x6f, 0x6f, 0x6c, 0x49, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x22, 0x65, 0x0a, 0x1b, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, + 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x46, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, + 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0a, 0x75, 0x73, + 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, + 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, + 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/rpc/cc/arduino/cli/commands/v1/upload.proto b/rpc/cc/arduino/cli/commands/v1/upload.proto index d7638b26c08..d0aa108c0ef 100644 --- a/rpc/cc/arduino/cli/commands/v1/upload.proto +++ b/rpc/cc/arduino/cli/commands/v1/upload.proto @@ -162,9 +162,6 @@ message SupportedUserFieldsRequest { // necessary to pick the right upload tool for the board specified // with the FQBN. string protocol = 3; - // If an FQBN is not provided but both protocol and address are - // we can try and detect the FQBN using this information. - string address = 4; } message UserField { diff --git a/test/test_compile_part_1.py b/test/test_compile_part_1.py index 90579a79f20..e53c6360b37 100644 --- a/test/test_compile_part_1.py +++ b/test/test_compile_part_1.py @@ -131,7 +131,7 @@ def test_compile_with_sketch_with_symlink_selfloop(run_command, data_dir): result = run_command(["compile", "-b", fqbn, sketch_path]) # The assertion is a bit relaxed in this case because win behaves differently from macOs and linux # returning a different error detailed message - assert "Error during build: Can't open sketch:" in result.stderr + assert "Error opening sketch:" in result.stderr assert not result.ok sketch_name = "CompileIntegrationTestSymlinkDirLoop" @@ -153,7 +153,7 @@ def test_compile_with_sketch_with_symlink_selfloop(run_command, data_dir): result = run_command(["compile", "-b", fqbn, sketch_path]) # The assertion is a bit relaxed in this case because win behaves differently from macOs and linux # returning a different error detailed message - assert "Error during build: Can't open sketch:" in result.stderr + assert "Error opening sketch:" in result.stderr assert not result.ok diff --git a/test/test_compile_part_3.py b/test/test_compile_part_3.py index d2385c8b5f6..69b50864542 100644 --- a/test/test_compile_part_3.py +++ b/test/test_compile_part_3.py @@ -91,17 +91,17 @@ def test_compile_sketch_with_multiple_main_files(run_command, data_dir): # Build sketch from folder res = run_command(["compile", "--clean", "-b", fqbn, sketch_path]) assert res.failed - assert "Error during build: Can't open sketch: multiple main sketch files found" in res.stderr + assert "Error opening sketch: multiple main sketch files found" in res.stderr # Build sketch from .ino file res = run_command(["compile", "--clean", "-b", fqbn, sketch_ino_file]) assert res.failed - assert "Error during build: Can't open sketch: multiple main sketch files found" in res.stderr + assert "Error opening sketch: multiple main sketch files found" in res.stderr # Build sketch from .pde file res = run_command(["compile", "--clean", "-b", fqbn, sketch_pde_file]) assert res.failed - assert "Error during build: Can't open sketch: multiple main sketch files found" in res.stderr + assert "Error opening sketch: multiple main sketch files found" in res.stderr def test_compile_sketch_case_mismatch_fails(run_command, data_dir): @@ -124,15 +124,15 @@ def test_compile_sketch_case_mismatch_fails(run_command, data_dir): # * Compiling with sketch path res = run_command(["compile", "--clean", "-b", fqbn, sketch_path]) assert res.failed - assert "Error during build: Can't open sketch: no valid sketch found" in res.stderr + assert "Error opening sketch: no valid sketch found" in res.stderr # * Compiling with sketch main file res = run_command(["compile", "--clean", "-b", fqbn, sketch_main_file]) assert res.failed - assert "Error during build: Can't open sketch: no valid sketch found" in res.stderr + assert "Error opening sketch: no valid sketch found" in res.stderr # * Compiling in sketch path res = run_command(["compile", "--clean", "-b", fqbn], custom_working_dir=sketch_path) assert res.failed - assert "Error during build: Can't open sketch: no valid sketch found" in res.stderr + assert "Error opening sketch: no valid sketch found" in res.stderr def test_compile_with_only_compilation_database_flag(run_command, data_dir): diff --git a/test/test_compile_part_4.py b/test/test_compile_part_4.py index bdc85d8cb9e..2066811a1d1 100644 --- a/test/test_compile_part_4.py +++ b/test/test_compile_part_4.py @@ -377,7 +377,7 @@ def test_compile_without_upload_and_fqbn(run_command, data_dir): res = run_command(["compile", sketch_path]) assert res.failed - assert "Error during build: Missing FQBN (Fully Qualified Board Name)" in res.stderr + assert "Missing FQBN (Fully Qualified Board Name)" in res.stderr def test_compile_non_installed_platform_with_wrong_packager_and_arch(run_command, data_dir): diff --git a/test/test_upload.py b/test/test_upload.py index 3020a50c210..a6eed7f249d 100644 --- a/test/test_upload.py +++ b/test/test_upload.py @@ -364,7 +364,7 @@ def test_compile_and_upload_combo_sketch_with_mismatched_casing(run_command, dat # Try to compile res = run_command(["compile", "--clean", "-b", board.fqbn, "-u", "-p", board.address, sketch_path]) assert res.failed - assert "Error during build: Can't open sketch: no valid sketch found in" in res.stderr + assert "Error opening sketch: no valid sketch found in" in res.stderr def test_upload_sketch_with_mismatched_casing(run_command, data_dir, detected_boards, wait_for_board):