diff options
-rw-r--r-- | main.go | 47 | ||||
-rw-r--r-- | monitor.go | 93 |
2 files changed, 111 insertions, 29 deletions
@@ -1436,6 +1436,7 @@ func main() { llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable") cpuprofile := flag.String("cpuprofile", "", "cpuprofile output") monitor := flag.Bool("monitor", false, "enable serial monitor") + info := flag.Bool("info", false, "print information") baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor") // Internal flags, that are only intended for TinyGo development. @@ -1731,41 +1732,29 @@ func main() { os.Exit(1) } case "monitor": - err := Monitor("", *port, options) - handleCompilerError(err) + if *info { + serialPortInfo, err := ListSerialPorts() + handleCompilerError(err) + for _, s := range serialPortInfo { + fmt.Printf("%s %4s %4s %s\n", s.Name, s.VID, s.PID, s.Target) + } + } else { + err := Monitor("", *port, options) + handleCompilerError(err) + } case "targets": - dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets") - entries, err := os.ReadDir(dir) + specs, err := GetTargetSpecs() if err != nil { fmt.Fprintln(os.Stderr, "could not list targets:", err) os.Exit(1) return } - for _, entry := range entries { - entryInfo, err := entry.Info() - if err != nil { - fmt.Fprintln(os.Stderr, "could not get entry info:", err) - os.Exit(1) - return - } - if !entryInfo.Mode().IsRegular() || !strings.HasSuffix(entry.Name(), ".json") { - // Only inspect JSON files. - continue - } - path := filepath.Join(dir, entry.Name()) - spec, err := compileopts.LoadTarget(&compileopts.Options{Target: path}) - if err != nil { - fmt.Fprintln(os.Stderr, "could not list target:", err) - os.Exit(1) - return - } - if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" { - // This doesn't look like a regular target file, but rather like - // a parent target (such as targets/cortex-m.json). - continue - } - name := entry.Name() - name = name[:len(name)-5] + names := []string{} + for key := range specs { + names = append(names, key) + } + sort.Strings(names) + for _, name := range names { fmt.Println(name) } case "info": diff --git a/monitor.go b/monitor.go index 9bef531f9..23846b352 100644 --- a/monitor.go +++ b/monitor.go @@ -11,14 +11,19 @@ import ( "io" "os" "os/signal" + "path/filepath" "regexp" "strconv" + "strings" "time" "github.com/mattn/go-tty" "github.com/tinygo-org/tinygo/builder" "github.com/tinygo-org/tinygo/compileopts" + "github.com/tinygo-org/tinygo/goenv" + "go.bug.st/serial" + "go.bug.st/serial/enumerator" ) // Monitor connects to the given port and reads/writes the serial port. @@ -128,6 +133,94 @@ func Monitor(executable, port string, options *compileopts.Options) error { return <-errCh } +// SerialPortInfo is a structure that holds information about the port and its +// associated TargetSpec. +type SerialPortInfo struct { + Name string + IsUSB bool + VID string + PID string + Target string + Spec *compileopts.TargetSpec +} + +// ListSerialPort returns serial port information and any detected TinyGo +// target +func ListSerialPorts() ([]SerialPortInfo, error) { + maps, err := GetTargetSpecs() + if err != nil { + return nil, err + } + + portsList, err := enumerator.GetDetailedPortsList() + if err != nil { + return nil, err + } + + serialPortInfo := []SerialPortInfo{} + for _, p := range portsList { + info := SerialPortInfo{ + Name: p.Name, + IsUSB: p.IsUSB, + VID: p.VID, + PID: p.PID, + } + vid := strings.ToLower(p.VID) + pid := strings.ToLower(p.PID) + for k, v := range maps { + usbInterfaces := v.SerialPort + for _, s := range usbInterfaces { + parts := strings.Split(s, ":") + if len(parts) != 2 { + continue + } + if vid == strings.ToLower(parts[0]) && pid == strings.ToLower(parts[1]) { + info.Target = k + info.Spec = v + } + } + } + serialPortInfo = append(serialPortInfo, info) + } + + return serialPortInfo, nil +} + +func GetTargetSpecs() (map[string]*compileopts.TargetSpec, error) { + dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets") + entries, err := os.ReadDir(dir) + if err != nil { + return nil, fmt.Errorf("could not list targets: %w", err) + } + + maps := map[string]*compileopts.TargetSpec{} + for _, entry := range entries { + entryInfo, err := entry.Info() + if err != nil { + return nil, fmt.Errorf("could not get entry info: %w", err) + } + if !entryInfo.Mode().IsRegular() || !strings.HasSuffix(entry.Name(), ".json") { + // Only inspect JSON files. + continue + } + path := filepath.Join(dir, entry.Name()) + spec, err := compileopts.LoadTarget(&compileopts.Options{Target: path}) + if err != nil { + return nil, fmt.Errorf("cnuld not list target: %w", err) + } + if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" { + // This doesn't look like a regular target file, but rather like + // a parent target (such as targets/cortex-m.json). + continue + } + name := entry.Name() + name = name[:len(name)-5] + //fmt.Println(name) + maps[name] = spec + } + return maps, nil +} + var addressMatch = regexp.MustCompile(`^panic: runtime error at 0x([0-9a-f]+): `) // Extract the address from the "panic: runtime error at" message. |