buildAndRun builds and runs the given program, writing output to stdout and errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and passes command line arguments and environment variables in a way appropriate for the given emulator.
(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error)
| 845 | // passes command line arguments and environment variables in a way appropriate |
| 846 | // for the given emulator. |
| 847 | 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) { |
| 848 | // Determine whether we're on a system that supports environment variables |
| 849 | // and command line parameters (operating systems, WASI) or not (baremetal, |
| 850 | // WebAssembly in the browser). If we're on a system without an environment, |
| 851 | // we need to pass command line arguments and environment variables through |
| 852 | // global variables (built into the binary directly) instead of the |
| 853 | // conventional way. |
| 854 | needsEnvInVars := config.GOOS() == "js" |
| 855 | for _, tag := range config.BuildTags() { |
| 856 | if tag == "baremetal" { |
| 857 | needsEnvInVars = true |
| 858 | } |
| 859 | } |
| 860 | var args, env []string |
| 861 | var extraCmdEnv []string |
| 862 | if needsEnvInVars { |
| 863 | runtimeGlobals := make(map[string]string) |
| 864 | if len(cmdArgs) != 0 { |
| 865 | runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00") |
| 866 | } |
| 867 | if len(environmentVars) != 0 { |
| 868 | runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00") |
| 869 | } |
| 870 | if len(runtimeGlobals) != 0 { |
| 871 | // This sets the global variables like they would be set with |
| 872 | // `-ldflags="-X=runtime.osArgs=first\x00second`. |
| 873 | // The runtime package has two variables (osArgs and osEnv) that are |
| 874 | // both strings, from which the parameters and environment variables |
| 875 | // are read. |
| 876 | config.Options.GlobalValues = map[string]map[string]string{ |
| 877 | "runtime": runtimeGlobals, |
| 878 | } |
| 879 | } |
| 880 | } else { |
| 881 | // Pass environment variables and command line parameters as usual. |
| 882 | // This also works on qemu-aarch64 etc. |
| 883 | args = cmdArgs |
| 884 | env = environmentVars |
| 885 | } |
| 886 | |
| 887 | // Create a temporary directory for intermediary files. |
| 888 | tmpdir, err := os.MkdirTemp("", "tinygo") |
| 889 | if err != nil { |
| 890 | return builder.BuildResult{}, err |
| 891 | } |
| 892 | if !config.Options.Work { |
| 893 | defer os.RemoveAll(tmpdir) |
| 894 | } |
| 895 | |
| 896 | // Build the binary to be run. |
| 897 | format, fileExt := config.EmulatorFormat() |
| 898 | result, err := builder.Build(pkgName, fileExt, tmpdir, config) |
| 899 | if err != nil { |
| 900 | return result, err |
| 901 | } |
| 902 | |
| 903 | // If needed, set a timeout on the command. This is done in tests so |
| 904 | // they don't waste resources on a stalled test. |