github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"context"
     7  	"encoding/json"
     8  	"errors"
     9  	"flag"
    10  	"fmt"
    11  	"go/scanner"
    12  	"go/types"
    13  	"io"
    14  	"os"
    15  	"os/exec"
    16  	"os/signal"
    17  	"path/filepath"
    18  	"regexp"
    19  	"runtime"
    20  	"runtime/pprof"
    21  	"sort"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  	"sync/atomic"
    26  	"time"
    27  
    28  	"github.com/google/shlex"
    29  	"github.com/inhies/go-bytesize"
    30  	"github.com/mattn/go-colorable"
    31  	"github.com/tinygo-org/tinygo/builder"
    32  	"github.com/tinygo-org/tinygo/compileopts"
    33  	"github.com/tinygo-org/tinygo/goenv"
    34  	"github.com/tinygo-org/tinygo/interp"
    35  	"github.com/tinygo-org/tinygo/loader"
    36  	"golang.org/x/tools/go/buildutil"
    37  	"tinygo.org/x/go-llvm"
    38  
    39  	"go.bug.st/serial"
    40  	"go.bug.st/serial/enumerator"
    41  )
    42  
    43  // commandError is an error type to wrap os/exec.Command errors. This provides
    44  // some more information regarding what went wrong while running a command.
    45  type commandError struct {
    46  	Msg  string
    47  	File string
    48  	Err  error
    49  }
    50  
    51  func (e *commandError) Error() string {
    52  	return e.Msg + " " + e.File + ": " + e.Err.Error()
    53  }
    54  
    55  // moveFile renames the file from src to dst. If renaming doesn't work (for
    56  // example, the rename crosses a filesystem boundary), the file is copied and
    57  // the old file is removed.
    58  func moveFile(src, dst string) error {
    59  	err := os.Rename(src, dst)
    60  	if err == nil {
    61  		// Success!
    62  		return nil
    63  	}
    64  	// Failed to move, probably a different filesystem.
    65  	// Do a copy + remove.
    66  	err = copyFile(src, dst)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	return os.Remove(src)
    71  }
    72  
    73  // copyFile copies the given file or directory from src to dst. It can copy over
    74  // a possibly already existing file (but not directory) at the destination.
    75  func copyFile(src, dst string) error {
    76  	source, err := os.Open(src)
    77  	if err != nil {
    78  		return err
    79  	}
    80  	defer source.Close()
    81  
    82  	st, err := source.Stat()
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	if st.IsDir() {
    88  		err := os.Mkdir(dst, st.Mode().Perm())
    89  		if err != nil {
    90  			return err
    91  		}
    92  		names, err := source.Readdirnames(0)
    93  		if err != nil {
    94  			return err
    95  		}
    96  		for _, name := range names {
    97  			err := copyFile(filepath.Join(src, name), filepath.Join(dst, name))
    98  			if err != nil {
    99  				return err
   100  			}
   101  		}
   102  		return nil
   103  	} else {
   104  		destination, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, st.Mode())
   105  		if err != nil {
   106  			return err
   107  		}
   108  		defer destination.Close()
   109  
   110  		_, err = io.Copy(destination, source)
   111  		return err
   112  	}
   113  }
   114  
   115  // executeCommand is a simple wrapper to exec.Cmd
   116  func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
   117  	if options.PrintCommands != nil {
   118  		options.PrintCommands(name, arg...)
   119  	}
   120  	return exec.Command(name, arg...)
   121  }
   122  
   123  // printCommand prints a command to stdout while formatting it like a real
   124  // command (escaping characters etc). The resulting command should be easy to
   125  // run directly in a shell, although it is not guaranteed to be a safe shell
   126  // escape. That's not a problem as the primary use case is printing the command,
   127  // not running it.
   128  func printCommand(cmd string, args ...string) {
   129  	command := append([]string{cmd}, args...)
   130  	for i, arg := range command {
   131  		// Source: https://www.oreilly.com/library/view/learning-the-bash/1565923472/ch01s09.html
   132  		const specialChars = "~`#$&*()\\|[]{};'\"<>?! "
   133  		if strings.ContainsAny(arg, specialChars) {
   134  			// See: https://stackoverflow.com/questions/15783701/which-characters-need-to-be-escaped-when-using-bash
   135  			arg = "'" + strings.ReplaceAll(arg, `'`, `'\''`) + "'"
   136  			command[i] = arg
   137  		}
   138  	}
   139  	fmt.Fprintln(os.Stderr, strings.Join(command, " "))
   140  }
   141  
   142  // Build compiles and links the given package and writes it to outpath.
   143  func Build(pkgName, outpath string, options *compileopts.Options) error {
   144  	config, err := builder.NewConfig(options)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	if options.PrintJSON {
   150  		b, err := json.MarshalIndent(config, "", "  ")
   151  		if err != nil {
   152  			handleCompilerError(err)
   153  		}
   154  		fmt.Printf("%s\n", string(b))
   155  		return nil
   156  	}
   157  
   158  	// Create a temporary directory for intermediary files.
   159  	tmpdir, err := os.MkdirTemp("", "tinygo")
   160  	if err != nil {
   161  		return err
   162  	}
   163  	if !options.Work {
   164  		defer os.RemoveAll(tmpdir)
   165  	}
   166  
   167  	// Do the build.
   168  	result, err := builder.Build(pkgName, outpath, tmpdir, config)
   169  	if err != nil {
   170  		return err
   171  	}
   172  
   173  	if result.Binary != "" {
   174  		// If result.Binary is set, it means there is a build output (elf, hex,
   175  		// etc) that we need to move to the outpath. If it isn't set, it means
   176  		// the build output was a .ll, .bc or .o file that has already been
   177  		// written to outpath and so we don't need to do anything.
   178  
   179  		if outpath == "" {
   180  			if strings.HasSuffix(pkgName, ".go") {
   181  				// A Go file was specified directly on the command line.
   182  				// Base the binary name off of it.
   183  				outpath = filepath.Base(pkgName[:len(pkgName)-3]) + config.DefaultBinaryExtension()
   184  			} else {
   185  				// Pick a default output path based on the main directory.
   186  				outpath = filepath.Base(result.MainDir) + config.DefaultBinaryExtension()
   187  			}
   188  		}
   189  
   190  		if err := os.Rename(result.Binary, outpath); err != nil {
   191  			// Moving failed. Do a file copy.
   192  			inf, err := os.Open(result.Binary)
   193  			if err != nil {
   194  				return err
   195  			}
   196  			defer inf.Close()
   197  			outf, err := os.OpenFile(outpath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
   198  			if err != nil {
   199  				return err
   200  			}
   201  
   202  			// Copy data to output file.
   203  			_, err = io.Copy(outf, inf)
   204  			if err != nil {
   205  				return err
   206  			}
   207  
   208  			// Check whether file writing was successful.
   209  			return outf.Close()
   210  		}
   211  	}
   212  
   213  	// Move was successful.
   214  	return nil
   215  }
   216  
   217  // Test runs the tests in the given package. Returns whether the test passed and
   218  // possibly an error if the test failed to run.
   219  func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options, outpath string) (bool, error) {
   220  	options.TestConfig.CompileTestBinary = true
   221  	config, err := builder.NewConfig(options)
   222  	if err != nil {
   223  		return false, err
   224  	}
   225  
   226  	testConfig := &options.TestConfig
   227  
   228  	// Pass test flags to the test binary.
   229  	var flags []string
   230  	if testConfig.Verbose {
   231  		flags = append(flags, "-test.v")
   232  	}
   233  	if testConfig.Short {
   234  		flags = append(flags, "-test.short")
   235  	}
   236  	if testConfig.RunRegexp != "" {
   237  		flags = append(flags, "-test.run="+testConfig.RunRegexp)
   238  	}
   239  	if testConfig.SkipRegexp != "" {
   240  		flags = append(flags, "-test.skip="+testConfig.SkipRegexp)
   241  	}
   242  	if testConfig.BenchRegexp != "" {
   243  		flags = append(flags, "-test.bench="+testConfig.BenchRegexp)
   244  	}
   245  	if testConfig.BenchTime != "" {
   246  		flags = append(flags, "-test.benchtime="+testConfig.BenchTime)
   247  	}
   248  	if testConfig.BenchMem {
   249  		flags = append(flags, "-test.benchmem")
   250  	}
   251  	if testConfig.Count != nil && *testConfig.Count != 1 {
   252  		flags = append(flags, "-test.count="+strconv.Itoa(*testConfig.Count))
   253  	}
   254  	if testConfig.Shuffle != "" {
   255  		flags = append(flags, "-test.shuffle="+testConfig.Shuffle)
   256  	}
   257  
   258  	logToStdout := testConfig.Verbose || testConfig.BenchRegexp != ""
   259  
   260  	var buf bytes.Buffer
   261  	var output io.Writer = &buf
   262  	// Send the test output to stdout if -v or -bench
   263  	if logToStdout {
   264  		output = os.Stdout
   265  	}
   266  
   267  	passed := false
   268  	var duration time.Duration
   269  	result, err := buildAndRun(pkgName, config, output, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
   270  		if testConfig.CompileOnly || outpath != "" {
   271  			// Write test binary to the specified file name.
   272  			if outpath == "" {
   273  				// No -o path was given, so create one now.
   274  				// This matches the behavior of go test.
   275  				outpath = filepath.Base(result.MainDir) + ".test"
   276  			}
   277  			copyFile(result.Binary, outpath)
   278  		}
   279  		if testConfig.CompileOnly {
   280  			// Do not run the test.
   281  			passed = true
   282  			return nil
   283  		}
   284  
   285  		// Tests are always run in the package directory.
   286  		cmd.Dir = result.MainDir
   287  
   288  		// wasmtime is the default emulator used for `-target=wasip1`. wasmtime
   289  		// is a WebAssembly runtime CLI with WASI enabled by default. However,
   290  		// only stdio are allowed by default. For example, while STDOUT routes
   291  		// to the host, other files don't. It also does not inherit environment
   292  		// variables from the host. Some tests read testdata files, often from
   293  		// outside the package directory. Other tests require temporary
   294  		// writeable directories. We allow this by adding wasmtime flags below.
   295  		if config.EmulatorName() == "wasmtime" {
   296  			// At this point, The current working directory is at the package
   297  			// directory. Ex. $GOROOT/src/compress/flate for compress/flate.
   298  			// buildAndRun has already added arguments for wasmtime, that allow
   299  			// read-access to files such as "testdata/huffman-zero.in".
   300  			//
   301  			// Ex. main(.wasm) --dir=. -- -test.v
   302  
   303  			// Below adds additional wasmtime flags in case a test reads files
   304  			// outside its directory, like "../testdata/e.txt". This allows any
   305  			// relative directory up to the module root, even if the test never
   306  			// reads any files.
   307  			//
   308  			// Ex. run --dir=.. --dir=../.. --dir=../../..
   309  			dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot)
   310  			args := []string{"run"}
   311  			for _, d := range dirs[1:] {
   312  				args = append(args, "--dir="+d)
   313  			}
   314  
   315  			// The below re-organizes the arguments so that the current
   316  			// directory is added last.
   317  			args = append(args, cmd.Args[1:]...)
   318  			cmd.Args = append(cmd.Args[:1:1], args...)
   319  		}
   320  
   321  		// Run the test.
   322  		start := time.Now()
   323  		err = cmd.Run()
   324  		duration = time.Since(start)
   325  		passed = err == nil
   326  
   327  		// if verbose or benchmarks, then output is already going to stdout
   328  		// However, if we failed and weren't printing to stdout, print the output we accumulated.
   329  		if !passed && !logToStdout {
   330  			buf.WriteTo(stdout)
   331  		}
   332  
   333  		if _, ok := err.(*exec.ExitError); ok {
   334  			// Binary exited with a non-zero exit code, which means the test
   335  			// failed. Return nil to avoid printing a useless "exited with
   336  			// error" error message.
   337  			return nil
   338  		}
   339  		return err
   340  	})
   341  	importPath := strings.TrimSuffix(result.ImportPath, ".test")
   342  
   343  	var w io.Writer = stdout
   344  	if logToStdout {
   345  		w = os.Stdout
   346  	}
   347  	if err, ok := err.(loader.NoTestFilesError); ok {
   348  		fmt.Fprintf(w, "?   \t%s\t[no test files]\n", err.ImportPath)
   349  		// Pretend the test passed - it at least didn't fail.
   350  		return true, nil
   351  	} else if passed && !testConfig.CompileOnly {
   352  		fmt.Fprintf(w, "ok  \t%s\t%.3fs\n", importPath, duration.Seconds())
   353  	} else {
   354  		fmt.Fprintf(w, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
   355  	}
   356  	return passed, err
   357  }
   358  
   359  func dirsToModuleRoot(maindir, modroot string) []string {
   360  	var dirs = []string{"."}
   361  	last := ".."
   362  	// strip off path elements until we hit the module root
   363  	// adding `..`, `../..`, `../../..` until we're done
   364  	for maindir != modroot {
   365  		dirs = append(dirs, last)
   366  		last = filepath.Join(last, "..")
   367  		maindir = filepath.Dir(maindir)
   368  	}
   369  	return dirs
   370  }
   371  
   372  // Flash builds and flashes the built binary to the given serial port.
   373  func Flash(pkgName, port string, options *compileopts.Options) error {
   374  	config, err := builder.NewConfig(options)
   375  	if err != nil {
   376  		return err
   377  	}
   378  
   379  	// determine the type of file to compile
   380  	var fileExt string
   381  
   382  	flashMethod, _ := config.Programmer()
   383  	switch flashMethod {
   384  	case "command", "":
   385  		switch {
   386  		case strings.Contains(config.Target.FlashCommand, "{hex}"):
   387  			fileExt = ".hex"
   388  		case strings.Contains(config.Target.FlashCommand, "{elf}"):
   389  			fileExt = ".elf"
   390  		case strings.Contains(config.Target.FlashCommand, "{bin}"):
   391  			fileExt = ".bin"
   392  		case strings.Contains(config.Target.FlashCommand, "{uf2}"):
   393  			fileExt = ".uf2"
   394  		case strings.Contains(config.Target.FlashCommand, "{zip}"):
   395  			fileExt = ".zip"
   396  		default:
   397  			return errors.New("invalid target file - did you forget the {hex} token in the 'flash-command' section?")
   398  		}
   399  	case "msd":
   400  		if config.Target.FlashFilename == "" {
   401  			return errors.New("invalid target file: flash-method was set to \"msd\" but no msd-firmware-name was set")
   402  		}
   403  		fileExt = filepath.Ext(config.Target.FlashFilename)
   404  	case "openocd":
   405  		fileExt = ".hex"
   406  	case "bmp":
   407  		fileExt = ".elf"
   408  	case "native":
   409  		return errors.New("unknown flash method \"native\" - did you miss a -target flag?")
   410  	default:
   411  		return errors.New("unknown flash method: " + flashMethod)
   412  	}
   413  
   414  	// Create a temporary directory for intermediary files.
   415  	tmpdir, err := os.MkdirTemp("", "tinygo")
   416  	if err != nil {
   417  		return err
   418  	}
   419  	if !options.Work {
   420  		defer os.RemoveAll(tmpdir)
   421  	}
   422  
   423  	// Build the binary.
   424  	result, err := builder.Build(pkgName, fileExt, tmpdir, config)
   425  	if err != nil {
   426  		return err
   427  	}
   428  
   429  	// do we need port reset to put MCU into bootloader mode?
   430  	if config.Target.PortReset == "true" && flashMethod != "openocd" {
   431  		port, err := getDefaultPort(port, config.Target.SerialPort)
   432  		if err == nil {
   433  			err = touchSerialPortAt1200bps(port)
   434  			if err != nil {
   435  				return &commandError{"failed to reset port", port, err}
   436  			}
   437  			// give the target MCU a chance to restart into bootloader
   438  			time.Sleep(3 * time.Second)
   439  		}
   440  	}
   441  
   442  	// Flash the binary to the MCU.
   443  	switch flashMethod {
   444  	case "", "command":
   445  		// Create the command.
   446  		flashCmd := config.Target.FlashCommand
   447  		flashCmdList, err := shlex.Split(flashCmd)
   448  		if err != nil {
   449  			return fmt.Errorf("could not parse flash command %#v: %w", flashCmd, err)
   450  		}
   451  
   452  		if strings.Contains(flashCmd, "{port}") {
   453  			var err error
   454  			port, err = getDefaultPort(port, config.Target.SerialPort)
   455  			if err != nil {
   456  				return err
   457  			}
   458  		}
   459  
   460  		// Fill in fields in the command template.
   461  		fileToken := "{" + fileExt[1:] + "}"
   462  		for i, arg := range flashCmdList {
   463  			arg = strings.ReplaceAll(arg, fileToken, result.Binary)
   464  			arg = strings.ReplaceAll(arg, "{port}", port)
   465  			flashCmdList[i] = arg
   466  		}
   467  
   468  		// Execute the command.
   469  		if len(flashCmdList) < 2 {
   470  			return fmt.Errorf("invalid flash command: %#v", flashCmd)
   471  		}
   472  		cmd := executeCommand(config.Options, flashCmdList[0], flashCmdList[1:]...)
   473  		cmd.Stdout = os.Stdout
   474  		cmd.Stderr = os.Stderr
   475  		cmd.Dir = goenv.Get("TINYGOROOT")
   476  		err = cmd.Run()
   477  		if err != nil {
   478  			return &commandError{"failed to flash", result.Binary, err}
   479  		}
   480  	case "msd":
   481  		// this flashing method copies the binary data to a Mass Storage Device (msd)
   482  		switch fileExt {
   483  		case ".uf2":
   484  			err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary, config.Options)
   485  			if err != nil {
   486  				return &commandError{"failed to flash", result.Binary, err}
   487  			}
   488  		case ".hex":
   489  			err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary, config.Options)
   490  			if err != nil {
   491  				return &commandError{"failed to flash", result.Binary, err}
   492  			}
   493  		default:
   494  			return errors.New("mass storage device flashing currently only supports uf2 and hex")
   495  		}
   496  	case "openocd":
   497  		args, err := config.OpenOCDConfiguration()
   498  		if err != nil {
   499  			return err
   500  		}
   501  		exit := " reset exit"
   502  		if config.Target.OpenOCDVerify != nil && *config.Target.OpenOCDVerify {
   503  			exit = " verify" + exit
   504  		}
   505  		args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+exit)
   506  		cmd := executeCommand(config.Options, "openocd", args...)
   507  		cmd.Stdout = os.Stdout
   508  		cmd.Stderr = os.Stderr
   509  		err = cmd.Run()
   510  		if err != nil {
   511  			return &commandError{"failed to flash", result.Binary, err}
   512  		}
   513  	case "bmp":
   514  		gdb, err := config.Target.LookupGDB()
   515  		if err != nil {
   516  			return err
   517  		}
   518  		var bmpGDBPort string
   519  		bmpGDBPort, _, err = getBMPPorts()
   520  		if err != nil {
   521  			return err
   522  		}
   523  		args := []string{"-ex", "target extended-remote " + bmpGDBPort, "-ex", "monitor swdp_scan", "-ex", "attach 1", "-ex", "load", filepath.ToSlash(result.Binary)}
   524  		cmd := executeCommand(config.Options, gdb, args...)
   525  		cmd.Stdout = os.Stdout
   526  		cmd.Stderr = os.Stderr
   527  		err = cmd.Run()
   528  		if err != nil {
   529  			return &commandError{"failed to flash", result.Binary, err}
   530  		}
   531  	default:
   532  		return fmt.Errorf("unknown flash method: %s", flashMethod)
   533  	}
   534  	if options.Monitor {
   535  		return Monitor(result.Executable, "", config)
   536  	}
   537  	return nil
   538  }
   539  
   540  // Debug compiles and flashes a program to a microcontroller (just like Flash)
   541  // but instead of resetting the target, it will drop into a debug shell like GDB
   542  // or LLDB. You can then set breakpoints, run the `continue` command to start,
   543  // hit Ctrl+C to break the running program, etc.
   544  //
   545  // Note: this command is expected to execute just before exiting, as it
   546  // modifies global state.
   547  func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Options) error {
   548  	config, err := builder.NewConfig(options)
   549  	if err != nil {
   550  		return err
   551  	}
   552  	var cmdName string
   553  	switch debugger {
   554  	case "gdb":
   555  		cmdName, err = config.Target.LookupGDB()
   556  	case "lldb":
   557  		cmdName, err = builder.LookupCommand("lldb")
   558  	}
   559  	if err != nil {
   560  		return err
   561  	}
   562  
   563  	// Create a temporary directory for intermediary files.
   564  	tmpdir, err := os.MkdirTemp("", "tinygo")
   565  	if err != nil {
   566  		return err
   567  	}
   568  	if !options.Work {
   569  		defer os.RemoveAll(tmpdir)
   570  	}
   571  
   572  	// Build the binary to debug.
   573  	format, fileExt := config.EmulatorFormat()
   574  	result, err := builder.Build(pkgName, fileExt, tmpdir, config)
   575  	if err != nil {
   576  		return err
   577  	}
   578  
   579  	// Find a good way to run GDB.
   580  	gdbInterface, openocdInterface := config.Programmer()
   581  	switch gdbInterface {
   582  	case "msd", "command", "":
   583  		emulator := config.EmulatorName()
   584  		if emulator != "" {
   585  			if emulator == "mgba" {
   586  				gdbInterface = "mgba"
   587  			} else if emulator == "simavr" {
   588  				gdbInterface = "simavr"
   589  			} else if strings.HasPrefix(emulator, "qemu-system-") {
   590  				gdbInterface = "qemu"
   591  			} else {
   592  				// Assume QEMU as an emulator.
   593  				gdbInterface = "qemu-user"
   594  			}
   595  		} else if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
   596  			gdbInterface = "openocd"
   597  		} else if config.Target.JLinkDevice != "" {
   598  			gdbInterface = "jlink"
   599  		} else {
   600  			gdbInterface = "native"
   601  		}
   602  	}
   603  
   604  	// Run the GDB server, if necessary.
   605  	port := ""
   606  	var gdbCommands []string
   607  	var daemon *exec.Cmd
   608  	emulator, err := config.Emulator(format, result.Binary)
   609  	if err != nil {
   610  		return err
   611  	}
   612  	switch gdbInterface {
   613  	case "native":
   614  		// Run GDB directly.
   615  	case "bmp":
   616  		var bmpGDBPort string
   617  		bmpGDBPort, _, err = getBMPPorts()
   618  		if err != nil {
   619  			return err
   620  		}
   621  		port = bmpGDBPort
   622  		gdbCommands = append(gdbCommands, "monitor swdp_scan", "compare-sections", "attach 1", "load")
   623  	case "openocd":
   624  		port = ":3333"
   625  		gdbCommands = append(gdbCommands, "monitor halt", "load", "monitor reset halt")
   626  
   627  		// We need a separate debugging daemon for on-chip debugging.
   628  		args, err := config.OpenOCDConfiguration()
   629  		if err != nil {
   630  			return err
   631  		}
   632  		daemon = executeCommand(config.Options, "openocd", args...)
   633  		if ocdOutput {
   634  			// Make it clear which output is from the daemon.
   635  			w := &ColorWriter{
   636  				Out:    colorable.NewColorableStderr(),
   637  				Prefix: "openocd: ",
   638  				Color:  TermColorYellow,
   639  			}
   640  			daemon.Stdout = w
   641  			daemon.Stderr = w
   642  		}
   643  	case "jlink":
   644  		port = ":2331"
   645  		gdbCommands = append(gdbCommands, "load", "monitor reset halt")
   646  
   647  		// We need a separate debugging daemon for on-chip debugging.
   648  		daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice)
   649  		if ocdOutput {
   650  			// Make it clear which output is from the daemon.
   651  			w := &ColorWriter{
   652  				Out:    colorable.NewColorableStderr(),
   653  				Prefix: "jlink: ",
   654  				Color:  TermColorYellow,
   655  			}
   656  			daemon.Stdout = w
   657  			daemon.Stderr = w
   658  		}
   659  	case "qemu":
   660  		port = ":1234"
   661  		// Run in an emulator.
   662  		args := append(emulator[1:], "-s", "-S")
   663  		daemon = executeCommand(config.Options, emulator[0], args...)
   664  		daemon.Stdout = os.Stdout
   665  		daemon.Stderr = os.Stderr
   666  	case "qemu-user":
   667  		port = ":1234"
   668  		// Run in an emulator.
   669  		args := append([]string{"-g", "1234"}, emulator[1:]...)
   670  		daemon = executeCommand(config.Options, emulator[0], args...)
   671  		daemon.Stdout = os.Stdout
   672  		daemon.Stderr = os.Stderr
   673  	case "mgba":
   674  		port = ":2345"
   675  		// Run in an emulator.
   676  		args := append(emulator[1:], "-g")
   677  		daemon = executeCommand(config.Options, emulator[0], args...)
   678  		daemon.Stdout = os.Stdout
   679  		daemon.Stderr = os.Stderr
   680  	case "simavr":
   681  		port = ":1234"
   682  		// Run in an emulator.
   683  		args := append(emulator[1:], "-g")
   684  		daemon = executeCommand(config.Options, emulator[0], args...)
   685  		daemon.Stdout = os.Stdout
   686  		daemon.Stderr = os.Stderr
   687  	case "msd":
   688  		return errors.New("gdb is not supported for drag-and-drop programmable devices")
   689  	default:
   690  		return fmt.Errorf("gdb is not supported with interface %#v", gdbInterface)
   691  	}
   692  
   693  	if daemon != nil {
   694  		// Make sure the daemon doesn't receive Ctrl-C that is intended for
   695  		// GDB (to break the currently executing program).
   696  		setCommandAsDaemon(daemon)
   697  
   698  		// Start now, and kill it on exit.
   699  		err = daemon.Start()
   700  		if err != nil {
   701  			return &commandError{"failed to run", daemon.Path, err}
   702  		}
   703  		defer func() {
   704  			daemon.Process.Signal(os.Interrupt)
   705  			var stopped uint32
   706  			go func() {
   707  				time.Sleep(time.Millisecond * 100)
   708  				if atomic.LoadUint32(&stopped) == 0 {
   709  					daemon.Process.Kill()
   710  				}
   711  			}()
   712  			daemon.Wait()
   713  			atomic.StoreUint32(&stopped, 1)
   714  		}()
   715  	}
   716  
   717  	// Ignore Ctrl-C, it must be passed on to GDB.
   718  	c := make(chan os.Signal, 1)
   719  	signal.Notify(c, os.Interrupt)
   720  	go func() {
   721  		for range c {
   722  		}
   723  	}()
   724  
   725  	// Construct and execute a gdb or lldb command.
   726  	// By default: gdb -ex run <binary>
   727  	// Exit the debugger with Ctrl-D.
   728  	params := []string{result.Executable}
   729  	switch debugger {
   730  	case "gdb":
   731  		if port != "" {
   732  			params = append(params, "-ex", "target extended-remote "+port)
   733  		}
   734  		for _, cmd := range gdbCommands {
   735  			params = append(params, "-ex", cmd)
   736  		}
   737  	case "lldb":
   738  		params = append(params, "--arch", config.Triple())
   739  		if port != "" {
   740  			if strings.HasPrefix(port, ":") {
   741  				params = append(params, "-o", "gdb-remote "+port[1:])
   742  			} else {
   743  				return fmt.Errorf("cannot use LLDB over a gdb-remote that isn't a TCP port: %s", port)
   744  			}
   745  		}
   746  		for _, cmd := range gdbCommands {
   747  			if strings.HasPrefix(cmd, "monitor ") {
   748  				params = append(params, "-o", "process plugin packet "+cmd)
   749  			} else if cmd == "load" {
   750  				params = append(params, "-o", "target modules load --load --slide 0")
   751  			} else {
   752  				return fmt.Errorf("don't know how to convert GDB command %#v to LLDB", cmd)
   753  			}
   754  		}
   755  	}
   756  	cmd := executeCommand(config.Options, cmdName, params...)
   757  	cmd.Stdin = os.Stdin
   758  	cmd.Stdout = os.Stdout
   759  	cmd.Stderr = os.Stderr
   760  	err = cmd.Run()
   761  	if err != nil {
   762  		return &commandError{"failed to run " + cmdName + " with", result.Executable, err}
   763  	}
   764  	return nil
   765  }
   766  
   767  // Run compiles and runs the given program. Depending on the target provided in
   768  // the options, it will run the program directly on the host or will run it in
   769  // an emulator. For example, -target=wasm will cause the binary to be run inside
   770  // of a WebAssembly VM.
   771  func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
   772  	config, err := builder.NewConfig(options)
   773  	if err != nil {
   774  		return err
   775  	}
   776  
   777  	_, err = buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
   778  		return cmd.Run()
   779  	})
   780  	return err
   781  }
   782  
   783  // buildAndRun builds and runs the given program, writing output to stdout and
   784  // errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and
   785  // passes command line arguments and evironment variables in a way appropriate
   786  // for the given emulator.
   787  func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) (builder.BuildResult, error) {
   788  	// Determine whether we're on a system that supports environment variables
   789  	// and command line parameters (operating systems, WASI) or not (baremetal,
   790  	// WebAssembly in the browser). If we're on a system without an environment,
   791  	// we need to pass command line arguments and environment variables through
   792  	// global variables (built into the binary directly) instead of the
   793  	// conventional way.
   794  	needsEnvInVars := config.GOOS() == "js"
   795  	for _, tag := range config.BuildTags() {
   796  		if tag == "baremetal" {
   797  			needsEnvInVars = true
   798  		}
   799  	}
   800  	var args, emuArgs, env []string
   801  	var extraCmdEnv []string
   802  	if needsEnvInVars {
   803  		runtimeGlobals := make(map[string]string)
   804  		if len(cmdArgs) != 0 {
   805  			runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
   806  		}
   807  		if len(environmentVars) != 0 {
   808  			runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
   809  		}
   810  		if len(runtimeGlobals) != 0 {
   811  			// This sets the global variables like they would be set with
   812  			// `-ldflags="-X=runtime.osArgs=first\x00second`.
   813  			// The runtime package has two variables (osArgs and osEnv) that are
   814  			// both strings, from which the parameters and environment variables
   815  			// are read.
   816  			config.Options.GlobalValues = map[string]map[string]string{
   817  				"runtime": runtimeGlobals,
   818  			}
   819  		}
   820  	} else if config.EmulatorName() == "wasmtime" {
   821  		// Wasmtime needs some special flags to pass environment variables
   822  		// and allow reading from the current directory.
   823  		emuArgs = append(emuArgs, "--dir=.")
   824  		for _, v := range environmentVars {
   825  			emuArgs = append(emuArgs, "--env", v)
   826  		}
   827  		if len(cmdArgs) != 0 {
   828  			// Use of '--' argument no longer necessary as of Wasmtime v14:
   829  			// https://github.com/bytecodealliance/wasmtime/pull/6946
   830  			// args = append(args, "--")
   831  			args = append(args, cmdArgs...)
   832  		}
   833  
   834  		// Set this for nicer backtraces during tests, but don't override the user.
   835  		if _, ok := os.LookupEnv("WASMTIME_BACKTRACE_DETAILS"); !ok {
   836  			extraCmdEnv = append(extraCmdEnv, "WASMTIME_BACKTRACE_DETAILS=1")
   837  		}
   838  	} else {
   839  		// Pass environment variables and command line parameters as usual.
   840  		// This also works on qemu-aarch64 etc.
   841  		args = cmdArgs
   842  		env = environmentVars
   843  	}
   844  
   845  	// Create a temporary directory for intermediary files.
   846  	tmpdir, err := os.MkdirTemp("", "tinygo")
   847  	if err != nil {
   848  		return builder.BuildResult{}, err
   849  	}
   850  	if !config.Options.Work {
   851  		defer os.RemoveAll(tmpdir)
   852  	}
   853  
   854  	// Build the binary to be run.
   855  	format, fileExt := config.EmulatorFormat()
   856  	result, err := builder.Build(pkgName, fileExt, tmpdir, config)
   857  	if err != nil {
   858  		return result, err
   859  	}
   860  
   861  	// If needed, set a timeout on the command. This is done in tests so
   862  	// they don't waste resources on a stalled test.
   863  	var ctx context.Context
   864  	if timeout != 0 {
   865  		var cancel context.CancelFunc
   866  		ctx, cancel = context.WithTimeout(context.Background(), timeout)
   867  		defer cancel()
   868  	}
   869  
   870  	// Set up the command.
   871  	var name string
   872  	if config.Target.Emulator == "" {
   873  		name = result.Binary
   874  	} else {
   875  		emulator, err := config.Emulator(format, result.Binary)
   876  		if err != nil {
   877  			return result, err
   878  		}
   879  		name = emulator[0]
   880  		emuArgs = append(emuArgs, emulator[1:]...)
   881  		args = append(emuArgs, args...)
   882  	}
   883  	var cmd *exec.Cmd
   884  	if ctx != nil {
   885  		cmd = exec.CommandContext(ctx, name, args...)
   886  	} else {
   887  		cmd = exec.Command(name, args...)
   888  	}
   889  	cmd.Env = append(cmd.Env, env...)
   890  	cmd.Env = append(cmd.Env, extraCmdEnv...)
   891  
   892  	// Configure stdout/stderr. The stdout may go to a buffer, not a real
   893  	// stdout.
   894  	cmd.Stdout = stdout
   895  	cmd.Stderr = os.Stderr
   896  	if config.EmulatorName() == "simavr" {
   897  		cmd.Stdout = nil // don't print initial load commands
   898  		cmd.Stderr = stdout
   899  	}
   900  
   901  	// If this is a test, reserve CPU time for it so that increased
   902  	// parallelism doesn't blow up memory usage. If this isn't a test but
   903  	// simply `tinygo run`, then it is practically a no-op.
   904  	config.Options.Semaphore <- struct{}{}
   905  	defer func() {
   906  		<-config.Options.Semaphore
   907  	}()
   908  
   909  	// Run binary.
   910  	if config.Options.PrintCommands != nil {
   911  		config.Options.PrintCommands(cmd.Path, cmd.Args...)
   912  	}
   913  	err = run(cmd, result)
   914  	if err != nil {
   915  		if ctx != nil && ctx.Err() == context.DeadlineExceeded {
   916  			stdout.Write([]byte(fmt.Sprintf("--- timeout of %s exceeded, terminating...\n", timeout)))
   917  			err = ctx.Err()
   918  		}
   919  		return result, &commandError{"failed to run compiled binary", result.Binary, err}
   920  	}
   921  	return result, nil
   922  }
   923  
   924  func touchSerialPortAt1200bps(port string) (err error) {
   925  	retryCount := 3
   926  	for i := 0; i < retryCount; i++ {
   927  		// Open port
   928  		p, e := serial.Open(port, &serial.Mode{BaudRate: 1200})
   929  		if e != nil {
   930  			if runtime.GOOS == `windows` {
   931  				se, ok := e.(*serial.PortError)
   932  				if ok && se.Code() == serial.InvalidSerialPort {
   933  					// InvalidSerialPort error occurs when transitioning to boot
   934  					return nil
   935  				}
   936  			}
   937  			time.Sleep(1 * time.Second)
   938  			err = e
   939  			continue
   940  		}
   941  		defer p.Close()
   942  
   943  		p.SetDTR(false)
   944  		return nil
   945  	}
   946  	return fmt.Errorf("opening port: %s", err)
   947  }
   948  
   949  func flashUF2UsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
   950  	for start := time.Now(); time.Since(start) < options.Timeout; {
   951  		// Find a UF2 mount point.
   952  		mounts, err := findFATMounts(options)
   953  		if err != nil {
   954  			return err
   955  		}
   956  		for _, mount := range mounts {
   957  			for _, volume := range volumes {
   958  				if mount.name != volume {
   959  					continue
   960  				}
   961  				if _, err := os.Stat(filepath.Join(mount.path, "INFO_UF2.TXT")); err != nil {
   962  					// No INFO_UF2.TXT found, which is expected on a UF2
   963  					// filesystem.
   964  					continue
   965  				}
   966  				// Found the filesystem, so flash the device!
   967  				return moveFile(tmppath, filepath.Join(mount.path, "flash.uf2"))
   968  			}
   969  		}
   970  		time.Sleep(500 * time.Millisecond)
   971  	}
   972  	return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
   973  }
   974  
   975  func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
   976  	for start := time.Now(); time.Since(start) < options.Timeout; {
   977  		// Find all mount points.
   978  		mounts, err := findFATMounts(options)
   979  		if err != nil {
   980  			return err
   981  		}
   982  		for _, mount := range mounts {
   983  			for _, volume := range volumes {
   984  				if mount.name != volume {
   985  					continue
   986  				}
   987  				// Found the filesystem, so flash the device!
   988  				return moveFile(tmppath, filepath.Join(mount.path, "flash.hex"))
   989  			}
   990  		}
   991  		time.Sleep(500 * time.Millisecond)
   992  	}
   993  	return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
   994  }
   995  
   996  type mountPoint struct {
   997  	name string
   998  	path string
   999  }
  1000  
  1001  // Find all the mount points on the system that use the FAT filesystem.
  1002  func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
  1003  	var points []mountPoint
  1004  	switch runtime.GOOS {
  1005  	case "darwin":
  1006  		list, err := os.ReadDir("/Volumes")
  1007  		if err != nil {
  1008  			return nil, fmt.Errorf("could not list mount points: %w", err)
  1009  		}
  1010  		for _, elem := range list {
  1011  			// TODO: find a way to check for the filesystem type.
  1012  			// (Only return FAT filesystems).
  1013  			points = append(points, mountPoint{
  1014  				name: elem.Name(),
  1015  				path: filepath.Join("/Volumes", elem.Name()),
  1016  			})
  1017  		}
  1018  		sort.Slice(points, func(i, j int) bool {
  1019  			return points[i].path < points[j].name
  1020  		})
  1021  		return points, nil
  1022  	case "linux":
  1023  		tab, err := os.ReadFile("/proc/mounts") // symlink to /proc/self/mounts on my system
  1024  		if err != nil {
  1025  			return nil, fmt.Errorf("could not list mount points: %w", err)
  1026  		}
  1027  		for _, line := range strings.Split(string(tab), "\n") {
  1028  			fields := strings.Fields(line)
  1029  			if len(fields) <= 2 {
  1030  				continue
  1031  			}
  1032  			fstype := fields[2]
  1033  			if fstype != "vfat" {
  1034  				continue
  1035  			}
  1036  			points = append(points, mountPoint{
  1037  				name: filepath.Base(fields[1]),
  1038  				path: fields[1],
  1039  			})
  1040  		}
  1041  		return points, nil
  1042  	case "windows":
  1043  		// Obtain a list of all currently mounted volumes.
  1044  		cmd := executeCommand(options, "wmic",
  1045  			"PATH", "Win32_LogicalDisk",
  1046  			"get", "DeviceID,VolumeName,FileSystem,DriveType")
  1047  		var out bytes.Buffer
  1048  		cmd.Stdout = &out
  1049  		err := cmd.Run()
  1050  		if err != nil {
  1051  			return nil, fmt.Errorf("could not list mount points: %w", err)
  1052  		}
  1053  
  1054  		// Extract data to convert to a []mountPoint slice.
  1055  		for _, line := range strings.Split(out.String(), "\n") {
  1056  			words := strings.Fields(line)
  1057  			if len(words) < 3 {
  1058  				continue
  1059  			}
  1060  			if words[1] != "2" || words[2] != "FAT" {
  1061  				// - DriveType 2 is removable (which we're looking for).
  1062  				// - We only want to return FAT filesystems.
  1063  				continue
  1064  			}
  1065  			points = append(points, mountPoint{
  1066  				name: words[3],
  1067  				path: words[0],
  1068  			})
  1069  		}
  1070  		return points, nil
  1071  	default:
  1072  		return nil, fmt.Errorf("unknown GOOS for listing mount points: %s", runtime.GOOS)
  1073  	}
  1074  }
  1075  
  1076  // getDefaultPort returns the default serial port depending on the operating system.
  1077  func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err error) {
  1078  	portCandidates := strings.FieldsFunc(portFlag, func(c rune) bool { return c == ',' })
  1079  	if len(portCandidates) == 1 {
  1080  		return portCandidates[0], nil
  1081  	}
  1082  
  1083  	var ports []string
  1084  	switch runtime.GOOS {
  1085  	case "freebsd":
  1086  		ports, err = filepath.Glob("/dev/cuaU*")
  1087  	case "darwin", "linux", "windows":
  1088  		var portsList []*enumerator.PortDetails
  1089  		portsList, err = enumerator.GetDetailedPortsList()
  1090  		if err != nil {
  1091  			return "", err
  1092  		}
  1093  
  1094  		var preferredPortIDs [][2]uint16
  1095  		for _, s := range usbInterfaces {
  1096  			parts := strings.Split(s, ":")
  1097  			if len(parts) != 2 {
  1098  				return "", fmt.Errorf("could not parse USB VID/PID pair %q", s)
  1099  			}
  1100  			vid, err := strconv.ParseUint(parts[0], 16, 16)
  1101  			if err != nil {
  1102  				return "", fmt.Errorf("could not parse USB vendor ID %q: %w", parts[1], err)
  1103  			}
  1104  			pid, err := strconv.ParseUint(parts[1], 16, 16)
  1105  			if err != nil {
  1106  				return "", fmt.Errorf("could not parse USB product ID %q: %w", parts[1], err)
  1107  			}
  1108  			preferredPortIDs = append(preferredPortIDs, [2]uint16{uint16(vid), uint16(pid)})
  1109  		}
  1110  
  1111  		var primaryPorts []string   // ports picked from preferred USB VID/PID
  1112  		var secondaryPorts []string // other ports (as a fallback)
  1113  		for _, p := range portsList {
  1114  			if !p.IsUSB {
  1115  				continue
  1116  			}
  1117  			if p.VID != "" && p.PID != "" {
  1118  				foundPort := false
  1119  				vid, vidErr := strconv.ParseUint(p.VID, 16, 16)
  1120  				pid, pidErr := strconv.ParseUint(p.PID, 16, 16)
  1121  				if vidErr == nil && pidErr == nil {
  1122  					for _, id := range preferredPortIDs {
  1123  						if uint16(vid) == id[0] && uint16(pid) == id[1] {
  1124  							primaryPorts = append(primaryPorts, p.Name)
  1125  							foundPort = true
  1126  							continue
  1127  						}
  1128  					}
  1129  				}
  1130  				if foundPort {
  1131  					continue
  1132  				}
  1133  			}
  1134  
  1135  			secondaryPorts = append(secondaryPorts, p.Name)
  1136  		}
  1137  		if len(primaryPorts) == 1 {
  1138  			// There is exactly one match in the set of preferred ports. Use
  1139  			// this port, even if there may be others available. This allows
  1140  			// flashing a specific board even if there are multiple available.
  1141  			return primaryPorts[0], nil
  1142  		} else if len(primaryPorts) > 1 {
  1143  			// There are multiple preferred ports, probably because more than
  1144  			// one device of the same type are connected (e.g. two Arduino
  1145  			// Unos).
  1146  			ports = primaryPorts
  1147  		} else {
  1148  			// No preferred ports found. Fall back to other serial ports
  1149  			// available in the system.
  1150  			ports = secondaryPorts
  1151  		}
  1152  	default:
  1153  		return "", errors.New("unable to search for a default USB device to be flashed on this OS")
  1154  	}
  1155  
  1156  	if err != nil {
  1157  		return "", err
  1158  	} else if ports == nil {
  1159  		return "", errors.New("unable to locate a serial port")
  1160  	} else if len(ports) == 0 {
  1161  		return "", errors.New("no serial ports available")
  1162  	}
  1163  
  1164  	if len(portCandidates) == 0 {
  1165  		if len(usbInterfaces) > 0 {
  1166  			return "", errors.New("unable to search for a default USB device - use -port flag, available ports are " + strings.Join(ports, ", "))
  1167  		} else if len(ports) == 1 {
  1168  			return ports[0], nil
  1169  		} else {
  1170  			return "", errors.New("multiple serial ports available - use -port flag, available ports are " + strings.Join(ports, ", "))
  1171  		}
  1172  	}
  1173  
  1174  	for _, ps := range portCandidates {
  1175  		for _, p := range ports {
  1176  			if p == ps {
  1177  				return p, nil
  1178  			}
  1179  		}
  1180  	}
  1181  
  1182  	return "", errors.New("port you specified '" + strings.Join(portCandidates, ",") + "' does not exist, available ports are " + strings.Join(ports, ", "))
  1183  }
  1184  
  1185  // getBMPPorts returns BlackMagicProbe's serial ports if any
  1186  func getBMPPorts() (gdbPort, uartPort string, err error) {
  1187  	var portsList []*enumerator.PortDetails
  1188  	portsList, err = enumerator.GetDetailedPortsList()
  1189  	if err != nil {
  1190  		return "", "", err
  1191  	}
  1192  	var ports []string
  1193  	for _, p := range portsList {
  1194  		if !p.IsUSB {
  1195  			continue
  1196  		}
  1197  		if p.VID != "" && p.PID != "" {
  1198  			vid, vidErr := strconv.ParseUint(p.VID, 16, 16)
  1199  			pid, pidErr := strconv.ParseUint(p.PID, 16, 16)
  1200  			if vidErr == nil && pidErr == nil && vid == 0x1d50 && pid == 0x6018 {
  1201  				ports = append(ports, p.Name)
  1202  			}
  1203  		}
  1204  	}
  1205  	if len(ports) == 2 {
  1206  		return ports[0], ports[1], nil
  1207  	} else if len(ports) == 0 {
  1208  		return "", "", errors.New("no BMP detected")
  1209  	} else {
  1210  		return "", "", fmt.Errorf("expected 2 BMP serial ports, found %d - did you perhaps connect more than one BMP?", len(ports))
  1211  	}
  1212  }
  1213  
  1214  func usage(command string) {
  1215  	switch command {
  1216  	default:
  1217  		fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
  1218  		fmt.Fprintln(os.Stderr, "version:", goenv.Version())
  1219  		fmt.Fprintf(os.Stderr, "usage: %s <command> [arguments]\n", os.Args[0])
  1220  		fmt.Fprintln(os.Stderr, "\ncommands:")
  1221  		fmt.Fprintln(os.Stderr, "  build:   compile packages and dependencies")
  1222  		fmt.Fprintln(os.Stderr, "  run:     compile and run immediately")
  1223  		fmt.Fprintln(os.Stderr, "  test:    test packages")
  1224  		fmt.Fprintln(os.Stderr, "  flash:   compile and flash to the device")
  1225  		fmt.Fprintln(os.Stderr, "  gdb:     run/flash and immediately enter GDB")
  1226  		fmt.Fprintln(os.Stderr, "  lldb:    run/flash and immediately enter LLDB")
  1227  		fmt.Fprintln(os.Stderr, "  monitor: open communication port")
  1228  		fmt.Fprintln(os.Stderr, "  ports:   list available serial ports")
  1229  		fmt.Fprintln(os.Stderr, "  env:     list environment variables used during build")
  1230  		fmt.Fprintln(os.Stderr, "  list:    run go list using the TinyGo root")
  1231  		fmt.Fprintln(os.Stderr, "  clean:   empty cache directory ("+goenv.Get("GOCACHE")+")")
  1232  		fmt.Fprintln(os.Stderr, "  targets: list targets")
  1233  		fmt.Fprintln(os.Stderr, "  info:    show info for specified target")
  1234  		fmt.Fprintln(os.Stderr, "  version: show version")
  1235  		fmt.Fprintln(os.Stderr, "  help:    print this help text")
  1236  
  1237  		if flag.Parsed() {
  1238  			fmt.Fprintln(os.Stderr, "\nflags:")
  1239  			flag.PrintDefaults()
  1240  		}
  1241  
  1242  		fmt.Fprintln(os.Stderr, "\nfor more details, see https://tinygo.org/docs/reference/usage/")
  1243  	}
  1244  }
  1245  
  1246  // try to make the path relative to the current working directory. If any error
  1247  // occurs, this error is ignored and the absolute path is returned instead.
  1248  func tryToMakePathRelative(dir string) string {
  1249  	wd, err := os.Getwd()
  1250  	if err != nil {
  1251  		return dir
  1252  	}
  1253  	relpath, err := filepath.Rel(wd, dir)
  1254  	if err != nil {
  1255  		return dir
  1256  	}
  1257  	return relpath
  1258  }
  1259  
  1260  // printCompilerError prints compiler errors using the provided logger function
  1261  // (similar to fmt.Println).
  1262  //
  1263  // There is one exception: interp errors may print to stderr unconditionally due
  1264  // to limitations in the LLVM bindings.
  1265  func printCompilerError(logln func(...interface{}), err error) {
  1266  	switch err := err.(type) {
  1267  	case types.Error:
  1268  		printCompilerError(logln, scanner.Error{
  1269  			Pos: err.Fset.Position(err.Pos),
  1270  			Msg: err.Msg,
  1271  		})
  1272  	case scanner.Error:
  1273  		if !strings.HasPrefix(err.Pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) && !strings.HasPrefix(err.Pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
  1274  			// This file is not from the standard library (either the GOROOT or
  1275  			// the TINYGOROOT). Make the path relative, for easier reading.
  1276  			// Ignore any errors in the process (falling back to the absolute
  1277  			// path).
  1278  			err.Pos.Filename = tryToMakePathRelative(err.Pos.Filename)
  1279  		}
  1280  		logln(err)
  1281  	case scanner.ErrorList:
  1282  		for _, scannerErr := range err {
  1283  			printCompilerError(logln, *scannerErr)
  1284  		}
  1285  	case *interp.Error:
  1286  		logln("#", err.ImportPath)
  1287  		logln(err.Error())
  1288  		if len(err.Inst) != 0 {
  1289  			logln(err.Inst)
  1290  		}
  1291  		if len(err.Traceback) > 0 {
  1292  			logln("\ntraceback:")
  1293  			for _, line := range err.Traceback {
  1294  				logln(line.Pos.String() + ":")
  1295  				logln(line.Inst)
  1296  			}
  1297  		}
  1298  	case loader.Errors:
  1299  		logln("#", err.Pkg.ImportPath)
  1300  		for _, err := range err.Errs {
  1301  			printCompilerError(logln, err)
  1302  		}
  1303  	case loader.Error:
  1304  		logln(err.Err.Error())
  1305  		logln("package", err.ImportStack[0])
  1306  		for _, pkgPath := range err.ImportStack[1:] {
  1307  			logln("\timports", pkgPath)
  1308  		}
  1309  	case *builder.MultiError:
  1310  		for _, err := range err.Errs {
  1311  			printCompilerError(logln, err)
  1312  		}
  1313  	default:
  1314  		logln("error:", err)
  1315  	}
  1316  }
  1317  
  1318  func handleCompilerError(err error) {
  1319  	if err != nil {
  1320  		printCompilerError(func(args ...interface{}) {
  1321  			fmt.Fprintln(os.Stderr, args...)
  1322  		}, err)
  1323  		os.Exit(1)
  1324  	}
  1325  }
  1326  
  1327  // This is a special type for the -X flag to parse the pkgpath.Var=stringVal
  1328  // format. It has to be a special type to allow multiple variables to be defined
  1329  // this way.
  1330  type globalValuesFlag map[string]map[string]string
  1331  
  1332  func (m globalValuesFlag) String() string {
  1333  	return "pkgpath.Var=value"
  1334  }
  1335  
  1336  func (m globalValuesFlag) Set(value string) error {
  1337  	equalsIndex := strings.IndexByte(value, '=')
  1338  	if equalsIndex < 0 {
  1339  		return errors.New("expected format pkgpath.Var=value")
  1340  	}
  1341  	pathAndName := value[:equalsIndex]
  1342  	pointIndex := strings.LastIndexByte(pathAndName, '.')
  1343  	if pointIndex < 0 {
  1344  		return errors.New("expected format pkgpath.Var=value")
  1345  	}
  1346  	path := pathAndName[:pointIndex]
  1347  	name := pathAndName[pointIndex+1:]
  1348  	stringValue := value[equalsIndex+1:]
  1349  	if m[path] == nil {
  1350  		m[path] = make(map[string]string)
  1351  	}
  1352  	m[path][name] = stringValue
  1353  	return nil
  1354  }
  1355  
  1356  // parseGoLinkFlag parses the -ldflags parameter. Its primary purpose right now
  1357  // is the -X flag, for setting the value of global string variables.
  1358  func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
  1359  	set := flag.NewFlagSet("link", flag.ExitOnError)
  1360  	globalVarValues := make(globalValuesFlag)
  1361  	set.Var(globalVarValues, "X", "Set the value of the string variable to the given value.")
  1362  	flags, err := shlex.Split(flagsString)
  1363  	if err != nil {
  1364  		return nil, err
  1365  	}
  1366  	err = set.Parse(flags)
  1367  	if err != nil {
  1368  		return nil, err
  1369  	}
  1370  	return map[string]map[string]string(globalVarValues), nil
  1371  }
  1372  
  1373  // getListOfPackages returns a standard list of packages for a given list that might
  1374  // include wildards using `go list`.
  1375  // For example [./...] => ["pkg1", "pkg1/pkg12", "pkg2"]
  1376  func getListOfPackages(pkgs []string, options *compileopts.Options) ([]string, error) {
  1377  	config, err := builder.NewConfig(options)
  1378  	if err != nil {
  1379  		return nil, err
  1380  	}
  1381  	cmd, err := loader.List(config, nil, pkgs)
  1382  	if err != nil {
  1383  		return nil, fmt.Errorf("failed to run `go list`: %w", err)
  1384  	}
  1385  	outputBuf := bytes.NewBuffer(nil)
  1386  	cmd.Stdout = outputBuf
  1387  	cmd.Stderr = os.Stderr
  1388  	err = cmd.Run()
  1389  	if err != nil {
  1390  		return nil, err
  1391  	}
  1392  
  1393  	var pkgNames []string
  1394  	sc := bufio.NewScanner(outputBuf)
  1395  	for sc.Scan() {
  1396  		pkgNames = append(pkgNames, sc.Text())
  1397  	}
  1398  
  1399  	return pkgNames, nil
  1400  }
  1401  
  1402  func main() {
  1403  	if len(os.Args) < 2 {
  1404  		fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
  1405  		usage("")
  1406  		os.Exit(1)
  1407  	}
  1408  	command := os.Args[1]
  1409  
  1410  	opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
  1411  	gc := flag.String("gc", "", "garbage collector to use (none, leaking, conservative)")
  1412  	panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
  1413  	scheduler := flag.String("scheduler", "", "which scheduler to use (none, tasks, asyncify)")
  1414  	serial := flag.String("serial", "", "which serial output to use (none, uart, usb)")
  1415  	work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
  1416  	interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
  1417  	var tags buildutil.TagsFlag
  1418  	flag.Var(&tags, "tags", "a space-separated list of extra build tags")
  1419  	target := flag.String("target", "", "chip/board name or JSON target specification file")
  1420  	var stackSize uint64
  1421  	flag.Func("stack-size", "goroutine stack size (if unknown at compile time)", func(s string) error {
  1422  		size, err := bytesize.Parse(s)
  1423  		stackSize = uint64(size)
  1424  		return err
  1425  	})
  1426  	printSize := flag.String("size", "", "print sizes (none, short, full)")
  1427  	printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
  1428  	printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
  1429  	printCommands := flag.Bool("x", false, "Print commands")
  1430  	parallelism := flag.Int("p", runtime.GOMAXPROCS(0), "the number of build jobs that can run in parallel")
  1431  	nodebug := flag.Bool("no-debug", false, "strip debug information")
  1432  	ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
  1433  	ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
  1434  	port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
  1435  	timeout := flag.Duration("timeout", 20*time.Second, "the length of time to retry locating the MSD volume to be used for flashing")
  1436  	programmer := flag.String("programmer", "", "which hardware programmer to use")
  1437  	ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
  1438  	llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
  1439  	cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
  1440  	monitor := flag.Bool("monitor", false, "enable serial monitor")
  1441  	baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor")
  1442  
  1443  	// Internal flags, that are only intended for TinyGo development.
  1444  	printIR := flag.Bool("internal-printir", false, "print LLVM IR")
  1445  	dumpSSA := flag.Bool("internal-dumpssa", false, "dump internal Go SSA")
  1446  	verifyIR := flag.Bool("internal-verifyir", false, "run extra verification steps on LLVM IR")
  1447  	// Don't generate debug information in the IR, to make IR more readable.
  1448  	// You generally want debug information in IR for various features, like
  1449  	// stack size calculation and features like -size=short, -print-allocs=,
  1450  	// etc. The -no-debug flag is used to strip it at link time. But for TinyGo
  1451  	// development it can be useful to not emit debug information at all.
  1452  	skipDwarf := flag.Bool("internal-nodwarf", false, "internal flag, use -no-debug instead")
  1453  
  1454  	var flagJSON, flagDeps, flagTest bool
  1455  	if command == "help" || command == "list" || command == "info" || command == "build" {
  1456  		flag.BoolVar(&flagJSON, "json", false, "print data in JSON format")
  1457  	}
  1458  	if command == "help" || command == "list" {
  1459  		flag.BoolVar(&flagDeps, "deps", false, "supply -deps flag to go list")
  1460  		flag.BoolVar(&flagTest, "test", false, "supply -test flag to go list")
  1461  	}
  1462  	var outpath string
  1463  	if command == "help" || command == "build" || command == "build-library" || command == "test" {
  1464  		flag.StringVar(&outpath, "o", "", "output filename")
  1465  	}
  1466  
  1467  	var testConfig compileopts.TestConfig
  1468  	if command == "help" || command == "test" {
  1469  		flag.BoolVar(&testConfig.CompileOnly, "c", false, "compile the test binary but do not run it")
  1470  		flag.BoolVar(&testConfig.Verbose, "v", false, "verbose: print additional output")
  1471  		flag.BoolVar(&testConfig.Short, "short", false, "short: run smaller test suite to save time")
  1472  		flag.StringVar(&testConfig.RunRegexp, "run", "", "run: regexp of tests to run")
  1473  		flag.StringVar(&testConfig.SkipRegexp, "skip", "", "skip: regexp of tests to skip")
  1474  		testConfig.Count = flag.Int("count", 1, "count: number of times to run tests/benchmarks `count` times")
  1475  		flag.StringVar(&testConfig.BenchRegexp, "bench", "", "bench: regexp of benchmarks to run")
  1476  		flag.StringVar(&testConfig.BenchTime, "benchtime", "", "run each benchmark for duration `d`")
  1477  		flag.BoolVar(&testConfig.BenchMem, "benchmem", false, "show memory stats for benchmarks")
  1478  		flag.StringVar(&testConfig.Shuffle, "shuffle", "", "shuffle the order the tests and benchmarks run")
  1479  	}
  1480  
  1481  	// Early command processing, before commands are interpreted by the Go flag
  1482  	// library.
  1483  	switch command {
  1484  	case "clang", "ld.lld", "wasm-ld":
  1485  		err := builder.RunTool(command, os.Args[2:]...)
  1486  		if err != nil {
  1487  			fmt.Fprintln(os.Stderr, err)
  1488  			os.Exit(1)
  1489  		}
  1490  		os.Exit(0)
  1491  	}
  1492  
  1493  	flag.CommandLine.Parse(os.Args[2:])
  1494  	globalVarValues, err := parseGoLinkFlag(*ldflags)
  1495  	if err != nil {
  1496  		fmt.Fprintln(os.Stderr, err)
  1497  		os.Exit(1)
  1498  	}
  1499  
  1500  	var printAllocs *regexp.Regexp
  1501  	if *printAllocsString != "" {
  1502  		printAllocs, err = regexp.Compile(*printAllocsString)
  1503  		if err != nil {
  1504  			fmt.Fprintln(os.Stderr, err)
  1505  			os.Exit(1)
  1506  		}
  1507  	}
  1508  
  1509  	var ocdCommands []string
  1510  	if *ocdCommandsString != "" {
  1511  		ocdCommands = strings.Split(*ocdCommandsString, ",")
  1512  	}
  1513  
  1514  	options := &compileopts.Options{
  1515  		GOOS:            goenv.Get("GOOS"),
  1516  		GOARCH:          goenv.Get("GOARCH"),
  1517  		GOARM:           goenv.Get("GOARM"),
  1518  		Target:          *target,
  1519  		StackSize:       stackSize,
  1520  		Opt:             *opt,
  1521  		GC:              *gc,
  1522  		PanicStrategy:   *panicStrategy,
  1523  		Scheduler:       *scheduler,
  1524  		Serial:          *serial,
  1525  		Work:            *work,
  1526  		InterpTimeout:   *interpTimeout,
  1527  		PrintIR:         *printIR,
  1528  		DumpSSA:         *dumpSSA,
  1529  		VerifyIR:        *verifyIR,
  1530  		SkipDWARF:       *skipDwarf,
  1531  		Semaphore:       make(chan struct{}, *parallelism),
  1532  		Debug:           !*nodebug,
  1533  		PrintSizes:      *printSize,
  1534  		PrintStacks:     *printStacks,
  1535  		PrintAllocs:     printAllocs,
  1536  		Tags:            []string(tags),
  1537  		TestConfig:      testConfig,
  1538  		GlobalValues:    globalVarValues,
  1539  		Programmer:      *programmer,
  1540  		OpenOCDCommands: ocdCommands,
  1541  		LLVMFeatures:    *llvmFeatures,
  1542  		PrintJSON:       flagJSON,
  1543  		Monitor:         *monitor,
  1544  		BaudRate:        *baudrate,
  1545  		Timeout:         *timeout,
  1546  	}
  1547  	if *printCommands {
  1548  		options.PrintCommands = printCommand
  1549  	}
  1550  
  1551  	err = options.Verify()
  1552  	if err != nil {
  1553  		fmt.Fprintln(os.Stderr, err.Error())
  1554  		usage(command)
  1555  		os.Exit(1)
  1556  	}
  1557  
  1558  	if *cpuprofile != "" {
  1559  		f, err := os.Create(*cpuprofile)
  1560  		if err != nil {
  1561  			fmt.Fprintln(os.Stderr, "could not create CPU profile: ", err)
  1562  			os.Exit(1)
  1563  		}
  1564  		defer f.Close()
  1565  		if err := pprof.StartCPUProfile(f); err != nil {
  1566  			fmt.Fprintln(os.Stderr, "could not start CPU profile: ", err)
  1567  			os.Exit(1)
  1568  		}
  1569  		defer pprof.StopCPUProfile()
  1570  	}
  1571  
  1572  	switch command {
  1573  	case "build":
  1574  		pkgName := "."
  1575  		if flag.NArg() == 1 {
  1576  			pkgName = filepath.ToSlash(flag.Arg(0))
  1577  		} else if flag.NArg() > 1 {
  1578  			fmt.Fprintln(os.Stderr, "build only accepts a single positional argument: package name, but multiple were specified")
  1579  			usage(command)
  1580  			os.Exit(1)
  1581  		}
  1582  		if options.Target == "" && filepath.Ext(outpath) == ".wasm" {
  1583  			options.Target = "wasm"
  1584  		}
  1585  
  1586  		err := Build(pkgName, outpath, options)
  1587  		handleCompilerError(err)
  1588  	case "build-library":
  1589  		// Note: this command is only meant to be used while making a release!
  1590  		if outpath == "" {
  1591  			fmt.Fprintln(os.Stderr, "No output filename supplied (-o).")
  1592  			usage(command)
  1593  			os.Exit(1)
  1594  		}
  1595  		if *target == "" {
  1596  			fmt.Fprintln(os.Stderr, "No target (-target).")
  1597  		}
  1598  		if flag.NArg() != 1 {
  1599  			fmt.Fprintf(os.Stderr, "Build-library only accepts exactly one library name as argument, %d given\n", flag.NArg())
  1600  			usage(command)
  1601  			os.Exit(1)
  1602  		}
  1603  		var lib *builder.Library
  1604  		switch name := flag.Arg(0); name {
  1605  		case "compiler-rt":
  1606  			lib = &builder.CompilerRT
  1607  		case "picolibc":
  1608  			lib = &builder.Picolibc
  1609  		default:
  1610  			fmt.Fprintf(os.Stderr, "Unknown library: %s\n", name)
  1611  			os.Exit(1)
  1612  		}
  1613  		tmpdir, err := os.MkdirTemp("", "tinygo*")
  1614  		if err != nil {
  1615  			handleCompilerError(err)
  1616  		}
  1617  		defer os.RemoveAll(tmpdir)
  1618  		spec, err := compileopts.LoadTarget(options)
  1619  		if err != nil {
  1620  			handleCompilerError(err)
  1621  		}
  1622  		config := &compileopts.Config{
  1623  			Options: options,
  1624  			Target:  spec,
  1625  		}
  1626  		path, err := lib.Load(config, tmpdir)
  1627  		handleCompilerError(err)
  1628  		err = copyFile(path, outpath)
  1629  		if err != nil {
  1630  			handleCompilerError(err)
  1631  		}
  1632  	case "flash", "gdb", "lldb":
  1633  		pkgName := filepath.ToSlash(flag.Arg(0))
  1634  		if command == "flash" {
  1635  			err := Flash(pkgName, *port, options)
  1636  			handleCompilerError(err)
  1637  		} else {
  1638  			if !options.Debug {
  1639  				fmt.Fprintln(os.Stderr, "Debug disabled while running debugger?")
  1640  				usage(command)
  1641  				os.Exit(1)
  1642  			}
  1643  			err := Debug(command, pkgName, *ocdOutput, options)
  1644  			handleCompilerError(err)
  1645  		}
  1646  	case "run":
  1647  		if flag.NArg() < 1 {
  1648  			fmt.Fprintln(os.Stderr, "No package specified.")
  1649  			usage(command)
  1650  			os.Exit(1)
  1651  		}
  1652  		pkgName := filepath.ToSlash(flag.Arg(0))
  1653  		err := Run(pkgName, options, flag.Args()[1:])
  1654  		handleCompilerError(err)
  1655  	case "test":
  1656  		var pkgNames []string
  1657  		for i := 0; i < flag.NArg(); i++ {
  1658  			pkgNames = append(pkgNames, filepath.ToSlash(flag.Arg(i)))
  1659  		}
  1660  		if len(pkgNames) == 0 {
  1661  			pkgNames = []string{"."}
  1662  		}
  1663  
  1664  		explicitPkgNames, err := getListOfPackages(pkgNames, options)
  1665  		if err != nil {
  1666  			fmt.Printf("cannot resolve packages: %v\n", err)
  1667  			os.Exit(1)
  1668  		}
  1669  
  1670  		if outpath != "" && len(explicitPkgNames) > 1 {
  1671  			fmt.Println("cannot use -o flag with multiple packages")
  1672  			os.Exit(1)
  1673  		}
  1674  
  1675  		fail := make(chan struct{}, 1)
  1676  		var wg sync.WaitGroup
  1677  		bufs := make([]testOutputBuf, len(explicitPkgNames))
  1678  		for i := range bufs {
  1679  			bufs[i].done = make(chan struct{})
  1680  		}
  1681  
  1682  		wg.Add(1)
  1683  		go func() {
  1684  			defer wg.Done()
  1685  
  1686  			// Flush the output one test at a time.
  1687  			// This ensures that outputs from different tests are not mixed together.
  1688  			for i := range bufs {
  1689  				err := bufs[i].flush(os.Stdout, os.Stderr)
  1690  				if err != nil {
  1691  					// There was an error writing to stdout or stderr, so we probbably cannot print this.
  1692  					select {
  1693  					case fail <- struct{}{}:
  1694  					default:
  1695  					}
  1696  				}
  1697  			}
  1698  		}()
  1699  
  1700  		// Build and run the tests concurrently.
  1701  		// This uses an additional semaphore to reduce the memory usage.
  1702  		testSema := make(chan struct{}, cap(options.Semaphore))
  1703  		for i, pkgName := range explicitPkgNames {
  1704  			pkgName := pkgName
  1705  			buf := &bufs[i]
  1706  			testSema <- struct{}{}
  1707  			wg.Add(1)
  1708  			go func() {
  1709  				defer wg.Done()
  1710  				defer func() { <-testSema }()
  1711  				defer close(buf.done)
  1712  				stdout := (*testStdout)(buf)
  1713  				stderr := (*testStderr)(buf)
  1714  				passed, err := Test(pkgName, stdout, stderr, options, outpath)
  1715  				if err != nil {
  1716  					printCompilerError(func(args ...interface{}) {
  1717  						fmt.Fprintln(stderr, args...)
  1718  					}, err)
  1719  				}
  1720  				if !passed {
  1721  					select {
  1722  					case fail <- struct{}{}:
  1723  					default:
  1724  					}
  1725  				}
  1726  			}()
  1727  		}
  1728  
  1729  		// Wait for all tests to finish.
  1730  		wg.Wait()
  1731  		close(fail)
  1732  		if _, fail := <-fail; fail {
  1733  			os.Exit(1)
  1734  		}
  1735  	case "monitor":
  1736  		config, err := builder.NewConfig(options)
  1737  		handleCompilerError(err)
  1738  		err = Monitor("", *port, config)
  1739  		handleCompilerError(err)
  1740  	case "ports":
  1741  		serialPortInfo, err := ListSerialPorts()
  1742  		handleCompilerError(err)
  1743  		if len(serialPortInfo) == 0 {
  1744  			fmt.Println("No serial ports found.")
  1745  		}
  1746  		fmt.Printf("%-20s %-9s %s\n", "Port", "ID", "Boards")
  1747  		for _, s := range serialPortInfo {
  1748  			fmt.Printf("%-20s %4s:%4s %s\n", s.Name, s.VID, s.PID, s.Target)
  1749  		}
  1750  	case "targets":
  1751  		specs, err := compileopts.GetTargetSpecs()
  1752  		if err != nil {
  1753  			fmt.Fprintln(os.Stderr, "could not list targets:", err)
  1754  			os.Exit(1)
  1755  			return
  1756  		}
  1757  		names := []string{}
  1758  		for key := range specs {
  1759  			names = append(names, key)
  1760  		}
  1761  		sort.Strings(names)
  1762  		for _, name := range names {
  1763  			fmt.Println(name)
  1764  		}
  1765  	case "info":
  1766  		if flag.NArg() == 1 {
  1767  			options.Target = flag.Arg(0)
  1768  		} else if flag.NArg() > 1 {
  1769  			fmt.Fprintln(os.Stderr, "only one target name is accepted")
  1770  			usage(command)
  1771  			os.Exit(1)
  1772  		}
  1773  		config, err := builder.NewConfig(options)
  1774  		if err != nil {
  1775  			fmt.Fprintln(os.Stderr, err)
  1776  			usage(command)
  1777  			os.Exit(1)
  1778  		}
  1779  		config.GoMinorVersion = 0 // this avoids creating the list of Go1.x build tags.
  1780  		if err != nil {
  1781  			fmt.Fprintln(os.Stderr, err)
  1782  			os.Exit(1)
  1783  		}
  1784  		cachedGOROOT, err := loader.GetCachedGoroot(config)
  1785  		if err != nil {
  1786  			fmt.Fprintln(os.Stderr, err)
  1787  			os.Exit(1)
  1788  		}
  1789  		if flagJSON {
  1790  			json, _ := json.MarshalIndent(struct {
  1791  				Target     *compileopts.TargetSpec `json:"target"`
  1792  				GOROOT     string                  `json:"goroot"`
  1793  				GOOS       string                  `json:"goos"`
  1794  				GOARCH     string                  `json:"goarch"`
  1795  				GOARM      string                  `json:"goarm"`
  1796  				BuildTags  []string                `json:"build_tags"`
  1797  				GC         string                  `json:"garbage_collector"`
  1798  				Scheduler  string                  `json:"scheduler"`
  1799  				LLVMTriple string                  `json:"llvm_triple"`
  1800  			}{
  1801  				Target:     config.Target,
  1802  				GOROOT:     cachedGOROOT,
  1803  				GOOS:       config.GOOS(),
  1804  				GOARCH:     config.GOARCH(),
  1805  				GOARM:      config.GOARM(),
  1806  				BuildTags:  config.BuildTags(),
  1807  				GC:         config.GC(),
  1808  				Scheduler:  config.Scheduler(),
  1809  				LLVMTriple: config.Triple(),
  1810  			}, "", "  ")
  1811  			fmt.Println(string(json))
  1812  		} else {
  1813  			fmt.Printf("LLVM triple:       %s\n", config.Triple())
  1814  			fmt.Printf("GOOS:              %s\n", config.GOOS())
  1815  			fmt.Printf("GOARCH:            %s\n", config.GOARCH())
  1816  			fmt.Printf("build tags:        %s\n", strings.Join(config.BuildTags(), " "))
  1817  			fmt.Printf("garbage collector: %s\n", config.GC())
  1818  			fmt.Printf("scheduler:         %s\n", config.Scheduler())
  1819  			fmt.Printf("cached GOROOT:     %s\n", cachedGOROOT)
  1820  		}
  1821  	case "list":
  1822  		config, err := builder.NewConfig(options)
  1823  		if err != nil {
  1824  			fmt.Fprintln(os.Stderr, err)
  1825  			usage(command)
  1826  			os.Exit(1)
  1827  		}
  1828  		var extraArgs []string
  1829  		if flagJSON {
  1830  			extraArgs = append(extraArgs, "-json")
  1831  		}
  1832  		if flagDeps {
  1833  			extraArgs = append(extraArgs, "-deps")
  1834  		}
  1835  		if flagTest {
  1836  			extraArgs = append(extraArgs, "-test")
  1837  		}
  1838  		cmd, err := loader.List(config, extraArgs, flag.Args())
  1839  		if err != nil {
  1840  			fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
  1841  			os.Exit(1)
  1842  		}
  1843  		cmd.Stdout = os.Stdout
  1844  		cmd.Stderr = os.Stderr
  1845  		err = cmd.Run()
  1846  		if err != nil {
  1847  			if exitErr, ok := err.(*exec.ExitError); ok {
  1848  				os.Exit(exitErr.ExitCode())
  1849  			}
  1850  			fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
  1851  			os.Exit(1)
  1852  		}
  1853  	case "clean":
  1854  		// remove cache directory
  1855  		err := os.RemoveAll(goenv.Get("GOCACHE"))
  1856  		if err != nil {
  1857  			fmt.Fprintln(os.Stderr, "cannot clean cache:", err)
  1858  			os.Exit(1)
  1859  		}
  1860  	case "help":
  1861  		command := ""
  1862  		if flag.NArg() >= 1 {
  1863  			command = flag.Arg(0)
  1864  		}
  1865  		usage(command)
  1866  	case "version":
  1867  		goversion := "<unknown>"
  1868  		if s, err := goenv.GorootVersionString(); err == nil {
  1869  			goversion = s
  1870  		}
  1871  		fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", goenv.Version(), runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
  1872  	case "env":
  1873  		if flag.NArg() == 0 {
  1874  			// Show all environment variables.
  1875  			for _, key := range goenv.Keys {
  1876  				fmt.Printf("%s=%#v\n", key, goenv.Get(key))
  1877  			}
  1878  		} else {
  1879  			// Show only one (or a few) environment variables.
  1880  			for i := 0; i < flag.NArg(); i++ {
  1881  				fmt.Println(goenv.Get(flag.Arg(i)))
  1882  			}
  1883  		}
  1884  	default:
  1885  		fmt.Fprintln(os.Stderr, "Unknown command:", command)
  1886  		usage("")
  1887  		os.Exit(1)
  1888  	}
  1889  }
  1890  
  1891  // testOutputBuf is used to buffer the output of concurrent tests.
  1892  type testOutputBuf struct {
  1893  	mu             sync.Mutex
  1894  	output         []outputEntry
  1895  	stdout, stderr io.Writer
  1896  	outerr, errerr error
  1897  	done           chan struct{}
  1898  }
  1899  
  1900  // flush the output to stdout and stderr.
  1901  // This waits until done is closed.
  1902  func (b *testOutputBuf) flush(stdout, stderr io.Writer) error {
  1903  	b.mu.Lock()
  1904  
  1905  	var err error
  1906  	b.stdout = stdout
  1907  	b.stderr = stderr
  1908  	for _, e := range b.output {
  1909  		var w io.Writer
  1910  		var errDst *error
  1911  		if e.stderr {
  1912  			w = stderr
  1913  			errDst = &b.errerr
  1914  		} else {
  1915  			w = stdout
  1916  			errDst = &b.outerr
  1917  		}
  1918  		if *errDst != nil {
  1919  			continue
  1920  		}
  1921  
  1922  		_, werr := w.Write(e.data)
  1923  		if werr != nil {
  1924  			if err == nil {
  1925  				err = werr
  1926  			}
  1927  			*errDst = err
  1928  		}
  1929  	}
  1930  
  1931  	b.mu.Unlock()
  1932  
  1933  	<-b.done
  1934  
  1935  	return err
  1936  }
  1937  
  1938  // testStdout writes stdout from a test to the output buffer.
  1939  type testStdout testOutputBuf
  1940  
  1941  func (out *testStdout) Write(data []byte) (int, error) {
  1942  	buf := (*testOutputBuf)(out)
  1943  	buf.mu.Lock()
  1944  
  1945  	if buf.stdout != nil {
  1946  		// Write the output directly.
  1947  		err := out.outerr
  1948  		buf.mu.Unlock()
  1949  		if err != nil {
  1950  			return 0, err
  1951  		}
  1952  		return buf.stdout.Write(data)
  1953  	}
  1954  
  1955  	defer buf.mu.Unlock()
  1956  
  1957  	// Append the output.
  1958  	if len(buf.output) == 0 || buf.output[len(buf.output)-1].stderr {
  1959  		buf.output = append(buf.output, outputEntry{
  1960  			stderr: false,
  1961  		})
  1962  	}
  1963  	last := &buf.output[len(buf.output)-1]
  1964  	last.data = append(last.data, data...)
  1965  
  1966  	return len(data), nil
  1967  }
  1968  
  1969  // testStderr writes stderr from a test to the output buffer.
  1970  type testStderr testOutputBuf
  1971  
  1972  func (out *testStderr) Write(data []byte) (int, error) {
  1973  	buf := (*testOutputBuf)(out)
  1974  	buf.mu.Lock()
  1975  
  1976  	if buf.stderr != nil {
  1977  		// Write the output directly.
  1978  		err := out.errerr
  1979  		buf.mu.Unlock()
  1980  		if err != nil {
  1981  			return 0, err
  1982  		}
  1983  		return buf.stderr.Write(data)
  1984  	}
  1985  
  1986  	defer buf.mu.Unlock()
  1987  
  1988  	// Append the output.
  1989  	if len(buf.output) == 0 || !buf.output[len(buf.output)-1].stderr {
  1990  		buf.output = append(buf.output, outputEntry{
  1991  			stderr: true,
  1992  		})
  1993  	}
  1994  	last := &buf.output[len(buf.output)-1]
  1995  	last.data = append(last.data, data...)
  1996  
  1997  	return len(data), nil
  1998  }
  1999  
  2000  type outputEntry struct {
  2001  	stderr bool
  2002  	data   []byte
  2003  }