@ -155,43 +155,54 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
return nil
return nil
}
}
return builder . Build ( pkgName , outpath , config , func ( result builder . BuildResult ) error {
// Create a temporary directory for intermediary files.
if outpath == "" {
tmpdir , err := os . MkdirTemp ( "" , "tinygo" )
if strings . HasSuffix ( pkgName , ".go" ) {
if err != nil {
// A Go file was specified directly on the command line.
return err
// Base the binary name off of it.
}
outpath = filepath . Base ( pkgName [ : len ( pkgName ) - 3 ] ) + config . DefaultBinaryExtension ( )
defer os . RemoveAll ( tmpdir )
} else {
// Pick a default output path based on the main directory.
outpath = filepath . Base ( result . MainDir ) + config . DefaultBinaryExtension ( )
}
}
if err := os . Rename ( result . Binary , outpath ) ; err != nil {
// Moving failed. Do a file copy.
inf , err := os . Open ( result . Binary )
if err != nil {
return err
}
defer inf . Close ( )
outf , err := os . OpenFile ( outpath , os . O_RDWR | os . O_CREATE | os . O_TRUNC , 0777 )
if err != nil {
return err
}
// Copy data to output file .
// Do the build.
_ , err = io . Copy ( outf , inf )
result , err := builder . Build ( pkgName , outpath , tmpdir , config )
if err != nil {
if err != nil {
return err
return err
}
}
// Check whether file writing was successful.
if outpath == "" {
return outf . Close ( )
if strings . HasSuffix ( pkgName , ".go" ) {
// A Go file was specified directly on the command line.
// Base the binary name off of it.
outpath = filepath . Base ( pkgName [ : len ( pkgName ) - 3 ] ) + config . DefaultBinaryExtension ( )
} else {
} else {
// Move was successful.
// Pick a default output path based on the main directory.
return nil
outpath = filepath . Base ( result . MainDir ) + config . DefaultBinaryExtension ( )
}
}
} )
}
if err := os . Rename ( result . Binary , outpath ) ; err != nil {
// Moving failed. Do a file copy.
inf , err := os . Open ( result . Binary )
if err != nil {
return err
}
defer inf . Close ( )
outf , err := os . OpenFile ( outpath , os . O_RDWR | os . O_CREATE | os . O_TRUNC , 0777 )
if err != nil {
return err
}
// Copy data to output file.
_ , err = io . Copy ( outf , inf )
if err != nil {
return err
}
// Check whether file writing was successful.
return outf . Close ( )
}
// Move was successful.
return nil
}
}
// Test runs the tests in the given package. Returns whether the test passed and
// Test runs the tests in the given package. Returns whether the test passed and
@ -371,116 +382,128 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
return errors . New ( "unknown flash method: " + flashMethod )
return errors . New ( "unknown flash method: " + flashMethod )
}
}
return builder . Build ( pkgName , fileExt , config , func ( result builder . BuildResult ) error {
// Create a temporary directory for intermediary files.
// do we need port reset to put MCU into bootloader mode?
tmpdir , err := os . MkdirTemp ( "" , "tinygo" )
if config . Target . PortReset == "true" && flashMethod != "openocd" {
if err != nil {
port , err := getDefaultPort ( port , config . Target . SerialPort )
return err
if err == nil {
}
err = touchSerialPortAt1200bps ( port )
defer os . RemoveAll ( tmpdir )
if err != nil {
return & commandError { "failed to reset port" , result . Binary , err }
}
// give the target MCU a chance to restart into bootloader
time . Sleep ( 3 * time . Second )
}
}
// this flashing method copies the binary data to a Mass Storage Device (msd)
// Build the binary.
switch flashMethod {
result , err := builder . Build ( pkgName , fileExt , tmpdir , config )
case "" , "command" :
if err != nil {
// Create the command.
return err
flashCmd := config . Target . FlashCommand
}
flashCmdList , err := shlex . Split ( flashCmd )
if err != nil {
return fmt . Errorf ( "could not parse flash command %#v: %w" , flashCmd , err )
}
if strings . Contains ( flashCmd , "{port}" ) {
// do we need port reset to put MCU into bootloader mode?
var err error
if config . Target . PortReset == "true" && flashMethod != "openocd" {
port , err = getDefaultPort ( port , config . Target . SerialPort )
port , err := getDefaultPort ( port , config . Target . SerialPort )
if err != nil {
if err == nil {
return err
err = touchSerialPortAt1200bps ( port )
}
if err != nil {
return & commandError { "failed to reset port" , result . Binary , err }
}
}
// give the target MCU a chance to restart into bootloader
time . Sleep ( 3 * time . Second )
}
}
// Fill in fields in the command template.
// Flash the binary to the MCU.
fileToken := "{" + fileExt [ 1 : ] + "}"
switch flashMethod {
for i , arg := range flashCmdList {
case "" , "command" :
arg = strings . ReplaceAll ( arg , fileToken , result . Binary )
// Create the command.
arg = strings . ReplaceAll ( arg , "{port}" , port )
flashCmd := config . Target . FlashCommand
flashCmdList [ i ] = arg
flashCmdList , err := shlex . Split ( flashCmd )
}
if err != nil {
return fmt . Errorf ( "could not parse flash command %#v: %w" , flashCmd , err )
}
// Execute the command.
if strings . Contains ( flashCmd , "{port}" ) {
if len ( flashCmdList ) < 2 {
var err error
return fmt . Errorf ( "invalid flash command: %#v" , flashCmd )
port , err = getDefaultPort ( port , config . Target . SerialPort )
}
cmd := executeCommand ( config . Options , flashCmdList [ 0 ] , flashCmdList [ 1 : ] ... )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
cmd . Dir = goenv . Get ( "TINYGOROOT" )
err = cmd . Run ( )
if err != nil {
return & commandError { "failed to flash" , result . Binary , err }
}
case "msd" :
switch fileExt {
case ".uf2" :
err := flashUF2UsingMSD ( config . Target . FlashVolume , result . Binary , config . Options )
if err != nil {
return & commandError { "failed to flash" , result . Binary , err }
}
case ".hex" :
err := flashHexUsingMSD ( config . Target . FlashVolume , result . Binary , config . Options )
if err != nil {
return & commandError { "failed to flash" , result . Binary , err }
}
default :
return errors . New ( "mass storage device flashing currently only supports uf2 and hex" )
}
case "openocd" :
args , err := config . OpenOCDConfiguration ( )
if err != nil {
if err != nil {
return err
return err
}
}
exit := " reset exit"
}
if config . Target . OpenOCDVerify != nil && * config . Target . OpenOCDVerify {
exit = " verify" + exit
// Fill in fields in the command template.
}
fileToken := "{" + fileExt [ 1 : ] + "}"
args = append ( args , "-c" , "program " + filepath . ToSlash ( result . Binary ) + exit )
for i , arg := range flashCmdList {
cmd := executeCommand ( config . Options , "openocd" , args ... )
arg = strings . ReplaceAll ( arg , fileToken , result . Binary )
cmd . Stdout = os . Stdout
arg = strings . ReplaceAll ( arg , "{port}" , port )
cmd . Stderr = os . Stderr
flashCmdList [ i ] = arg
err = cmd . Run ( )
}
// Execute the command.
if len ( flashCmdList ) < 2 {
return fmt . Errorf ( "invalid flash command: %#v" , flashCmd )
}
cmd := executeCommand ( config . Options , flashCmdList [ 0 ] , flashCmdList [ 1 : ] ... )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
cmd . Dir = goenv . Get ( "TINYGOROOT" )
err = cmd . Run ( )
if err != nil {
return & commandError { "failed to flash" , result . Binary , err }
}
case "msd" :
// this flashing method copies the binary data to a Mass Storage Device (msd)
switch fileExt {
case ".uf2" :
err := flashUF2UsingMSD ( config . Target . FlashVolume , result . Binary , config . Options )
if err != nil {
if err != nil {
return & commandError { "failed to flash" , result . Binary , err }
return & commandError { "failed to flash" , result . Binary , err }
}
}
case "bmp" :
case ".hex" :
gdb , err := config . Target . LookupGDB ( )
err := flashHexUsingMSD ( config . Target . FlashVolume , result . Binary , config . Options )
if err != nil {
return err
}
var bmpGDBPort string
bmpGDBPort , _ , err = getBMPPorts ( )
if err != nil {
return err
}
args := [ ] string { "-ex" , "target extended-remote " + bmpGDBPort , "-ex" , "monitor swdp_scan" , "-ex" , "attach 1" , "-ex" , "load" , filepath . ToSlash ( result . Binary ) }
cmd := executeCommand ( config . Options , gdb , args ... )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
err = cmd . Run ( )
if err != nil {
if err != nil {
return & commandError { "failed to flash" , result . Binary , err }
return & commandError { "failed to flash" , result . Binary , err }
}
}
default :
default :
return fmt . Errorf ( "unknown flash method: %s" , flashMethod )
return errors . New ( "mass storage device flashing currently only supports uf2 and hex" )
}
}
if options . Monitor {
case "openocd" :
return Monitor ( "" , options )
args , err := config . OpenOCDConfiguration ( )
if err != nil {
return err
}
}
return nil
exit := " reset exit"
} )
if config . Target . OpenOCDVerify != nil && * config . Target . OpenOCDVerify {
exit = " verify" + exit
}
args = append ( args , "-c" , "program " + filepath . ToSlash ( result . Binary ) + exit )
cmd := executeCommand ( config . Options , "openocd" , args ... )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
err = cmd . Run ( )
if err != nil {
return & commandError { "failed to flash" , result . Binary , err }
}
case "bmp" :
gdb , err := config . Target . LookupGDB ( )
if err != nil {
return err
}
var bmpGDBPort string
bmpGDBPort , _ , err = getBMPPorts ( )
if err != nil {
return err
}
args := [ ] string { "-ex" , "target extended-remote " + bmpGDBPort , "-ex" , "monitor swdp_scan" , "-ex" , "attach 1" , "-ex" , "load" , filepath . ToSlash ( result . Binary ) }
cmd := executeCommand ( config . Options , gdb , args ... )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
err = cmd . Run ( )
if err != nil {
return & commandError { "failed to flash" , result . Binary , err }
}
default :
return fmt . Errorf ( "unknown flash method: %s" , flashMethod )
}
if options . Monitor {
return Monitor ( "" , options )
}
return nil
}
}
// Debug compiles and flashes a program to a microcontroller (just like Flash)
// Debug compiles and flashes a program to a microcontroller (just like Flash)
@ -506,195 +529,206 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
return err
return err
}
}
// Create a temporary directory for intermediary files.
tmpdir , err := os . MkdirTemp ( "" , "tinygo" )
if err != nil {
return err
}
defer os . RemoveAll ( tmpdir )
// Build the binary to debug.
format , fileExt := config . EmulatorFormat ( )
format , fileExt := config . EmulatorFormat ( )
return builder . Build ( pkgName , fileExt , config , func ( result builder . BuildResult ) error {
result , err := builder . Build ( pkgName , fileExt , tmpdir , config )
// Find a good way to run GDB.
if err != nil {
gdbInterface , openocdInterface := config . Programmer ( )
return err
switch gdbInterface {
}
case "msd" , "command" , "" :
emulator := config . EmulatorName ( )
// Find a good way to run GDB.
if emulator != "" {
gdbInterface , openocdInterface := config . Programmer ( )
if emulator == "mgba" {
switch gdbInterface {
gdbInterface = "mgba"
case "msd" , "command" , "" :
} else if emulator == "simavr" {
emulator := config . EmulatorName ( )
gdbInterface = "simavr"
if emulator != "" {
} else if strings . HasPrefix ( emulator , "qemu-system-" ) {
if emulator == "mgba" {
gdbInterface = "qemu"
gdbInterface = "mgba"
} else {
} else if emulator == "simavr" {
// Assume QEMU as an emulator.
gdbInterface = "simavr"
gdbInterface = "qemu-user"
} else if strings . HasPrefix ( emulator , "qemu-system-" ) {
}
gdbInterface = "qemu"
} else if openocdInterface != "" && config . Target . OpenOCDTarget != "" {
gdbInterface = "openocd"
} else if config . Target . JLinkDevice != "" {
gdbInterface = "jlink"
} else {
} else {
gdbInterface = "native"
// Assume QEMU as an emulator.
gdbInterface = "qemu-user"
}
}
} else if openocdInterface != "" && config . Target . OpenOCDTarget != "" {
gdbInterface = "openocd"
} else if config . Target . JLinkDevice != "" {
gdbInterface = "jlink"
} else {
gdbInterface = "native"
}
}
}
// Run the GDB server, if necessary.
// Run the GDB server, if necessary.
port := ""
port := ""
var gdbCommands [ ] string
var gdbCommands [ ] string
var daemon * exec . Cmd
var daemon * exec . Cmd
emulator , err := config . Emulator ( format , result . Binary )
emulator , err := config . Emulator ( format , result . Binary )
if err != nil {
return err
}
switch gdbInterface {
case "native" :
// Run GDB directly.
case "bmp" :
var bmpGDBPort string
bmpGDBPort , _ , err = getBMPPorts ( )
if err != nil {
if err != nil {
return err
return err
}
}
switch gdbInterface {
port = bmpGDBPort
case "native" :
gdbCommands = append ( gdbCommands , "monitor swdp_scan" , "compare-sections" , "attach 1" , "load" )
// Run GDB directly.
case "openocd" :
case "bmp" :
port = ":3333"
var bmpGDBPort string
gdbCommands = append ( gdbCommands , "monitor halt" , "load" , "monitor reset halt" )
bmpGDBPort , _ , err = getBMPPorts ( )
if err != nil {
// We need a separate debugging daemon for on-chip debugging.
return err
args , err := config . OpenOCDConfiguration ( )
}
if err != nil {
port = bmpGDBPort
return err
gdbCommands = append ( gdbCommands , "monitor swdp_scan" , "compare-sections" , "attach 1" , "load" )
}
case "openocd" :
daemon = executeCommand ( config . Options , "openocd" , args ... )
port = ":3333"
if ocdOutput {
gdbCommands = append ( gdbCommands , "monitor halt" , "load" , "monitor reset halt" )
// Make it clear which output is from the daemon.
w := & ColorWriter {
// We need a separate debugging daemon for on-chip debugging.
Out : colorable . NewColorableStderr ( ) ,
args , err := config . OpenOCDConfiguration ( )
Prefix : "openocd: " ,
if err != nil {
Color : TermColorYellow ,
return err
}
daemon = executeCommand ( config . Options , "openocd" , args ... )
if ocdOutput {
// Make it clear which output is from the daemon.
w := & ColorWriter {
Out : colorable . NewColorableStderr ( ) ,
Prefix : "openocd: " ,
Color : TermColorYellow ,
}
daemon . Stdout = w
daemon . Stderr = w
}
}
case "jlink" :
daemon . Stdout = w
port = ":2331"
daemon . Stderr = w
gdbCommands = append ( gdbCommands , "load" , "monitor reset halt" )
}
case "jlink" :
// We need a separate debugging daemon for on-chip debugging.
port = ":2331"
daemon = executeCommand ( config . Options , "JLinkGDBServer" , "-device" , config . Target . JLinkDevice )
gdbCommands = append ( gdbCommands , "load" , "monitor reset halt" )
if ocdOutput {
// Make it clear which output is from the daemon .
// We need a separate debugging daemon for on-chip debugging.
w := & ColorWriter {
daemon = executeCommand ( config . Options , "JLinkGDBServer" , "-device" , config . Target . JLinkDevice )
Out : colorable . NewColorableStderr ( ) ,
if ocdOutput {
Prefix : "jlink: " ,
// Make it clear which output is from the daemon.
Color : TermColorYellow ,
w := & ColorWriter {
}
Out : colorable . NewColorableStderr ( ) ,
daemon . Stdout = w
Prefix : "jlink: " ,
daemon . Stderr = w
Color : TermColorYellow ,
}
}
case "qemu" :
daemon . Stdout = w
port = ":1234"
daemon . Stderr = w
// Run in an emulator.
}
args := append ( emulator [ 1 : ] , "-s" , "-S" )
case "qemu" :
daemon = executeCommand ( config . Options , emulator [ 0 ] , args ... )
port = ":1234"
daemon . Stdout = os . Stdout
// Run in an emulator.
daemon . Stderr = os . Stderr
args := append ( emulator [ 1 : ] , "-s" , "-S" )
case "qemu-user" :
daemon = executeCommand ( config . Options , emulator [ 0 ] , args ... )
port = ":1234"
daemon . Stdout = os . Stdout
// Run in an emulator.
daemon . Stderr = os . Stderr
args := append ( emulator [ 1 : ] , "-g" , "1234" )
case "qemu-user" :
daemon = executeCommand ( config . Options , emulator [ 0 ] , args ... )
port = ":1234"
daemon . Stdout = os . Stdout
// Run in an emulator.
daemon . Stderr = os . Stderr
args := append ( emulator [ 1 : ] , "-g" , "1234" )
case "mgba" :
daemon = executeCommand ( config . Options , emulator [ 0 ] , args ... )
port = ":2345"
daemon . Stdout = os . Stdout
// Run in an emulator.
daemon . Stderr = os . Stderr
args := append ( emulator [ 1 : ] , "-g" )
case "mgba" :
daemon = executeCommand ( config . Options , emulator [ 0 ] , args ... )
port = ":2345"
daemon . Stdout = os . Stdout
// Run in an emulator.
daemon . Stderr = os . Stderr
args := append ( emulator [ 1 : ] , "-g" )
case "simavr" :
daemon = executeCommand ( config . Options , emulator [ 0 ] , args ... )
port = ":1234"
daemon . Stdout = os . Stdout
// Run in an emulator.
daemon . Stderr = os . Stderr
args := append ( emulator [ 1 : ] , "-g" )
case "simavr" :
daemon = executeCommand ( config . Options , emulator [ 0 ] , args ... )
port = ":1234"
daemon . Stdout = os . Stdout
// Run in an emulator.
daemon . Stderr = os . Stderr
args := append ( emulator [ 1 : ] , "-g" )
case "msd" :
daemon = executeCommand ( config . Options , emulator [ 0 ] , args ... )
return errors . New ( "gdb is not supported for drag-and-drop programmable devices" )
daemon . Stdout = os . Stdout
default :
daemon . Stderr = os . Stderr
return fmt . Errorf ( "gdb is not supported with interface %#v" , gdbInterface )
case "msd" :
}
return errors . New ( "gdb is not supported for drag-and-drop programmable devices" )
default :
return fmt . Errorf ( "gdb is not supported with interface %#v" , gdbInterface )
}
if daemon != nil {
if daemon != nil {
// Make sure the daemon doesn't receive Ctrl-C that is intended for
// Make sure the daemon doesn't receive Ctrl-C that is intended for
// GDB (to break the currently executing program).
// GDB (to break the currently executing program).
setCommandAsDaemon ( daemon )
setCommandAsDaemon ( daemon )
// Start now, and kill it on exit.
// Start now, and kill it on exit.
err = daemon . Start ( )
err = daemon . Start ( )
if err != nil {
if err != nil {
return & commandError { "failed to run" , daemon . Path , err }
return & commandError { "failed to run" , daemon . Path , err }
}
defer func ( ) {
daemon . Process . Signal ( os . Interrupt )
var stopped uint32
go func ( ) {
time . Sleep ( time . Millisecond * 100 )
if atomic . LoadUint32 ( & stopped ) == 0 {
daemon . Process . Kill ( )
}
} ( )
daemon . Wait ( )
atomic . StoreUint32 ( & stopped , 1 )
} ( )
}
}
defer func ( ) {
// Ignore Ctrl-C, it must be passed on to GDB.
daemon . Process . Signal ( os . Interrupt )
c := make ( chan os . Signal , 1 )
var stopped uint32
signal . Notify ( c , os . Interrupt )
go func ( ) {
go func ( ) {
time . Sleep ( time . Millisecond * 100 )
for range c {
if atomic . LoadUint32 ( & stopped ) == 0 {
}
daemon . Process . Kill ( )
}
} ( )
daemon . Wait ( )
atomic . StoreUint32 ( & stopped , 1 )
} ( )
} ( )
}
// Construct and execute a gdb or lldb command.
// Ignore Ctrl-C, it must be passed on to GDB.
// By default: gdb -ex run <binary>
c := make ( chan os . Signal , 1 )
// Exit the debugger with Ctrl-D.
signal . Notify ( c , os . Interrupt )
params := [ ] string { result . Executable }
go func ( ) {
switch debugger {
for range c {
case "gdb" :
}
if port != "" {
} ( )
params = append ( params , "-ex" , "target extended-remote " + port )
}
// Construct and execute a gdb or lldb command.
for _ , cmd := range gdbCommands {
// By default: gdb -ex run <binary>
params = append ( params , "-ex" , cmd )
// Exit the debugger with Ctrl-D.
}
params := [ ] string { result . Executable }
case "lldb" :
switch debugger {
params = append ( params , "--arch" , config . Triple ( ) )
case "gdb" :
if port != "" {
if port != "" {
if strings . HasPrefix ( port , ":" ) {
params = append ( params , "-ex" , "target extended-remote " + port )
params = append ( params , "-o" , "gdb-remote " + port [ 1 : ] )
}
} else {
for _ , cmd := range gdbCommands {
return fmt . Errorf ( "cannot use LLDB over a gdb-remote that isn't a TCP port: %s" , port )
params = append ( params , "-ex" , cmd )
}
}
}
case "lldb" :
for _ , cmd := range gdbCommands {
params = append ( params , "--arch" , config . Triple ( ) )
if strings . HasPrefix ( cmd , "monitor " ) {
if port != "" {
params = append ( params , "-o" , "process plugin packet " + cmd )
if strings . HasPrefix ( port , ":" ) {
} else if cmd == "load" {
params = append ( params , "-o" , "gdb-remote " + port [ 1 : ] )
params = append ( params , "-o" , "target modules load --load --slide 0" )
} else {
} else {
return fmt . Errorf ( "cannot use LLDB over a gdb-remote that isn't a TCP port: %s" , port )
return fmt . Errorf ( "don't know how to convert GDB command %#v to LLDB" , cmd )
}
}
}
}
}
cmd := executeCommand ( config . Options , cmdName , params ... )
for _ , cmd := range gdbCommands {
cmd . Stdin = os . Stdin
if strings . HasPrefix ( cmd , "monitor " ) {
cmd . Stdout = os . Stdout
params = append ( params , "-o" , "process plugin packet " + cmd )
cmd . Stderr = os . Stderr
} else if cmd == "load" {
err = cmd . Run ( )
params = append ( params , "-o" , "target modules load --load --slide 0" )
if err != nil {
} else {
return & commandError { "failed to run " + cmdName + " with" , result . Executable , err }
return fmt . Errorf ( "don't know how to convert GDB command %#v to LLDB" , cmd )
}
}
}
return nil
}
} )
cmd := executeCommand ( config . Options , cmdName , params ... )
cmd . Stdin = os . Stdin
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
err = cmd . Run ( )
if err != nil {
return & commandError { "failed to run " + cmdName + " with" , result . Executable , err }
}
return nil
}
}
// Run compiles and runs the given program. Depending on the target provided in
// Run compiles and runs the given program. Depending on the target provided in
@ -767,69 +801,80 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
env = environmentVars
env = environmentVars
}
}
// Create a temporary directory for intermediary files.
tmpdir , err := os . MkdirTemp ( "" , "tinygo" )
if err != nil {
return err
}
defer os . RemoveAll ( tmpdir )
// Build the binary to be run.
format , fileExt := config . EmulatorFormat ( )
format , fileExt := config . EmulatorFormat ( )
return builder . Build ( pkgName , fileExt , config , func ( result builder . BuildResult ) error {
result , err := builder . Build ( pkgName , fileExt , tmpdir , config )
// If needed, set a timeout on the command. This is done in tests so
if err != nil {
// they don't waste resources on a stalled test.
return err
var ctx context . Context
}
if timeout != 0 {
var cancel context . CancelFunc
ctx , cancel = context . WithTimeout ( context . Background ( ) , timeout )
defer cancel ( )
}
// Set up the command.
var name string
if config . Target . Emulator == "" {
name = result . Binary
} else {
emulator , err := config . Emulator ( format , result . Binary )
if err != nil {
return err
}
name = emulator [ 0 ]
emuArgs := append ( [ ] string ( nil ) , emulator [ 1 : ] ... )
args = append ( emuArgs , args ... )
}
var cmd * exec . Cmd
if ctx != nil {
cmd = exec . CommandContext ( ctx , name , args ... )
} else {
cmd = exec . Command ( name , args ... )
}
cmd . Env = env
// Configure stdout/stderr. The stdout may go to a buffer, not a real
// If needed, set a timeout on the command. This is done in tests so
// stdout.
// they don't waste resources on a stalled test.
cmd . Stdout = stdout
var ctx context . Context
cmd . Stderr = os . Stderr
if timeout != 0 {
if config . EmulatorName ( ) == "simavr" {
var cancel context . CancelFunc
cmd . Stdout = nil // don't print initial load commands
ctx , cancel = context . WithTimeout ( context . Background ( ) , timeout )
cmd . Stderr = stdout
defer cancel ( )
}
// Set up the command.
var name string
if config . Target . Emulator == "" {
name = result . Binary
} else {
emulator , err := config . Emulator ( format , result . Binary )
if err != nil {
return err
}
}
name = emulator [ 0 ]
emuArgs := append ( [ ] string ( nil ) , emulator [ 1 : ] ... )
args = append ( emuArgs , args ... )
}
var cmd * exec . Cmd
if ctx != nil {
cmd = exec . CommandContext ( ctx , name , args ... )
} else {
cmd = exec . Command ( name , args ... )
}
cmd . Env = env
// If this is a test, reserve CPU time for it so that increased
// Configure stdout/stderr. The stdout may go to a buffer, not a real
// parallelism doesn't blow up memory usage. If this isn't a test but
// stdout.
// simply `tinygo run`, then it is practically a no-op.
cmd . Stdout = stdout
config . Options . Semaphore <- struct { } { }
cmd . Stderr = os . Stderr
defer func ( ) {
if config . EmulatorName ( ) == "simavr" {
<- config . Options . Semaphore
cmd . Stdout = nil // don't print initial load commands
} ( )
cmd . Stderr = stdout
}
// Run binary.
// If this is a test, reserve CPU time for it so that increased
if config . Options . PrintCommands != nil {
// parallelism doesn't blow up memory usage. If this isn't a test but
config . Options . PrintCommands ( cmd . Path , cmd . Args ... )
// simply `tinygo run`, then it is practically a no-op.
}
config . Options . Semaphore <- struct { } { }
err := run ( cmd , result )
defer func ( ) {
if err != nil {
<- config . Options . Semaphore
if ctx != nil && ctx . Err ( ) == context . DeadlineExceeded {
} ( )
stdout . Write ( [ ] byte ( fmt . Sprintf ( "--- timeout of %s exceeded, terminating...\n" , timeout ) ) )
err = ctx . Err ( )
// Run binary.
}
if config . Options . PrintCommands != nil {
return & commandError { "failed to run compiled binary" , result . Binary , err }
config . Options . PrintCommands ( cmd . Path , cmd . Args ... )
}
err = run ( cmd , result )
if err != nil {
if ctx != nil && ctx . Err ( ) == context . DeadlineExceeded {
stdout . Write ( [ ] byte ( fmt . Sprintf ( "--- timeout of %s exceeded, terminating...\n" , timeout ) ) )
err = ctx . Err ( )
}
}
return nil
return & commandError { "failed to run compiled binary" , result . Binary , err }
} )
}
return nil
}
}
func touchSerialPortAt1200bps ( port string ) ( err error ) {
func touchSerialPortAt1200bps ( port string ) ( err error ) {