From 99ef6e497d91b617725ebf35d36a152218e2e5de Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Thu, 2 Dec 2021 18:20:00 +0100 Subject: [PATCH] Add board autodetection on upload --- arduino/errors.go | 33 +++++++ cli/compile/compile.go | 35 +++++++- cli/upload/upload.go | 1 + commands/upload/upload.go | 89 ++++++++++++++++++- rpc/cc/arduino/cli/commands/v1/commands.pb.go | 7 +- .../cli/commands/v1/commands_grpc.pb.go | 54 ++++++----- rpc/cc/arduino/cli/commands/v1/upload.pb.go | 52 ++++++----- rpc/cc/arduino/cli/commands/v1/upload.proto | 3 + test/test_compile_part_4.py | 13 +++ test/test_upload.py | 36 ++++++++ 10 files changed, 265 insertions(+), 58 deletions(-) diff --git a/arduino/errors.go b/arduino/errors.go index 2b4e34dba5b..f5c9356760d 100644 --- a/arduino/errors.go +++ b/arduino/errors.go @@ -18,6 +18,7 @@ package arduino import ( "fmt" + "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" @@ -123,6 +124,26 @@ func (e *InvalidVersionError) Unwrap() error { return e.Cause } +// 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 +} + +func (e *MultipleBoardsDetectedError) Error() string { + return tr( + "Please specify an FQBN. Multiple possible ports detected on port %s with protocol %s", + e.Port.Address, + e.Port.Protocol, + ) +} + +// ToRPCStatus converts the error into a *status.Status +func (e *MultipleBoardsDetectedError) ToRPCStatus() *status.Status { + return status.New(codes.InvalidArgument, e.Error()) +} + // MissingFQBNError is returned when the FQBN is mandatory and not specified type MissingFQBNError struct{} @@ -153,6 +174,18 @@ func (e *UnknownFQBNError) ToRPCStatus() *status.Status { return status.New(codes.NotFound, e.Error()) } +// MissingPortAddressError is returned when the port protocol is mandatory and not specified +type MissingPortAddressError struct{} + +func (e *MissingPortAddressError) Error() string { + return tr("Missing port protocol") +} + +// ToRPCStatus converts the error into a *status.Status +func (e *MissingPortAddressError) ToRPCStatus() *status.Status { + return status.New(codes.InvalidArgument, e.Error()) +} + // MissingPortProtocolError is returned when the port protocol is mandatory and not specified type MissingPortProtocolError struct{} diff --git a/cli/compile/compile.go b/cli/compile/compile.go index 0563f8501db..aa76f807644 100644 --- a/cli/compile/compile.go +++ b/cli/compile/compile.go @@ -21,9 +21,12 @@ import ( "encoding/json" "os" + "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/output" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/configuration" "github.com/arduino/arduino-cli/i18n" "github.com/sirupsen/logrus" @@ -150,9 +153,28 @@ 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: fqbn.String(), + Fqbn: detectedFqbn, SketchPath: sketchPath.String(), ShowProperties: showProperties, Preprocess: preprocess, @@ -183,12 +205,17 @@ func runCompileCommand(cmd *cobra.Command, args []string) { } if compileError == nil && uploadAfterCompile { - sk := arguments.NewSketch(sketchPath) - discoveryPort := port.GetDiscoveryPort(inst, sk) + 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, }) if err != nil { @@ -204,7 +231,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { uploadRequest := &rpc.UploadRequest{ Instance: inst, - Fqbn: fqbn.String(), + Fqbn: detectedFqbn, SketchPath: sketchPath.String(), Port: discoveryPort.ToRPC(), Verbose: verbose, diff --git a/cli/upload/upload.go b/cli/upload/upload.go index 3580ba2ade2..c333795cfef 100644 --- a/cli/upload/upload.go +++ b/cli/upload/upload.go @@ -104,6 +104,7 @@ func runUploadCommand(command *cobra.Command, args []string) { userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{ Instance: instance, Fqbn: fqbn.String(), + Address: discoveryPort.Address, Protocol: discoveryPort.Protocol, }) if err != nil { diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 0d6ca7aba72..2fe21e35a7d 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -52,7 +52,16 @@ func SupportedUserFields(ctx context.Context, req *rpc.SupportedUserFieldsReques return nil, &arduino.InvalidInstanceError{} } - fqbn, err := cores.ParseFQBN(req.GetFqbn()) + 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) if err != nil { return nil, &arduino.InvalidFQBNError{Cause: err} } @@ -72,6 +81,76 @@ 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) { @@ -190,9 +269,15 @@ func runProgramAction(pm *packagemanager.PackageManager, if fqbnIn == "" && sk != nil && sk.Metadata != nil { fqbnIn = sk.Metadata.CPU.Fqbn } + if fqbnIn == "" { - return &arduino.MissingFQBNError{} + 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/rpc/cc/arduino/cli/commands/v1/commands.pb.go b/rpc/cc/arduino/cli/commands/v1/commands.pb.go index 5e7da05716c..3cac392ea2c 100644 --- a/rpc/cc/arduino/cli/commands/v1/commands.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/commands.pb.go @@ -938,9 +938,10 @@ type NewSketchRequest struct { Instance *Instance `protobuf:"bytes,1,opt,name=instance,proto3" json:"instance,omitempty"` // New sketch name SketchName string `protobuf:"bytes,2,opt,name=sketch_name,json=sketchName,proto3" json:"sketch_name,omitempty"` - // Optional: create a sketch directory in this directory - // (used as "sketches" directory), the directory must exist. - // "directories.User" directory will be used otherwise. + // Optional: create a Sketch in this directory + // (used as "Sketchbook" directory). + // Default Sketchbook directory "directories.User" is used if sketch_dir is + // empty. SketchDir string `protobuf:"bytes,3,opt,name=sketch_dir,json=sketchDir,proto3" json:"sketch_dir,omitempty"` } diff --git a/rpc/cc/arduino/cli/commands/v1/commands_grpc.pb.go b/rpc/cc/arduino/cli/commands/v1/commands_grpc.pb.go index 452552c93ed..636c3229d1c 100644 --- a/rpc/cc/arduino/cli/commands/v1/commands_grpc.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/commands_grpc.pb.go @@ -11,7 +11,6 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // ArduinoCoreServiceClient is the client API for ArduinoCoreService service. @@ -127,7 +126,7 @@ func (c *arduinoCoreServiceClient) Create(ctx context.Context, in *CreateRequest } func (c *arduinoCoreServiceClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (ArduinoCoreService_InitClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[0], "/cc.arduino.cli.commands.v1.ArduinoCoreService/Init", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[0], "/cc.arduino.cli.commands.v1.ArduinoCoreService/Init", opts...) if err != nil { return nil, err } @@ -168,7 +167,7 @@ func (c *arduinoCoreServiceClient) Destroy(ctx context.Context, in *DestroyReque } func (c *arduinoCoreServiceClient) UpdateIndex(ctx context.Context, in *UpdateIndexRequest, opts ...grpc.CallOption) (ArduinoCoreService_UpdateIndexClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[1], "/cc.arduino.cli.commands.v1.ArduinoCoreService/UpdateIndex", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[1], "/cc.arduino.cli.commands.v1.ArduinoCoreService/UpdateIndex", opts...) if err != nil { return nil, err } @@ -200,7 +199,7 @@ func (x *arduinoCoreServiceUpdateIndexClient) Recv() (*UpdateIndexResponse, erro } func (c *arduinoCoreServiceClient) UpdateLibrariesIndex(ctx context.Context, in *UpdateLibrariesIndexRequest, opts ...grpc.CallOption) (ArduinoCoreService_UpdateLibrariesIndexClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[2], "/cc.arduino.cli.commands.v1.ArduinoCoreService/UpdateLibrariesIndex", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[2], "/cc.arduino.cli.commands.v1.ArduinoCoreService/UpdateLibrariesIndex", opts...) if err != nil { return nil, err } @@ -232,7 +231,7 @@ func (x *arduinoCoreServiceUpdateLibrariesIndexClient) Recv() (*UpdateLibrariesI } func (c *arduinoCoreServiceClient) UpdateCoreLibrariesIndex(ctx context.Context, in *UpdateCoreLibrariesIndexRequest, opts ...grpc.CallOption) (ArduinoCoreService_UpdateCoreLibrariesIndexClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[3], "/cc.arduino.cli.commands.v1.ArduinoCoreService/UpdateCoreLibrariesIndex", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[3], "/cc.arduino.cli.commands.v1.ArduinoCoreService/UpdateCoreLibrariesIndex", opts...) if err != nil { return nil, err } @@ -273,7 +272,7 @@ func (c *arduinoCoreServiceClient) Outdated(ctx context.Context, in *OutdatedReq } func (c *arduinoCoreServiceClient) Upgrade(ctx context.Context, in *UpgradeRequest, opts ...grpc.CallOption) (ArduinoCoreService_UpgradeClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[4], "/cc.arduino.cli.commands.v1.ArduinoCoreService/Upgrade", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[4], "/cc.arduino.cli.commands.v1.ArduinoCoreService/Upgrade", opts...) if err != nil { return nil, err } @@ -350,7 +349,7 @@ func (c *arduinoCoreServiceClient) BoardDetails(ctx context.Context, in *BoardDe } func (c *arduinoCoreServiceClient) BoardAttach(ctx context.Context, in *BoardAttachRequest, opts ...grpc.CallOption) (ArduinoCoreService_BoardAttachClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[5], "/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardAttach", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[5], "/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardAttach", opts...) if err != nil { return nil, err } @@ -409,7 +408,7 @@ func (c *arduinoCoreServiceClient) BoardSearch(ctx context.Context, in *BoardSea } func (c *arduinoCoreServiceClient) BoardListWatch(ctx context.Context, opts ...grpc.CallOption) (ArduinoCoreService_BoardListWatchClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[6], "/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardListWatch", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[6], "/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardListWatch", opts...) if err != nil { return nil, err } @@ -440,7 +439,7 @@ func (x *arduinoCoreServiceBoardListWatchClient) Recv() (*BoardListWatchResponse } func (c *arduinoCoreServiceClient) Compile(ctx context.Context, in *CompileRequest, opts ...grpc.CallOption) (ArduinoCoreService_CompileClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[7], "/cc.arduino.cli.commands.v1.ArduinoCoreService/Compile", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[7], "/cc.arduino.cli.commands.v1.ArduinoCoreService/Compile", opts...) if err != nil { return nil, err } @@ -472,7 +471,7 @@ func (x *arduinoCoreServiceCompileClient) Recv() (*CompileResponse, error) { } func (c *arduinoCoreServiceClient) PlatformInstall(ctx context.Context, in *PlatformInstallRequest, opts ...grpc.CallOption) (ArduinoCoreService_PlatformInstallClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[8], "/cc.arduino.cli.commands.v1.ArduinoCoreService/PlatformInstall", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[8], "/cc.arduino.cli.commands.v1.ArduinoCoreService/PlatformInstall", opts...) if err != nil { return nil, err } @@ -504,7 +503,7 @@ func (x *arduinoCoreServicePlatformInstallClient) Recv() (*PlatformInstallRespon } func (c *arduinoCoreServiceClient) PlatformDownload(ctx context.Context, in *PlatformDownloadRequest, opts ...grpc.CallOption) (ArduinoCoreService_PlatformDownloadClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[9], "/cc.arduino.cli.commands.v1.ArduinoCoreService/PlatformDownload", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[9], "/cc.arduino.cli.commands.v1.ArduinoCoreService/PlatformDownload", opts...) if err != nil { return nil, err } @@ -536,7 +535,7 @@ func (x *arduinoCoreServicePlatformDownloadClient) Recv() (*PlatformDownloadResp } func (c *arduinoCoreServiceClient) PlatformUninstall(ctx context.Context, in *PlatformUninstallRequest, opts ...grpc.CallOption) (ArduinoCoreService_PlatformUninstallClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[10], "/cc.arduino.cli.commands.v1.ArduinoCoreService/PlatformUninstall", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[10], "/cc.arduino.cli.commands.v1.ArduinoCoreService/PlatformUninstall", opts...) if err != nil { return nil, err } @@ -568,7 +567,7 @@ func (x *arduinoCoreServicePlatformUninstallClient) Recv() (*PlatformUninstallRe } func (c *arduinoCoreServiceClient) PlatformUpgrade(ctx context.Context, in *PlatformUpgradeRequest, opts ...grpc.CallOption) (ArduinoCoreService_PlatformUpgradeClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[11], "/cc.arduino.cli.commands.v1.ArduinoCoreService/PlatformUpgrade", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[11], "/cc.arduino.cli.commands.v1.ArduinoCoreService/PlatformUpgrade", opts...) if err != nil { return nil, err } @@ -600,7 +599,7 @@ func (x *arduinoCoreServicePlatformUpgradeClient) Recv() (*PlatformUpgradeRespon } func (c *arduinoCoreServiceClient) Upload(ctx context.Context, in *UploadRequest, opts ...grpc.CallOption) (ArduinoCoreService_UploadClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[12], "/cc.arduino.cli.commands.v1.ArduinoCoreService/Upload", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[12], "/cc.arduino.cli.commands.v1.ArduinoCoreService/Upload", opts...) if err != nil { return nil, err } @@ -632,7 +631,7 @@ func (x *arduinoCoreServiceUploadClient) Recv() (*UploadResponse, error) { } func (c *arduinoCoreServiceClient) UploadUsingProgrammer(ctx context.Context, in *UploadUsingProgrammerRequest, opts ...grpc.CallOption) (ArduinoCoreService_UploadUsingProgrammerClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[13], "/cc.arduino.cli.commands.v1.ArduinoCoreService/UploadUsingProgrammer", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[13], "/cc.arduino.cli.commands.v1.ArduinoCoreService/UploadUsingProgrammer", opts...) if err != nil { return nil, err } @@ -682,7 +681,7 @@ func (c *arduinoCoreServiceClient) ListProgrammersAvailableForUpload(ctx context } func (c *arduinoCoreServiceClient) BurnBootloader(ctx context.Context, in *BurnBootloaderRequest, opts ...grpc.CallOption) (ArduinoCoreService_BurnBootloaderClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[14], "/cc.arduino.cli.commands.v1.ArduinoCoreService/BurnBootloader", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[14], "/cc.arduino.cli.commands.v1.ArduinoCoreService/BurnBootloader", opts...) if err != nil { return nil, err } @@ -732,7 +731,7 @@ func (c *arduinoCoreServiceClient) PlatformList(ctx context.Context, in *Platfor } func (c *arduinoCoreServiceClient) LibraryDownload(ctx context.Context, in *LibraryDownloadRequest, opts ...grpc.CallOption) (ArduinoCoreService_LibraryDownloadClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[15], "/cc.arduino.cli.commands.v1.ArduinoCoreService/LibraryDownload", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[15], "/cc.arduino.cli.commands.v1.ArduinoCoreService/LibraryDownload", opts...) if err != nil { return nil, err } @@ -764,7 +763,7 @@ func (x *arduinoCoreServiceLibraryDownloadClient) Recv() (*LibraryDownloadRespon } func (c *arduinoCoreServiceClient) LibraryInstall(ctx context.Context, in *LibraryInstallRequest, opts ...grpc.CallOption) (ArduinoCoreService_LibraryInstallClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[16], "/cc.arduino.cli.commands.v1.ArduinoCoreService/LibraryInstall", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[16], "/cc.arduino.cli.commands.v1.ArduinoCoreService/LibraryInstall", opts...) if err != nil { return nil, err } @@ -796,7 +795,7 @@ func (x *arduinoCoreServiceLibraryInstallClient) Recv() (*LibraryInstallResponse } func (c *arduinoCoreServiceClient) ZipLibraryInstall(ctx context.Context, in *ZipLibraryInstallRequest, opts ...grpc.CallOption) (ArduinoCoreService_ZipLibraryInstallClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[17], "/cc.arduino.cli.commands.v1.ArduinoCoreService/ZipLibraryInstall", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[17], "/cc.arduino.cli.commands.v1.ArduinoCoreService/ZipLibraryInstall", opts...) if err != nil { return nil, err } @@ -828,7 +827,7 @@ func (x *arduinoCoreServiceZipLibraryInstallClient) Recv() (*ZipLibraryInstallRe } func (c *arduinoCoreServiceClient) GitLibraryInstall(ctx context.Context, in *GitLibraryInstallRequest, opts ...grpc.CallOption) (ArduinoCoreService_GitLibraryInstallClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[18], "/cc.arduino.cli.commands.v1.ArduinoCoreService/GitLibraryInstall", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[18], "/cc.arduino.cli.commands.v1.ArduinoCoreService/GitLibraryInstall", opts...) if err != nil { return nil, err } @@ -860,7 +859,7 @@ func (x *arduinoCoreServiceGitLibraryInstallClient) Recv() (*GitLibraryInstallRe } func (c *arduinoCoreServiceClient) LibraryUninstall(ctx context.Context, in *LibraryUninstallRequest, opts ...grpc.CallOption) (ArduinoCoreService_LibraryUninstallClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[19], "/cc.arduino.cli.commands.v1.ArduinoCoreService/LibraryUninstall", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[19], "/cc.arduino.cli.commands.v1.ArduinoCoreService/LibraryUninstall", opts...) if err != nil { return nil, err } @@ -892,7 +891,7 @@ func (x *arduinoCoreServiceLibraryUninstallClient) Recv() (*LibraryUninstallResp } func (c *arduinoCoreServiceClient) LibraryUpgradeAll(ctx context.Context, in *LibraryUpgradeAllRequest, opts ...grpc.CallOption) (ArduinoCoreService_LibraryUpgradeAllClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[20], "/cc.arduino.cli.commands.v1.ArduinoCoreService/LibraryUpgradeAll", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[20], "/cc.arduino.cli.commands.v1.ArduinoCoreService/LibraryUpgradeAll", opts...) if err != nil { return nil, err } @@ -951,7 +950,7 @@ func (c *arduinoCoreServiceClient) LibraryList(ctx context.Context, in *LibraryL } func (c *arduinoCoreServiceClient) Monitor(ctx context.Context, opts ...grpc.CallOption) (ArduinoCoreService_MonitorClient, error) { - stream, err := c.cc.NewStream(ctx, &ArduinoCoreService_ServiceDesc.Streams[21], "/cc.arduino.cli.commands.v1.ArduinoCoreService/Monitor", opts...) + stream, err := c.cc.NewStream(ctx, &_ArduinoCoreService_serviceDesc.Streams[21], "/cc.arduino.cli.commands.v1.ArduinoCoreService/Monitor", opts...) if err != nil { return nil, err } @@ -1222,8 +1221,8 @@ type UnsafeArduinoCoreServiceServer interface { mustEmbedUnimplementedArduinoCoreServiceServer() } -func RegisterArduinoCoreServiceServer(s grpc.ServiceRegistrar, srv ArduinoCoreServiceServer) { - s.RegisterService(&ArduinoCoreService_ServiceDesc, srv) +func RegisterArduinoCoreServiceServer(s *grpc.Server, srv ArduinoCoreServiceServer) { + s.RegisterService(&_ArduinoCoreService_serviceDesc, srv) } func _ArduinoCoreService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { @@ -2040,10 +2039,7 @@ func _ArduinoCoreService_EnumerateMonitorPortSettings_Handler(srv interface{}, c return interceptor(ctx, in, info, handler) } -// ArduinoCoreService_ServiceDesc is the grpc.ServiceDesc for ArduinoCoreService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var ArduinoCoreService_ServiceDesc = grpc.ServiceDesc{ +var _ArduinoCoreService_serviceDesc = grpc.ServiceDesc{ ServiceName: "cc.arduino.cli.commands.v1.ArduinoCoreService", HandlerType: (*ArduinoCoreServiceServer)(nil), Methods: []grpc.MethodDesc{ diff --git a/rpc/cc/arduino/cli/commands/v1/upload.pb.go b/rpc/cc/arduino/cli/commands/v1/upload.pb.go index f151a4288ea..e9544a52876 100644 --- a/rpc/cc/arduino/cli/commands/v1/upload.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/upload.pb.go @@ -776,6 +776,9 @@ 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() { @@ -831,6 +834,13 @@ 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 @@ -1096,7 +1106,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, 0x8e, 0x01, 0x0a, 0x1a, 0x53, 0x75, 0x70, + 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x22, 0xa8, 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, @@ -1105,25 +1115,27 @@ 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, 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, 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, } var ( diff --git a/rpc/cc/arduino/cli/commands/v1/upload.proto b/rpc/cc/arduino/cli/commands/v1/upload.proto index d0aa108c0ef..d7638b26c08 100644 --- a/rpc/cc/arduino/cli/commands/v1/upload.proto +++ b/rpc/cc/arduino/cli/commands/v1/upload.proto @@ -162,6 +162,9 @@ 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_4.py b/test/test_compile_part_4.py index 62c6074aecb..b3ad168ca1c 100644 --- a/test/test_compile_part_4.py +++ b/test/test_compile_part_4.py @@ -365,3 +365,16 @@ def test_compile_with_relative_build_path(run_command, data_dir, copy_sketch): assert "libraries" in built_files assert "preproc" in built_files assert "sketch" in built_files + + +def test_compile_without_upload_and_fqbn(run_command, data_dir): + assert run_command(["update"]) + + # Create a sketch + sketch_name = "SketchSimple" + sketch_path = Path(data_dir, sketch_name) + assert run_command(["sketch", "new", sketch_path]) + + res = run_command(["compile", sketch_path]) + assert res.failed + assert "Error during build: Missing FQBN (Fully Qualified Board Name)" in res.stderr diff --git a/test/test_upload.py b/test/test_upload.py index 2edc9ec7e8b..3020a50c210 100644 --- a/test/test_upload.py +++ b/test/test_upload.py @@ -388,3 +388,39 @@ def test_upload_sketch_with_mismatched_casing(run_command, data_dir, detected_bo res = run_command(["upload", "-b", board.fqbn, "-p", board.address, sketch_path]) assert res.failed assert "Error during Upload: no valid sketch found in" in res.stderr + + +def test_upload_to_port_with_board_autodetect(run_command, data_dir, detected_boards): + assert run_command(["update"]) + + # Create a sketch + sketch_name = "SketchSimple" + sketch_path = Path(data_dir, sketch_name) + assert run_command(["sketch", "new", sketch_path]) + + for board in detected_boards: + # Install core + core = ":".join(board.fqbn.split(":")[:2]) + assert run_command(["core", "install", core]) + + assert run_command(["compile", "-b", board.fqbn, sketch_path]) + + res = run_command(["upload", "-p", board.address, sketch_path]) + assert res.ok + + +def test_compile_and_upload_to_port_with_board_autodetect(run_command, data_dir, detected_boards): + assert run_command(["update"]) + + # Create a sketch + sketch_name = "SketchSimple" + sketch_path = Path(data_dir, sketch_name) + assert run_command(["sketch", "new", sketch_path]) + + for board in detected_boards: + # Install core + core = ":".join(board.fqbn.split(":")[:2]) + assert run_command(["core", "install", core]) + + res = run_command(["compile", "-u", "-p", board.address, sketch_path]) + assert res.ok