@@ -11,14 +11,19 @@ import (
1111 "io"
1212 "os"
1313 "os/signal"
14+ "path/filepath"
1415 "regexp"
1516 "strconv"
17+ "strings"
1618 "time"
1719
1820 "github.com/mattn/go-tty"
1921 "github.com/tinygo-org/tinygo/builder"
2022 "github.com/tinygo-org/tinygo/compileopts"
23+ "github.com/tinygo-org/tinygo/goenv"
24+
2125 "go.bug.st/serial"
26+ "go.bug.st/serial/enumerator"
2227)
2328
2429// 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 {
128133 return <- errCh
129134}
130135
136+ // SerialPortInfo is a structure that holds information about the port and its
137+ // associated TargetSpec.
138+ type SerialPortInfo struct {
139+ Name string
140+ IsUSB bool
141+ VID string
142+ PID string
143+ Target string
144+ Spec * compileopts.TargetSpec
145+ }
146+
147+ // ListSerialPort returns serial port information and any detected TinyGo
148+ // target
149+ func ListSerialPorts () ([]SerialPortInfo , error ) {
150+ maps , err := GetTargetSpecs ()
151+ if err != nil {
152+ return nil , err
153+ }
154+
155+ portsList , err := enumerator .GetDetailedPortsList ()
156+ if err != nil {
157+ return nil , err
158+ }
159+
160+ serialPortInfo := []SerialPortInfo {}
161+ for _ , p := range portsList {
162+ info := SerialPortInfo {
163+ Name : p .Name ,
164+ IsUSB : p .IsUSB ,
165+ VID : p .VID ,
166+ PID : p .PID ,
167+ }
168+ vid := strings .ToLower (p .VID )
169+ pid := strings .ToLower (p .PID )
170+ for k , v := range maps {
171+ usbInterfaces := v .SerialPort
172+ for _ , s := range usbInterfaces {
173+ parts := strings .Split (s , ":" )
174+ if len (parts ) != 2 {
175+ continue
176+ }
177+ if vid == strings .ToLower (parts [0 ]) && pid == strings .ToLower (parts [1 ]) {
178+ info .Target = k
179+ info .Spec = v
180+ }
181+ }
182+ }
183+ serialPortInfo = append (serialPortInfo , info )
184+ }
185+
186+ return serialPortInfo , nil
187+ }
188+
189+ func GetTargetSpecs () (map [string ]* compileopts.TargetSpec , error ) {
190+ dir := filepath .Join (goenv .Get ("TINYGOROOT" ), "targets" )
191+ entries , err := os .ReadDir (dir )
192+ if err != nil {
193+ return nil , fmt .Errorf ("could not list targets: %w" , err )
194+ }
195+
196+ maps := map [string ]* compileopts.TargetSpec {}
197+ for _ , entry := range entries {
198+ entryInfo , err := entry .Info ()
199+ if err != nil {
200+ return nil , fmt .Errorf ("could not get entry info: %w" , err )
201+ }
202+ if ! entryInfo .Mode ().IsRegular () || ! strings .HasSuffix (entry .Name (), ".json" ) {
203+ // Only inspect JSON files.
204+ continue
205+ }
206+ path := filepath .Join (dir , entry .Name ())
207+ spec , err := compileopts .LoadTarget (& compileopts.Options {Target : path })
208+ if err != nil {
209+ return nil , fmt .Errorf ("cnuld not list target: %w" , err )
210+ }
211+ if spec .FlashMethod == "" && spec .FlashCommand == "" && spec .Emulator == "" {
212+ // This doesn't look like a regular target file, but rather like
213+ // a parent target (such as targets/cortex-m.json).
214+ continue
215+ }
216+ name := entry .Name ()
217+ name = name [:len (name )- 5 ]
218+ //fmt.Println(name)
219+ maps [name ] = spec
220+ }
221+ return maps , nil
222+ }
223+
131224var addressMatch = regexp .MustCompile (`^panic: runtime error at 0x([0-9a-f]+): ` )
132225
133226// Extract the address from the "panic: runtime error at" message.
0 commit comments