Skip to content

Commit 55d8864

Browse files
committed
Workaround arduino-cli blocking commands
1 parent 4add8b2 commit 55d8864

File tree

1 file changed

+66
-20
lines changed

1 file changed

+66
-20
lines changed

arduino/cli/commander.go

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ type commander struct {
4444
func NewCommander() (arduino.Commander, error) {
4545
// Discard arduino-cli log info messages
4646
logrus.SetLevel(logrus.ErrorLevel)
47-
// Initialize arduino-cli configuration
47+
48+
// Initialize arduino-cli configuration.
4849
configuration.Settings = configuration.Init(configuration.FindConfigFileInArgsOrWorkingDirectory(os.Args))
49-
// Create arduino-cli instance, needed to execute arduino-cli commands
50+
51+
// Create and init an arduino-cli instance, needed to execute arduino-cli commands.
5052
inst, err := instance.Create()
5153
if err != nil {
5254
err = fmt.Errorf("creating arduino-cli instance: %w", err)
@@ -61,34 +63,64 @@ func NewCommander() (arduino.Commander, error) {
6163
return cmd, nil
6264
}
6365

66+
func mergeErrors(err error, errs []error) error {
67+
merr := errors.New("merged errors: ")
68+
empty := true
69+
70+
if err != nil {
71+
merr = fmt.Errorf("%w%v; ", merr, err)
72+
empty = false
73+
}
74+
75+
if len(errs) > 0 {
76+
empty = false
77+
for _, e := range errs {
78+
merr = fmt.Errorf("%w%v; ", merr, e)
79+
}
80+
}
81+
82+
if !empty {
83+
return merr
84+
}
85+
return nil
86+
}
87+
6488
// BoardList executes the 'arduino-cli board list' command
6589
// and returns its result.
66-
func (c *commander) BoardList() ([]*rpc.DetectedPort, error) {
90+
func (c *commander) BoardList(ctx context.Context) ([]*rpc.DetectedPort, error) {
6791
req := &rpc.BoardListRequest{
6892
Instance: c.Instance,
6993
Timeout: time.Second.Milliseconds(),
7094
}
7195

72-
ports, errs, err := board.List(req)
73-
if err != nil {
74-
err = fmt.Errorf("%s: %w", "detecting boards", err)
75-
return nil, err
96+
// There is no obvious way to cancel the execution of this command.
97+
// So, we execute it in a goroutine and leave it running alone if ctx gets cancelled.
98+
type resp struct {
99+
err error
100+
ports []*rpc.DetectedPort
76101
}
102+
quit := make(chan resp, 1)
103+
go func() {
104+
ports, errs, err := board.List(req)
105+
quit <- resp{err: mergeErrors(err, errs), ports: ports}
106+
close(quit)
107+
}()
77108

78-
if len(errs) > 0 {
79-
err = errors.New("starting discovery procedure: received errors: ")
80-
for _, e := range errs {
81-
err = fmt.Errorf("%w%v; ", err, e)
109+
// Wait for the command to complete or the context to be terminated.
110+
select {
111+
case <-ctx.Done():
112+
return nil, errors.New("board list command cancelled")
113+
case r := <-quit:
114+
if r.err != nil {
115+
return nil, fmt.Errorf("executing board list command: %w", r.err)
82116
}
83-
return nil, err
117+
return r.ports, nil
84118
}
85-
86-
return ports, nil
87119
}
88120

89121
// UploadBin executes the 'arduino-cli upload -i' command
90122
// and returns its result.
91-
func (c *commander) UploadBin(fqbn, bin, address, protocol string) error {
123+
func (c *commander) UploadBin(ctx context.Context, fqbn, bin, address, protocol string) error {
92124
req := &rpc.UploadRequest{
93125
Instance: c.Instance,
94126
Fqbn: fqbn,
@@ -97,11 +129,25 @@ func (c *commander) UploadBin(fqbn, bin, address, protocol string) error {
97129
Port: &rpc.Port{Address: address, Protocol: protocol},
98130
Verbose: false,
99131
}
100-
101132
l := logrus.StandardLogger().WithField("source", "arduino-cli").Writer()
102-
if _, err := upload.Upload(context.Background(), req, l, l); err != nil {
103-
err = fmt.Errorf("%s: %w", "uploading binary", err)
104-
return err
133+
134+
// There is no obvious way to cancel the execution of this command.
135+
// So, we execute it in a goroutine and leave it running if ctx gets cancelled.
136+
quit := make(chan error, 1)
137+
go func() {
138+
_, err := upload.Upload(ctx, req, l, l)
139+
quit <- err
140+
close(quit)
141+
}()
142+
143+
// Wait for the upload to complete or the context to be terminated.
144+
select {
145+
case <-ctx.Done():
146+
return errors.New("upload cancelled")
147+
case err := <-quit:
148+
if err != nil {
149+
return fmt.Errorf("uploading binary: %w", err)
150+
}
151+
return nil
105152
}
106-
return nil
107153
}

0 commit comments

Comments
 (0)