Skip to content

Commit 5373961

Browse files
authored
Added external programmer support (#720)
* Added scaffolding for external programmer support * Added programmers extraction in arduino/cores module * Implemented programmers list command * Print upload command line in verbose mode * Added programmer option to compile command * External programmer implementation * Factored function runTool in upload This will turn out useful for burn-bootloader that requires to run two actions in a row ("erase" and "bootloader"). * Implemented burn-bootloader * Increased tracing log * Test fix * Added BurnBootloder action * Make the upload port parameter mandatory only when really needed * Fixed nil pointer exception when burning-bootloader * Added sanity check on upload parameters
1 parent 15d35de commit 5373961

File tree

20 files changed

+1198
-268
lines changed

20 files changed

+1198
-268
lines changed

Diff for: arduino/cores/cores.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ type PlatformRelease struct {
4040
Resource *resources.DownloadResource
4141
Version *semver.Version
4242
BoardsManifest []*BoardManifest
43-
Dependencies ToolDependencies // The Dependency entries to load tools.
44-
Platform *Platform `json:"-"`
45-
Properties *properties.Map `json:"-"`
46-
Boards map[string]*Board `json:"-"`
47-
Programmers map[string]*properties.Map `json:"-"`
48-
Menus *properties.Map `json:"-"`
49-
InstallDir *paths.Path `json:"-"`
50-
IsIDEBundled bool `json:"-"`
43+
Dependencies ToolDependencies // The Dependency entries to load tools.
44+
Platform *Platform `json:"-"`
45+
Properties *properties.Map `json:"-"`
46+
Boards map[string]*Board `json:"-"`
47+
Programmers map[string]*Programmer `json:"-"`
48+
Menus *properties.Map `json:"-"`
49+
InstallDir *paths.Path `json:"-"`
50+
IsIDEBundled bool `json:"-"`
5151
}
5252

5353
// BoardManifest contains information about a board. These metadata are usually
@@ -117,7 +117,7 @@ func (platform *Platform) GetOrCreateRelease(version *semver.Version) (*Platform
117117
Version: version,
118118
Boards: map[string]*Board{},
119119
Properties: properties.NewMap(),
120-
Programmers: map[string]*properties.Map{},
120+
Programmers: map[string]*Programmer{},
121121
Platform: platform,
122122
}
123123
platform.Releases[tag] = release

Diff for: arduino/cores/packagemanager/loader.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,10 @@ func (pm *PackageManager) loadPlatformRelease(platform *cores.PlatformRelease, p
284284

285285
// Create programmers properties
286286
if programmersProperties, err := properties.SafeLoad(programmersTxtPath.String()); err == nil {
287-
platform.Programmers = properties.MergeMapsOfProperties(
288-
map[string]*properties.Map{},
289-
platform.Programmers, // TODO: Very weird, why not an empty one?
290-
programmersProperties.FirstLevelOf())
287+
for programmerID, programmerProperties := range programmersProperties.FirstLevelOf() {
288+
platform.Programmers[programmerID] = pm.loadProgrammer(programmerProperties)
289+
platform.Programmers[programmerID].PlatformRelease = platform
290+
}
291291
} else {
292292
return err
293293
}
@@ -299,6 +299,13 @@ func (pm *PackageManager) loadPlatformRelease(platform *cores.PlatformRelease, p
299299
return nil
300300
}
301301

302+
func (pm *PackageManager) loadProgrammer(programmerProperties *properties.Map) *cores.Programmer {
303+
return &cores.Programmer{
304+
Name: programmerProperties.Get("name"),
305+
Properties: programmerProperties,
306+
}
307+
}
308+
302309
func (pm *PackageManager) loadBoards(platform *cores.PlatformRelease) error {
303310
if platform.InstallDir == nil {
304311
return fmt.Errorf("platform not installed")

Diff for: arduino/cores/programmers.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package cores
17+
18+
import "github.com/arduino/go-properties-orderedmap"
19+
20+
// Programmer represents an external programmer
21+
type Programmer struct {
22+
Name string
23+
Properties *properties.Map
24+
PlatformRelease *PlatformRelease
25+
}

Diff for: cli/burnbootloader/burnbootloader.go

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package burnbootloader
17+
18+
import (
19+
"context"
20+
"os"
21+
22+
"github.com/arduino/arduino-cli/cli/errorcodes"
23+
"github.com/arduino/arduino-cli/cli/feedback"
24+
"github.com/arduino/arduino-cli/cli/instance"
25+
"github.com/arduino/arduino-cli/commands/upload"
26+
rpc "github.com/arduino/arduino-cli/rpc/commands"
27+
"github.com/arduino/arduino-cli/table"
28+
"github.com/arduino/go-paths-helper"
29+
"github.com/sirupsen/logrus"
30+
"github.com/spf13/cobra"
31+
)
32+
33+
var (
34+
fqbn string
35+
port string
36+
verbose bool
37+
verify bool
38+
importDir string
39+
programmer string
40+
burnBootloader bool
41+
)
42+
43+
// NewCommand created a new `burn-bootloader` command
44+
func NewCommand() *cobra.Command {
45+
burnBootloaderCommand := &cobra.Command{
46+
Use: "burn-bootloader",
47+
Short: "Upload the bootloader.",
48+
Long: "Upload the bootloader on the board using an external programmer.",
49+
Example: " " + os.Args[0] + " burn-bootloader -b arduino:avr:uno -P atmel-ice",
50+
Args: cobra.MaximumNArgs(1),
51+
Run: run,
52+
}
53+
54+
burnBootloaderCommand.Flags().StringVarP(&fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:avr:uno")
55+
burnBootloaderCommand.Flags().StringVarP(&port, "port", "p", "", "Upload port, e.g.: COM10 or /dev/ttyACM0")
56+
burnBootloaderCommand.Flags().BoolVarP(&verify, "verify", "t", false, "Verify uploaded binary after the upload.")
57+
burnBootloaderCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, "Turns on verbose mode.")
58+
burnBootloaderCommand.Flags().StringVarP(&programmer, "programmer", "P", "", "Use the specified programmer to upload or 'list' to list supported programmers.")
59+
60+
return burnBootloaderCommand
61+
}
62+
63+
func run(command *cobra.Command, args []string) {
64+
instance, err := instance.CreateInstance()
65+
if err != nil {
66+
feedback.Errorf("Error during Upload: %v", err)
67+
os.Exit(errorcodes.ErrGeneric)
68+
}
69+
70+
if programmer == "list" {
71+
resp, err := upload.ListProgrammersAvailableForUpload(context.Background(), &rpc.ListProgrammersAvailableForUploadReq{
72+
Instance: instance,
73+
Fqbn: fqbn,
74+
})
75+
if err != nil {
76+
feedback.Errorf("Error listing programmers: %v", err)
77+
os.Exit(errorcodes.ErrGeneric)
78+
}
79+
feedback.PrintResult(&programmersList{
80+
Programmers: resp.GetProgrammers(),
81+
})
82+
os.Exit(0)
83+
}
84+
85+
if _, err := upload.BurnBootloader(context.Background(), &rpc.BurnBootloaderReq{
86+
Instance: instance,
87+
Fqbn: fqbn,
88+
Port: port,
89+
Verbose: verbose,
90+
Verify: verify,
91+
Programmer: programmer,
92+
}, os.Stdout, os.Stderr); err != nil {
93+
feedback.Errorf("Error during Upload: %v", err)
94+
os.Exit(errorcodes.ErrGeneric)
95+
}
96+
os.Exit(0)
97+
}
98+
99+
// initSketchPath returns the current working directory
100+
func initSketchPath(sketchPath *paths.Path) *paths.Path {
101+
if sketchPath != nil {
102+
return sketchPath
103+
}
104+
105+
wd, err := paths.Getwd()
106+
if err != nil {
107+
feedback.Errorf("Couldn't get current working directory: %v", err)
108+
os.Exit(errorcodes.ErrGeneric)
109+
}
110+
logrus.Infof("Reading sketch from dir: %s", wd)
111+
return wd
112+
}
113+
114+
type programmersList struct {
115+
Programmers []*rpc.Programmer
116+
}
117+
118+
func (p *programmersList) Data() interface{} {
119+
return p.Programmers
120+
}
121+
122+
func (p *programmersList) String() string {
123+
t := table.New()
124+
t.SetHeader("ID", "Programmer Name", "Platform")
125+
for _, prog := range p.Programmers {
126+
t.AddRow(prog.GetId(), prog.GetName(), prog.GetPlatform())
127+
}
128+
return t.Render()
129+
}

Diff for: cli/cli.go

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"strings"
2323

2424
"github.com/arduino/arduino-cli/cli/board"
25+
"github.com/arduino/arduino-cli/cli/burnbootloader"
2526
"github.com/arduino/arduino-cli/cli/cache"
2627
"github.com/arduino/arduino-cli/cli/compile"
2728
"github.com/arduino/arduino-cli/cli/completion"
@@ -87,6 +88,7 @@ func createCliCommandTree(cmd *cobra.Command) {
8788
cmd.AddCommand(sketch.NewCommand())
8889
cmd.AddCommand(upload.NewCommand())
8990
cmd.AddCommand(debug.NewCommand())
91+
cmd.AddCommand(burnbootloader.NewCommand())
9092
cmd.AddCommand(version.NewCommand())
9193

9294
cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Print the logs on the standard output.")

Diff for: cli/compile/compile.go

+3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ var (
5050
dryRun bool // Use this flag to now write the output file
5151
libraries []string // List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths.
5252
optimizeForDebug bool // Optimize compile output for debug, not for release
53+
programmer string // Use the specified programmer to upload
5354
)
5455

5556
// NewCommand created a new `compile` command
@@ -84,6 +85,7 @@ func NewCommand() *cobra.Command {
8485
command.Flags().StringSliceVar(&libraries, "libraries", []string{},
8586
"List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths.")
8687
command.Flags().BoolVar(&optimizeForDebug, "optimize-for-debug", false, "Optional, optimize compile output for debug, not for release.")
88+
command.Flags().StringVarP(&programmer, "programmer", "P", "", "Optional, use the specified programmer to upload.")
8789

8890
return command
8991
}
@@ -135,6 +137,7 @@ func run(cmd *cobra.Command, args []string) {
135137
Verbose: verbose,
136138
Verify: verify,
137139
ImportDir: exportDir,
140+
Programmer: programmer,
138141
}, os.Stdout, os.Stderr)
139142

140143
if err != nil {

Diff for: cli/upload/upload.go

+59-5
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,20 @@ import (
2424
"github.com/arduino/arduino-cli/cli/instance"
2525
"github.com/arduino/arduino-cli/commands/upload"
2626
rpc "github.com/arduino/arduino-cli/rpc/commands"
27+
"github.com/arduino/arduino-cli/table"
2728
"github.com/arduino/go-paths-helper"
2829
"github.com/sirupsen/logrus"
2930
"github.com/spf13/cobra"
3031
)
3132

3233
var (
33-
fqbn string
34-
port string
35-
verbose bool
36-
verify bool
37-
importDir string
34+
fqbn string
35+
port string
36+
verbose bool
37+
verify bool
38+
importDir string
39+
programmer string
40+
burnBootloader bool
3841
)
3942

4043
// NewCommand created a new `upload` command
@@ -53,6 +56,7 @@ func NewCommand() *cobra.Command {
5356
uploadCommand.Flags().StringVarP(&importDir, "input-dir", "", "", "Direcory containing binaries to upload.")
5457
uploadCommand.Flags().BoolVarP(&verify, "verify", "t", false, "Verify uploaded binary after the upload.")
5558
uploadCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, "Optional, turns on verbose mode.")
59+
uploadCommand.Flags().StringVarP(&programmer, "programmer", "P", "", "Optional, use the specified programmer to upload or 'list' to list supported programmers.")
5660

5761
return uploadCommand
5862
}
@@ -64,12 +68,44 @@ func run(command *cobra.Command, args []string) {
6468
os.Exit(errorcodes.ErrGeneric)
6569
}
6670

71+
if programmer == "list" {
72+
resp, err := upload.ListProgrammersAvailableForUpload(context.Background(), &rpc.ListProgrammersAvailableForUploadReq{
73+
Instance: instance,
74+
Fqbn: fqbn,
75+
})
76+
if err != nil {
77+
feedback.Errorf("Error listing programmers: %v", err)
78+
os.Exit(errorcodes.ErrGeneric)
79+
}
80+
feedback.PrintResult(&programmersList{
81+
Programmers: resp.GetProgrammers(),
82+
})
83+
os.Exit(0)
84+
}
85+
6786
var path *paths.Path
6887
if len(args) > 0 {
6988
path = paths.New(args[0])
7089
}
7190
sketchPath := initSketchPath(path)
7291

92+
if burnBootloader {
93+
if _, err := upload.Upload(context.Background(), &rpc.UploadReq{
94+
Instance: instance,
95+
Fqbn: fqbn,
96+
SketchPath: sketchPath.String(),
97+
Port: port,
98+
Verbose: verbose,
99+
Verify: verify,
100+
ImportDir: importDir,
101+
Programmer: programmer,
102+
}, os.Stdout, os.Stderr); err != nil {
103+
feedback.Errorf("Error during Upload: %v", err)
104+
os.Exit(errorcodes.ErrGeneric)
105+
}
106+
os.Exit(0)
107+
}
108+
73109
if _, err := upload.Upload(context.Background(), &rpc.UploadReq{
74110
Instance: instance,
75111
Fqbn: fqbn,
@@ -78,6 +114,7 @@ func run(command *cobra.Command, args []string) {
78114
Verbose: verbose,
79115
Verify: verify,
80116
ImportDir: importDir,
117+
Programmer: programmer,
81118
}, os.Stdout, os.Stderr); err != nil {
82119
feedback.Errorf("Error during Upload: %v", err)
83120
os.Exit(errorcodes.ErrGeneric)
@@ -98,3 +135,20 @@ func initSketchPath(sketchPath *paths.Path) *paths.Path {
98135
logrus.Infof("Reading sketch from dir: %s", wd)
99136
return wd
100137
}
138+
139+
type programmersList struct {
140+
Programmers []*rpc.Programmer
141+
}
142+
143+
func (p *programmersList) Data() interface{} {
144+
return p.Programmers
145+
}
146+
147+
func (p *programmersList) String() string {
148+
t := table.New()
149+
t.SetHeader("ID", "Programmer Name", "Platform")
150+
for _, prog := range p.Programmers {
151+
t.AddRow(prog.GetId(), prog.GetName(), prog.GetPlatform())
152+
}
153+
return t.Render()
154+
}

Diff for: commands/daemon/daemon.go

+18
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,24 @@ func (s *ArduinoCoreServerImpl) Upload(req *rpc.UploadReq, stream rpc.ArduinoCor
216216
return stream.Send(resp)
217217
}
218218

219+
// BurnBootloader FIXMEDOC
220+
func (s *ArduinoCoreServerImpl) BurnBootloader(req *rpc.BurnBootloaderReq, stream rpc.ArduinoCore_BurnBootloaderServer) error {
221+
resp, err := upload.BurnBootloader(
222+
stream.Context(), req,
223+
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.BurnBootloaderResp{OutStream: data}) }),
224+
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.BurnBootloaderResp{ErrStream: data}) }),
225+
)
226+
if err != nil {
227+
return err
228+
}
229+
return stream.Send(resp)
230+
}
231+
232+
// ListProgrammersAvailableForUpload FIXMEDOC
233+
func (s *ArduinoCoreServerImpl) ListProgrammersAvailableForUpload(ctx context.Context, req *rpc.ListProgrammersAvailableForUploadReq) (*rpc.ListProgrammersAvailableForUploadResp, error) {
234+
return upload.ListProgrammersAvailableForUpload(ctx, req)
235+
}
236+
219237
// LibraryDownload FIXMEDOC
220238
func (s *ArduinoCoreServerImpl) LibraryDownload(req *rpc.LibraryDownloadReq, stream rpc.ArduinoCore_LibraryDownloadServer) error {
221239
resp, err := lib.LibraryDownload(

0 commit comments

Comments
 (0)