feat: make -port optional
This commit is contained in:
@@ -1,31 +1,143 @@
|
|||||||
package flash
|
package flash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/goplus/llgo/internal/crosscompile"
|
"github.com/goplus/llgo/internal/crosscompile"
|
||||||
"github.com/goplus/llgo/internal/env"
|
"github.com/goplus/llgo/internal/env"
|
||||||
|
"go.bug.st/serial/enumerator"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// From tinygo/main.go getDefaultPort
|
||||||
|
// GetPort returns the default serial port depending on the operating system and USB interfaces.
|
||||||
|
func GetPort(portFlag string, usbInterfaces []string) (string, error) {
|
||||||
|
portCandidates := strings.FieldsFunc(portFlag, func(c rune) bool { return c == ',' })
|
||||||
|
if len(portCandidates) == 1 {
|
||||||
|
return portCandidates[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ports []string
|
||||||
|
var err error
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "freebsd":
|
||||||
|
ports, err = filepath.Glob("/dev/cuaU*")
|
||||||
|
case "darwin", "linux", "windows":
|
||||||
|
var portsList []*enumerator.PortDetails
|
||||||
|
portsList, err = enumerator.GetDetailedPortsList()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var preferredPortIDs [][2]uint16
|
||||||
|
for _, s := range usbInterfaces {
|
||||||
|
parts := strings.Split(s, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return "", fmt.Errorf("could not parse USB VID/PID pair %q", s)
|
||||||
|
}
|
||||||
|
vid, err := strconv.ParseUint(parts[0], 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not parse USB vendor ID %q: %w", parts[0], err)
|
||||||
|
}
|
||||||
|
pid, err := strconv.ParseUint(parts[1], 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not parse USB product ID %q: %w", parts[1], err)
|
||||||
|
}
|
||||||
|
preferredPortIDs = append(preferredPortIDs, [2]uint16{uint16(vid), uint16(pid)})
|
||||||
|
}
|
||||||
|
|
||||||
|
var primaryPorts []string // ports picked from preferred USB VID/PID
|
||||||
|
var secondaryPorts []string // other ports (as a fallback)
|
||||||
|
for _, p := range portsList {
|
||||||
|
if !p.IsUSB {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.VID != "" && p.PID != "" {
|
||||||
|
foundPort := false
|
||||||
|
vid, vidErr := strconv.ParseUint(p.VID, 16, 16)
|
||||||
|
pid, pidErr := strconv.ParseUint(p.PID, 16, 16)
|
||||||
|
if vidErr == nil && pidErr == nil {
|
||||||
|
for _, id := range preferredPortIDs {
|
||||||
|
if uint16(vid) == id[0] && uint16(pid) == id[1] {
|
||||||
|
primaryPorts = append(primaryPorts, p.Name)
|
||||||
|
foundPort = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundPort {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
secondaryPorts = append(secondaryPorts, p.Name)
|
||||||
|
}
|
||||||
|
if len(primaryPorts) == 1 {
|
||||||
|
return primaryPorts[0], nil
|
||||||
|
} else if len(primaryPorts) > 1 {
|
||||||
|
ports = primaryPorts
|
||||||
|
} else {
|
||||||
|
ports = secondaryPorts
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return "", errors.New("unable to search for a default USB device to be flashed on this OS")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if ports == nil {
|
||||||
|
return "", errors.New("unable to locate a serial port")
|
||||||
|
} else if len(ports) == 0 {
|
||||||
|
return "", errors.New("no serial ports available")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(portCandidates) == 0 {
|
||||||
|
if len(usbInterfaces) > 0 {
|
||||||
|
return "", errors.New("unable to search for a default USB device - use -port flag, available ports are " + strings.Join(ports, ", "))
|
||||||
|
} else if len(ports) == 1 {
|
||||||
|
return ports[0], nil
|
||||||
|
} else {
|
||||||
|
return "", errors.New("multiple serial ports available - use -port flag, available ports are " + strings.Join(ports, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ps := range portCandidates {
|
||||||
|
for _, p := range ports {
|
||||||
|
if p == ps {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("port you specified '" + strings.Join(portCandidates, ",") + "' does not exist, available ports are " + strings.Join(ports, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
// Flash flashes firmware to a device based on the crosscompile configuration
|
// Flash flashes firmware to a device based on the crosscompile configuration
|
||||||
func Flash(crossCompile crosscompile.Export, app string, port string, verbose bool) error {
|
func Flash(crossCompile crosscompile.Export, app string, port string, verbose bool) error {
|
||||||
if verbose {
|
|
||||||
fmt.Fprintf(os.Stderr, "Flashing %s to port %s\n", app, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
method := crossCompile.Flash.Method
|
method := crossCompile.Flash.Method
|
||||||
if method == "" {
|
if method == "" {
|
||||||
method = "command"
|
method = "command"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve port for methods that need it (all except openocd)
|
||||||
|
if method != "openocd" {
|
||||||
|
var err error
|
||||||
|
port, err = GetPort(port, crossCompile.Flash.SerialPort)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find port: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
fmt.Fprintf(os.Stderr, "Flashing %s using method: %s\n", app, method)
|
fmt.Fprintf(os.Stderr, "Flashing %s using method: %s\n", app, method)
|
||||||
|
fmt.Fprintf(os.Stderr, "Using port: %s\n", port)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch method {
|
switch method {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/goplus/llgo/internal/crosscompile"
|
"github.com/goplus/llgo/internal/flash"
|
||||||
"github.com/mattn/go-tty"
|
"github.com/mattn/go-tty"
|
||||||
"go.bug.st/serial"
|
"go.bug.st/serial"
|
||||||
)
|
)
|
||||||
@@ -39,20 +39,12 @@ func Monitor(config MonitorConfig, verbose bool) error {
|
|||||||
config.WaitTime = 300
|
config.WaitTime = 300
|
||||||
}
|
}
|
||||||
|
|
||||||
// If target is specified, try to get port from crosscompile config
|
// Resolve port using flash.GetPort
|
||||||
if config.Target != "" && config.Port == "" {
|
port, err := flash.GetPort(config.Port, nil)
|
||||||
port, err := getPortFromTarget(config.Target)
|
if err != nil {
|
||||||
if err != nil && verbose {
|
return fmt.Errorf("failed to find port: %w", err)
|
||||||
fmt.Fprintf(os.Stderr, "Warning: could not get port from target: %v\n", err)
|
|
||||||
}
|
|
||||||
if port != "" {
|
|
||||||
config.Port = port
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Port == "" {
|
|
||||||
return fmt.Errorf("port not specified and could not determine from target")
|
|
||||||
}
|
}
|
||||||
|
config.Port = port
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
fmt.Fprintf(os.Stderr, "Connecting to %s at %d baud\n", config.Port, config.BaudRate)
|
fmt.Fprintf(os.Stderr, "Connecting to %s at %d baud\n", config.Port, config.BaudRate)
|
||||||
@@ -60,7 +52,6 @@ func Monitor(config MonitorConfig, verbose bool) error {
|
|||||||
|
|
||||||
// Open serial port with retry
|
// Open serial port with retry
|
||||||
var serialConn serial.Port
|
var serialConn serial.Port
|
||||||
var err error
|
|
||||||
for i := 0; i <= config.WaitTime; i++ {
|
for i := 0; i <= config.WaitTime; i++ {
|
||||||
serialConn, err = serial.Open(config.Port, &serial.Mode{BaudRate: config.BaudRate})
|
serialConn, err = serial.Open(config.Port, &serial.Mode{BaudRate: config.BaudRate})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -138,21 +129,6 @@ func Monitor(config MonitorConfig, verbose bool) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPortFromTarget tries to get serial port from target configuration
|
|
||||||
func getPortFromTarget(target string) (string, error) {
|
|
||||||
export, err := crosscompile.Use("", "", false, target)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to get port from serial port list
|
|
||||||
if len(export.Flash.SerialPort) > 0 {
|
|
||||||
return export.Flash.SerialPort[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", fmt.Errorf("no serial port found in target configuration")
|
|
||||||
}
|
|
||||||
|
|
||||||
var addressMatch = regexp.MustCompile(`^panic: runtime error at 0x([0-9a-f]+): `)
|
var addressMatch = regexp.MustCompile(`^panic: runtime error at 0x([0-9a-f]+): `)
|
||||||
|
|
||||||
// Extract the address from the "panic: runtime error at" message.
|
// Extract the address from the "panic: runtime error at" message.
|
||||||
|
|||||||
Reference in New Issue
Block a user