Skip to content

Commit da903d4

Browse files
committed
Refactored board autodetection in upload/compile
- use `board.List()` instead of implementing a duplicate of it - factored the logic that calculates FQBN, Port and possibly autodetects FQBN, so we have a single implementation for all commands of the cli.
1 parent 6f7c9ee commit da903d4

File tree

6 files changed

+86
-146
lines changed

6 files changed

+86
-146
lines changed

cli/arguments/fqbn.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@
1616
package arguments
1717

1818
import (
19+
"os"
1920
"strings"
2021

22+
"github.com/arduino/arduino-cli/arduino"
23+
"github.com/arduino/arduino-cli/arduino/sketch"
24+
"github.com/arduino/arduino-cli/cli/errorcodes"
25+
"github.com/arduino/arduino-cli/cli/feedback"
26+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2127
"github.com/spf13/cobra"
2228
)
2329

@@ -54,3 +60,44 @@ func (f *Fqbn) String() string {
5460
func (f *Fqbn) Set(fqbn string) {
5561
f.fqbn = fqbn
5662
}
63+
64+
// CalculateFQBNAndPort calculate the FQBN and Port metadata based on
65+
// parameters provided by the user.
66+
// This determine the FQBN based on:
67+
// - the value of the FQBN flag if explicitly specified, otherwise
68+
// - the FQBN value in sketch.json if available, otherwise
69+
// - it tries to autodetect the board connected to the given port flags
70+
// If all above methods fails, it returns the empty string.
71+
// The Port metadata are always returned except if:
72+
// - the port is not found, in this case nil is returned
73+
// - the FQBN autodetection fail, in this case the function prints an error and
74+
// terminates the execution
75+
func CalculateFQBNAndPort(portArgs *Port, fqbnArg *Fqbn, instance *rpc.Instance, sk *sketch.Sketch) (string, *rpc.Port) {
76+
// TODO: REMOVE sketch.Sketch from here
77+
78+
fqbn := fqbnArg.String()
79+
if fqbn == "" && sk != nil && sk.Metadata != nil {
80+
// If the user didn't specify an FQBN and a sketch.json file is present
81+
// read it from there.
82+
fqbn = sk.Metadata.CPU.Fqbn
83+
}
84+
if fqbn == "" {
85+
if portArgs == nil || portArgs.address == "" {
86+
feedback.Error(&arduino.MissingFQBNError{})
87+
os.Exit(errorcodes.ErrGeneric)
88+
}
89+
fqbn, port := portArgs.DetectFQBN(instance)
90+
if fqbn == "" {
91+
feedback.Error(&arduino.MissingFQBNError{})
92+
os.Exit(errorcodes.ErrGeneric)
93+
}
94+
return fqbn, port
95+
}
96+
97+
port, err := portArgs.GetPort(instance, sk)
98+
if err != nil {
99+
feedback.Errorf(tr("Error getting port metadata: %v", err))
100+
os.Exit(errorcodes.ErrGeneric)
101+
}
102+
return fqbn, port.ToRPC()
103+
}

cli/arguments/port.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ import (
2121
"os"
2222
"time"
2323

24+
"github.com/arduino/arduino-cli/arduino"
2425
"github.com/arduino/arduino-cli/arduino/discovery"
2526
"github.com/arduino/arduino-cli/arduino/sketch"
2627
"github.com/arduino/arduino-cli/cli/errorcodes"
2728
"github.com/arduino/arduino-cli/cli/feedback"
2829
"github.com/arduino/arduino-cli/commands"
30+
"github.com/arduino/arduino-cli/commands/board"
2931
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3032
"github.com/pkg/errors"
3133
"github.com/sirupsen/logrus"
@@ -152,12 +154,32 @@ func (p *Port) GetSearchTimeout() time.Duration {
152154
return p.timeout
153155
}
154156

155-
// GetDiscoveryPort is a helper function useful to get the port and handle possible errors
156-
func (p *Port) GetDiscoveryPort(instance *rpc.Instance, sk *sketch.Sketch) *discovery.Port {
157-
discoveryPort, err := p.GetPort(instance, sk)
157+
// DetectFQBN tries to identify the board connected to the port and returns the
158+
// discovered Port object together with the FQBN. If the port does not match
159+
// exactly 1 board,
160+
func (p *Port) DetectFQBN(inst *rpc.Instance) (string, *rpc.Port) {
161+
detectedPorts, err := board.List(&rpc.BoardListRequest{Instance: inst})
158162
if err != nil {
159-
feedback.Errorf(tr("Error discovering port: %v"), err)
163+
feedback.Errorf(tr("Error during FQBN detection: %v", err))
160164
os.Exit(errorcodes.ErrGeneric)
161165
}
162-
return discoveryPort
166+
for _, detectedPort := range detectedPorts {
167+
port := detectedPort.GetPort()
168+
if p.address != port.GetAddress() {
169+
continue
170+
}
171+
if p.protocol != "" && p.protocol != port.GetProtocol() {
172+
continue
173+
}
174+
if len(detectedPort.MatchingBoards) > 1 {
175+
feedback.Error(&arduino.MultipleBoardsDetectedError{Port: port})
176+
os.Exit(errorcodes.ErrBadArgument)
177+
}
178+
if len(detectedPort.MatchingBoards) == 0 {
179+
feedback.Error(&arduino.NoBoardsDetectedError{Port: port})
180+
os.Exit(errorcodes.ErrBadArgument)
181+
}
182+
return detectedPort.MatchingBoards[0].Fqbn, port
183+
}
184+
return "", nil
163185
}

cli/compile/compile.go

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import (
2626

2727
"github.com/arduino/arduino-cli/arduino"
2828
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
29-
"github.com/arduino/arduino-cli/arduino/discovery"
30-
"github.com/arduino/arduino-cli/arduino/sketch"
3129
"github.com/arduino/arduino-cli/cli/arguments"
3230
"github.com/arduino/arduino-cli/cli/feedback"
3331
"github.com/arduino/arduino-cli/cli/globals"
@@ -150,6 +148,8 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
150148
}
151149

152150
sketchPath := arguments.InitSketchPath(path)
151+
sk := arguments.NewSketch(sketchPath)
152+
fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, inst, sk)
153153

154154
if keysKeychain != "" || signKey != "" || encryptKey != "" {
155155
arguments.CheckFlagsMandatory(cmd, "keys-keychain", "sign-key", "encrypt-key")
@@ -172,25 +172,6 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
172172
overrides = o.Overrides
173173
}
174174

175-
fqbn := fqbnArg.String()
176-
var sk *sketch.Sketch
177-
var port *discovery.Port
178-
// If the user didn't provide an FQBN it might either mean
179-
// that she forgot or that is trying to compile and upload
180-
// using board autodetection.
181-
if fqbn == "" && uploadAfterCompile {
182-
sk = arguments.NewSketch(sketchPath)
183-
port = portArgs.GetDiscoveryPort(inst, sk)
184-
rpcPort := port.ToRPC()
185-
var err error
186-
pm := commands.GetPackageManager(inst.Id)
187-
fqbn, err = upload.DetectConnectedBoard(pm, rpcPort.Address, rpcPort.Protocol)
188-
if err != nil {
189-
feedback.Errorf(tr("Error during FQBN detection: %v", err))
190-
os.Exit(errorcodes.ErrGeneric)
191-
}
192-
}
193-
194175
compileRequest := &rpc.CompileRequest{
195176
Instance: inst,
196177
Fqbn: fqbn,
@@ -227,16 +208,9 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
227208
}
228209

229210
if compileError == nil && uploadAfterCompile {
230-
if sk == nil {
231-
sk = arguments.NewSketch(sketchPath)
232-
}
233-
if port == nil {
234-
port = portArgs.GetDiscoveryPort(inst, sk)
235-
}
236-
237211
userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{
238212
Instance: inst,
239-
Fqbn: fqbnArg.String(),
213+
Fqbn: fqbn,
240214
Address: port.Address,
241215
Protocol: port.Protocol,
242216
})
@@ -255,7 +229,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
255229
Instance: inst,
256230
Fqbn: fqbn,
257231
SketchPath: sketchPath.String(),
258-
Port: port.ToRPC(),
232+
Port: port,
259233
Verbose: verbose,
260234
Verify: verify,
261235
ImportDir: buildPath,

cli/debug/debug.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,12 @@ func runDebugCommand(command *cobra.Command, args []string) {
7777

7878
sketchPath := arguments.InitSketchPath(path)
7979
sk := arguments.NewSketch(sketchPath)
80-
port := portArgs.GetDiscoveryPort(instance, sk)
81-
80+
fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, instance, sk)
8281
debugConfigRequested := &dbg.DebugConfigRequest{
8382
Instance: instance,
84-
Fqbn: fqbnArg.String(),
83+
Fqbn: fqbn,
8584
SketchPath: sketchPath.String(),
86-
Port: port.ToRPC(),
85+
Port: port,
8786
Interpreter: interpreter,
8887
ImportDir: importDir,
8988
Programmer: programmer.String(),

cli/upload/upload.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -95,22 +95,11 @@ func runUploadCommand(command *cobra.Command, args []string) {
9595
feedback.Errorf(tr("Error during Upload: %v"), err)
9696
os.Exit(errorcodes.ErrGeneric)
9797
}
98-
99-
port, err := portArgs.GetPort(instance, sk)
100-
if err != nil {
101-
feedback.Errorf(tr("Error during Upload: %v"), err)
102-
os.Exit(errorcodes.ErrGeneric)
103-
}
104-
105-
if fqbnArg.String() == "" && sk != nil && sk.Metadata != nil {
106-
// If the user didn't specify an FQBN and a sketch.json file is present
107-
// read it from there.
108-
fqbnArg.Set(sk.Metadata.CPU.Fqbn)
109-
}
98+
fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, instance, sk)
11099

111100
userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{
112101
Instance: instance,
113-
Fqbn: fqbnArg.String(),
102+
Fqbn: fqbn,
114103
Address: port.Address,
115104
Protocol: port.Protocol,
116105
})
@@ -153,9 +142,9 @@ func runUploadCommand(command *cobra.Command, args []string) {
153142

154143
if _, err := upload.Upload(context.Background(), &rpc.UploadRequest{
155144
Instance: instance,
156-
Fqbn: fqbnArg.String(),
145+
Fqbn: fqbn,
157146
SketchPath: path,
158-
Port: port.ToRPC(),
147+
Port: port,
159148
Verbose: verbose,
160149
Verify: verify,
161150
ImportFile: importFile,

commands/upload/upload.go

Lines changed: 1 addition & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,7 @@ func SupportedUserFields(ctx context.Context, req *rpc.SupportedUserFieldsReques
5252
return nil, &arduino.InvalidInstanceError{}
5353
}
5454

55-
reqFQBN := req.GetFqbn()
56-
if reqFQBN == "" {
57-
var err error
58-
reqFQBN, err = DetectConnectedBoard(pm, req.Address, req.Protocol)
59-
if err != nil {
60-
return nil, err
61-
}
62-
}
63-
64-
fqbn, err := cores.ParseFQBN(reqFQBN)
55+
fqbn, err := cores.ParseFQBN(req.GetFqbn())
6556
if err != nil {
6657
return nil, &arduino.InvalidFQBNError{Cause: err}
6758
}
@@ -86,76 +77,6 @@ func SupportedUserFields(ctx context.Context, req *rpc.SupportedUserFieldsReques
8677
}, nil
8778
}
8879

89-
// DetectConnectedBoard returns the FQBN of the board connected to specified address and protocol.
90-
// In all other cases, like when more than a possible board is detected return an error.
91-
func DetectConnectedBoard(pm *packagemanager.PackageManager, address, protocol string) (string, error) {
92-
if address == "" {
93-
return "", &arduino.MissingPortAddressError{}
94-
}
95-
if protocol == "" {
96-
return "", &arduino.MissingPortProtocolError{}
97-
}
98-
99-
dm := pm.DiscoveryManager()
100-
101-
// Run discoveries if they're not already running
102-
if errs := dm.RunAll(); len(errs) > 0 {
103-
// Some discovery managed to not run, just log the errors,
104-
// we'll fail further down the line.
105-
for _, err := range errs {
106-
logrus.Error(err)
107-
}
108-
}
109-
110-
if errs := dm.StartAll(); len(errs) > 0 {
111-
// Some discovery managed to not start, just log the errors,
112-
// we'll fail further down the line.
113-
for _, err := range errs {
114-
logrus.Error(err)
115-
}
116-
}
117-
118-
ports, errs := dm.List()
119-
if len(errs) > 0 {
120-
// Errors at this time are not a big issue, we'll
121-
// fail further down the line if we can't find a
122-
// matching board and tell the user to provide
123-
// an FQBN.
124-
for _, err := range errs {
125-
logrus.Error(err)
126-
}
127-
}
128-
129-
for _, p := range ports {
130-
if p.Address == address && p.Protocol == protocol {
131-
// Found matching port, let's see if we can detect
132-
// which board is connected to it
133-
boards := pm.IdentifyBoard(p.Properties)
134-
if l := len(boards); l == 1 {
135-
// We found only one board connected, that must
136-
// the one the user want to upload to.
137-
board := boards[0]
138-
logrus.
139-
WithField("board", board.String()).
140-
WithField("address", address).
141-
WithField("protocol", protocol).
142-
Trace("Detected board")
143-
return board.FQBN(), nil
144-
} else if l >= 2 {
145-
// There are multiple boards detected on this port,
146-
// we have no way of knowing which one is the one.
147-
return "", &arduino.MultipleBoardsDetectedError{Port: p.ToRPC()}
148-
} else {
149-
// We found a matching port but there's either
150-
// no board connected or we can't recognize it.
151-
// The user must provide an FQBN.
152-
break
153-
}
154-
}
155-
}
156-
return "", &arduino.MissingFQBNError{}
157-
}
158-
15980
// getToolID returns the ID of the tool that supports the action and protocol combination by searching in props.
16081
// Returns error if tool cannot be found.
16182
func getToolID(props *properties.Map, action, protocol string) (string, error) {
@@ -271,18 +192,6 @@ func runProgramAction(pm *packagemanager.PackageManager,
271192

272193
logrus.WithField("port", port).Tracef("Upload port")
273194

274-
if fqbnIn == "" && sk != nil && sk.Metadata != nil {
275-
fqbnIn = sk.Metadata.CPU.Fqbn
276-
}
277-
278-
if fqbnIn == "" {
279-
var err error
280-
fqbnIn, err = DetectConnectedBoard(pm, port.Address, port.Protocol)
281-
if err != nil {
282-
return err
283-
}
284-
}
285-
286195
fqbn, err := cores.ParseFQBN(fqbnIn)
287196
if err != nil {
288197
return &arduino.InvalidFQBNError{Cause: err}

0 commit comments

Comments
 (0)