Skip to content

Implemented dry-run for upload and burn-bootloader #1352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions arduino/serialutils/serialutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package serialutils

import (
"fmt"
"strings"
"time"

"github.com/pkg/errors"
Expand Down Expand Up @@ -86,9 +87,28 @@ type ResetProgressCallbacks struct {
// If wait is true, this function will wait for a new port to appear and returns that
// one, otherwise the empty string is returned if the new port can not be detected or
// if the wait parameter is false.
// If dryRun is set to true this function will only emulate the port reset without actually
// performing it, this is useful to mockup for unit-testing and CI.
// In dryRun mode if the `portToTouch` ends with `"999"` and wait is true, Reset will
// return a new "bootloader" port as `portToTouch+"0"`.
// The error is set if the port listing fails.
func Reset(portToTouch string, wait bool, cb *ResetProgressCallbacks) (string, error) {
last, err := getPortMap()
func Reset(portToTouch string, wait bool, cb *ResetProgressCallbacks, dryRun bool) (string, error) {
getPorts := getPortMap // non dry-run default
if dryRun {
emulatedPort := portToTouch
getPorts = func() (map[string]bool, error) {
res := map[string]bool{}
if emulatedPort != "" {
res[emulatedPort] = true
}
if strings.HasSuffix(emulatedPort, "999") {
emulatedPort += "0"
}
return res, nil
}
}

last, err := getPorts()
if cb != nil && cb.Debug != nil {
cb.Debug(fmt.Sprintf("LAST: %v", last))
}
Expand All @@ -103,8 +123,12 @@ func Reset(portToTouch string, wait bool, cb *ResetProgressCallbacks) (string, e
if cb != nil && cb.TouchingPort != nil {
cb.TouchingPort(portToTouch)
}
if err := TouchSerialPortAt1200bps(portToTouch); err != nil {
fmt.Println("TOUCH: error during reset:", err)
if dryRun {
// do nothing!
} else {
if err := TouchSerialPortAt1200bps(portToTouch); err != nil {
fmt.Println("TOUCH: error during reset:", err)
}
}
}

Expand All @@ -116,8 +140,12 @@ func Reset(portToTouch string, wait bool, cb *ResetProgressCallbacks) (string, e
}

deadline := time.Now().Add(10 * time.Second)
if dryRun {
// use a much lower timeout in dryRun
deadline = time.Now().Add(100 * time.Millisecond)
}
for time.Now().Before(deadline) {
now, err := getPortMap()
now, err := getPorts()
if err != nil {
return "", err
}
Expand Down Expand Up @@ -146,7 +174,7 @@ func Reset(portToTouch string, wait bool, cb *ResetProgressCallbacks) (string, e
// the USB serial port appearing and disappearing rapidly before
// settling.
// This check ensure that the port is stable after one second.
check, err := getPortMap()
check, err := getPorts()
if err != nil {
return "", err
}
Expand Down
4 changes: 4 additions & 0 deletions cli/burnbootloader/burnbootloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var (
verbose bool
verify bool
programmer string
dryRun bool
)

// NewCommand created a new `burn-bootloader` command
Expand All @@ -51,6 +52,8 @@ func NewCommand() *cobra.Command {
burnBootloaderCommand.Flags().BoolVarP(&verify, "verify", "t", false, "Verify uploaded binary after the upload.")
burnBootloaderCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, "Turns on verbose mode.")
burnBootloaderCommand.Flags().StringVarP(&programmer, "programmer", "P", "", "Use the specified programmer to upload.")
burnBootloaderCommand.Flags().BoolVar(&dryRun, "dry-run", false, "Do not perform the actual upload, just log out actions")
burnBootloaderCommand.Flags().MarkHidden("dry-run")

return burnBootloaderCommand
}
Expand All @@ -65,6 +68,7 @@ func run(command *cobra.Command, args []string) {
Verbose: verbose,
Verify: verify,
Programmer: programmer,
DryRun: dryRun,
}, os.Stdout, os.Stderr); err != nil {
feedback.Errorf("Error during Upload: %v", err)
os.Exit(errorcodes.ErrGeneric)
Expand Down
5 changes: 4 additions & 1 deletion cli/upload/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var (
importDir string
importFile string
programmer string
dryRun bool
)

// NewCommand created a new `upload` command
Expand All @@ -59,7 +60,8 @@ func NewCommand() *cobra.Command {
uploadCommand.Flags().BoolVarP(&verify, "verify", "t", false, "Verify uploaded binary after the upload.")
uploadCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, "Optional, turns on verbose mode.")
uploadCommand.Flags().StringVarP(&programmer, "programmer", "P", "", "Optional, use the specified programmer to upload.")

uploadCommand.Flags().BoolVar(&dryRun, "dry-run", false, "Do not perform the actual upload, just log out actions")
uploadCommand.Flags().MarkHidden("dry-run")
return uploadCommand
}

Expand Down Expand Up @@ -97,6 +99,7 @@ func run(command *cobra.Command, args []string) {
ImportFile: importFile,
ImportDir: importDir,
Programmer: programmer,
DryRun: dryRun,
}, os.Stdout, os.Stderr); err != nil {
feedback.Errorf("Error during Upload: %v", err)
os.Exit(errorcodes.ErrGeneric)
Expand Down
1 change: 1 addition & 0 deletions commands/upload/burnbootloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStre
true, // burnBootloader
outStream,
errStream,
req.GetDryRun(),
)
if err != nil {
return nil, err
Expand Down
61 changes: 37 additions & 24 deletions commands/upload/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er
false, // burnBootloader
outStream,
errStream,
req.GetDryRun(),
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -98,7 +99,8 @@ func runProgramAction(pm *packagemanager.PackageManager,
importFile, importDir, fqbnIn, port string,
programmerID string,
verbose, verify, burnBootloader bool,
outStream, errStream io.Writer) error {
outStream, errStream io.Writer,
dryRun bool) error {

if burnBootloader && programmerID == "" {
return fmt.Errorf("no programmer specified for burning bootloader")
Expand Down Expand Up @@ -308,35 +310,42 @@ func runProgramAction(pm *packagemanager.PackageManager,
outStream.Write([]byte(fmt.Sprintln("Skipping 1200-bps touch reset: no serial port selected!")))
}

var cb *serialutils.ResetProgressCallbacks
if verbose {
cb = &serialutils.ResetProgressCallbacks{
TouchingPort: func(port string) {
logrus.WithField("phase", "board reset").Infof("Performing 1200-bps touch reset on serial port %s", port)
cb := &serialutils.ResetProgressCallbacks{
TouchingPort: func(port string) {
logrus.WithField("phase", "board reset").Infof("Performing 1200-bps touch reset on serial port %s", port)
if verbose {
outStream.Write([]byte(fmt.Sprintf("Performing 1200-bps touch reset on serial port %s", port)))
outStream.Write([]byte(fmt.Sprintln()))
},
WaitingForNewSerial: func() {
logrus.WithField("phase", "board reset").Info("Waiting for upload port...")
}
},
WaitingForNewSerial: func() {
logrus.WithField("phase", "board reset").Info("Waiting for upload port...")
if verbose {
outStream.Write([]byte(fmt.Sprintln("Waiting for upload port...")))
},
BootloaderPortFound: func(port string) {
}
},
BootloaderPortFound: func(port string) {
if port != "" {
logrus.WithField("phase", "board reset").Infof("Upload port found on %s", port)
} else {
logrus.WithField("phase", "board reset").Infof("No upload port found, using %s as fallback", actualPort)
}
if verbose {
if port != "" {
logrus.WithField("phase", "board reset").Infof("Upload port found on %s", port)
outStream.Write([]byte(fmt.Sprintf("Upload port found on %s", port)))
outStream.Write([]byte(fmt.Sprintln()))
} else {
logrus.WithField("phase", "board reset").Infof("No upload port found, using %s as fallback", actualPort)
outStream.Write([]byte(fmt.Sprintf("No upload port found, using %s as fallback", actualPort)))
outStream.Write([]byte(fmt.Sprintln()))
}
},
Debug: func(msg string) {
logrus.WithField("phase", "board reset").Debug(msg)
},
}
}
},
Debug: func(msg string) {
logrus.WithField("phase", "board reset").Debug(msg)
},
}
if newPort, err := serialutils.Reset(portToTouch, wait, cb); err != nil {

if newPort, err := serialutils.Reset(portToTouch, wait, cb, dryRun); err != nil {
outStream.Write([]byte(fmt.Sprintf("Cannot perform port reset: %s", err)))
outStream.Write([]byte(fmt.Sprintln()))
} else {
Expand All @@ -358,18 +367,18 @@ func runProgramAction(pm *packagemanager.PackageManager,

// Run recipes for upload
if burnBootloader {
if err := runTool("erase.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
if err := runTool("erase.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
return fmt.Errorf("chip erase error: %s", err)
}
if err := runTool("bootloader.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
if err := runTool("bootloader.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
return fmt.Errorf("burn bootloader error: %s", err)
}
} else if programmer != nil {
if err := runTool("program.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
if err := runTool("program.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
return fmt.Errorf("programming error: %s", err)
}
} else {
if err := runTool("upload.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
if err := runTool("upload.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
return fmt.Errorf("uploading error: %s", err)
}
}
Expand All @@ -378,7 +387,7 @@ func runProgramAction(pm *packagemanager.PackageManager,
return nil
}

func runTool(recipeID string, props *properties.Map, outStream, errStream io.Writer, verbose bool) error {
func runTool(recipeID string, props *properties.Map, outStream, errStream io.Writer, verbose bool, dryRun bool) error {
recipe, ok := props.GetOk(recipeID)
if !ok {
return fmt.Errorf("recipe not found '%s'", recipeID)
Expand All @@ -396,9 +405,13 @@ func runTool(recipeID string, props *properties.Map, outStream, errStream io.Wri
}

// Run Tool
logrus.WithField("phase", "upload").Tracef("Executing upload tool: %s", cmdLine)
if verbose {
outStream.Write([]byte(fmt.Sprintln(cmdLine)))
}
if dryRun {
return nil
}
cmd, err := executils.NewProcess(cmdArgs...)
if err != nil {
return fmt.Errorf("cannot execute upload tool: %s", err)
Expand Down
1 change: 1 addition & 0 deletions commands/upload/upload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ func TestUploadPropertiesComposition(t *testing.T) {
test.burnBootloader, // burnBootloader
outStream,
errStream,
false,
)
verboseVerifyOutput := "verbose verify"
if !verboseVerify {
Expand Down
Loading