all: make emulator command a string instead of a []string

This matches the flash-command and is generally a bit easier to work
with.
This commit also prepares for allowing multiple formats to be used in
the emulator command, which is necessary for the esp32.
This commit is contained in:
Ayke van Laethem
2022-04-22 17:45:19 +02:00
committed by Ron Evans
parent 4fe3a379a5
commit bd56636d58
15 changed files with 82 additions and 74 deletions
+32 -6
View File
@@ -10,6 +10,7 @@ import (
"regexp"
"strings"
"github.com/google/shlex"
"github.com/tinygo-org/tinygo/goenv"
)
@@ -494,13 +495,38 @@ func (c *Config) WasmAbi() string {
return c.Target.WasmAbi
}
// Emulator returns the emulator target config
func (c *Config) Emulator() []string {
var emulator []string
for _, s := range c.Target.Emulator {
emulator = append(emulator, strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")))
// EmulatorName is a shorthand to get the command for this emulator, something
// like qemu-system-arm or simavr.
func (c *Config) EmulatorName() string {
parts := strings.SplitN(c.Target.Emulator, " ", 2)
if len(parts) > 1 {
return parts[0]
}
return emulator
return ""
}
// EmulatorFormat returns the binary format for the emulator and the associated
// file extension. An empty string means to pass directly whatever the linker
// produces directly without conversion.
func (c *Config) EmulatorFormat() (format, fileExt string) {
return "", ""
}
// Emulator returns a ready-to-run command to run the given binary in an
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
// the compiled binary.
func (c *Config) Emulator(format, binary string) ([]string, error) {
parts, err := shlex.Split(c.Target.Emulator)
if err != nil {
return nil, fmt.Errorf("could not parse emulator command: %w", err)
}
var emulator []string
for _, s := range parts {
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT"))
s = strings.ReplaceAll(s, "{"+format+"}", binary)
emulator = append(emulator, s)
}
return emulator, nil
}
type TestConfig struct {
+9 -20
View File
@@ -44,8 +44,8 @@ type TargetSpec struct {
LDFlags []string `json:"ldflags"`
LinkerScript string `json:"linkerscript"`
ExtraFiles []string `json:"extra-files"`
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
Emulator string `json:"emulator"`
FlashCommand string `json:"flash-command"`
GDB []string `json:"gdb"`
PortReset string `json:"flash-1200-bps-reset"`
@@ -90,19 +90,8 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
if !src.IsNil() {
dst.Set(src)
}
case reflect.Slice: // for slices...
if src.Len() > 0 { // ... if not empty ...
switch tag := field.Tag.Get("override"); tag {
case "copy":
// copy the field of child to spec
dst.Set(src)
case "append", "":
// or append the field of child to spec
dst.Set(reflect.AppendSlice(dst, src))
default:
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
}
}
case reflect.Slice: // for slices, append the field
dst.Set(reflect.AppendSlice(dst, src))
default:
panic("unknown field type : " + kind.String())
}
@@ -335,20 +324,20 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
case "386":
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case.
if runtime.GOARCH != "amd64" {
spec.Emulator = []string{"qemu-i386"}
spec.Emulator = "qemu-i386 {}"
}
case "amd64":
spec.Emulator = []string{"qemu-x86_64"}
spec.Emulator = "qemu-x86_64 {}"
case "arm":
spec.Emulator = []string{"qemu-arm"}
spec.Emulator = "qemu-arm {}"
case "arm64":
spec.Emulator = []string{"qemu-aarch64"}
spec.Emulator = "qemu-aarch64 {}"
}
}
}
if goos != runtime.GOOS {
if goos == "windows" {
spec.Emulator = []string{"wine"}
spec.Emulator = "wine {}"
}
}
return &spec, nil
-5
View File
@@ -29,7 +29,6 @@ func TestOverrideProperties(t *testing.T) {
CPU: "baseCpu",
CFlags: []string{"-base-foo", "-base-bar"},
BuildTags: []string{"bt1", "bt2"},
Emulator: []string{"be1", "be2"},
DefaultStackSize: 42,
AutoStackSize: &baseAutoStackSize,
}
@@ -38,7 +37,6 @@ func TestOverrideProperties(t *testing.T) {
GOOS: "",
CPU: "chlidCpu",
CFlags: []string{"-child-foo", "-child-bar"},
Emulator: []string{"ce1", "ce2"},
AutoStackSize: &childAutoStackSize,
DefaultStackSize: 64,
}
@@ -57,9 +55,6 @@ func TestOverrideProperties(t *testing.T) {
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
t.Errorf("Overriding failed : got %v", base.BuildTags)
}
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
t.Errorf("Overriding failed : got %v", base.Emulator)
}
if *base.AutoStackSize != false {
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
}
+27 -26
View File
@@ -239,8 +239,7 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
cmd.Dir = result.MainDir
// Wasmtime needs a few extra flags to work.
emulator := config.Emulator()
if len(emulator) != 0 && emulator[0] == "wasmtime" {
if config.EmulatorName() == "wasmtime" {
// Add directories to the module root, but skip the current working
// directory which is already added by buildAndRun.
dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot)
@@ -484,18 +483,19 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
return err
}
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
format, fileExt := config.EmulatorFormat()
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
// Find a good way to run GDB.
gdbInterface, openocdInterface := config.Programmer()
switch gdbInterface {
case "msd", "command", "":
emulator := config.Emulator()
if len(emulator) != 0 {
if emulator[0] == "mgba" {
emulator := config.EmulatorName()
if emulator != "" {
if emulator == "mgba" {
gdbInterface = "mgba"
} else if emulator[0] == "simavr" {
} else if emulator == "simavr" {
gdbInterface = "simavr"
} else if strings.HasPrefix(emulator[0], "qemu-system-") {
} else if strings.HasPrefix(emulator, "qemu-system-") {
gdbInterface = "qemu"
} else {
// Assume QEMU as an emulator.
@@ -514,6 +514,10 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
port := ""
var gdbCommands []string
var daemon *exec.Cmd
emulator, err := config.Emulator(format, result.Binary)
if err != nil {
return err
}
switch gdbInterface {
case "native":
// Run GDB directly.
@@ -563,33 +567,29 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
}
case "qemu":
port = ":1234"
emulator := config.Emulator()
// Run in an emulator.
args := append(emulator[1:], result.Binary, "-s", "-S")
args := append(emulator[1:], "-s", "-S")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "qemu-user":
port = ":1234"
emulator := config.Emulator()
// Run in an emulator.
args := append(emulator[1:], "-g", "1234", result.Binary)
args := append(emulator[1:], "-g", "1234")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "mgba":
port = ":2345"
emulator := config.Emulator()
// Run in an emulator.
args := append(emulator[1:], result.Binary, "-g")
args := append(emulator[1:], "-g")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "simavr":
port = ":1234"
emulator := config.Emulator()
// Run in an emulator.
args := append(emulator[1:], "-g", result.Binary)
args := append(emulator[1:], "-g")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
@@ -666,7 +666,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
err = cmd.Run()
if err != nil {
return &commandError{"failed to run " + cmdName + " with", result.Binary, err}
}
@@ -694,9 +694,6 @@ func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
// passes command line arguments and evironment variables in a way appropriate
// for the given emulator.
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) error {
// make sure any special vars in the emulator definition are rewritten
emulator := config.Emulator()
// Determine whether we're on a system that supports environment variables
// and command line parameters (operating systems, WASI) or not (baremetal,
// WebAssembly in the browser). If we're on a system without an environment,
@@ -728,7 +725,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
"runtime": runtimeGlobals,
}
}
} else if len(emulator) != 0 && emulator[0] == "wasmtime" {
} else if config.EmulatorName() == "wasmtime" {
// Wasmtime needs some special flags to pass environment variables
// and allow reading from the current directory.
args = append(args, "--dir=.")
@@ -747,7 +744,8 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
env = environmentVars
}
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
format, fileExt := config.EmulatorFormat()
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
// If needed, set a timeout on the command. This is done in tests so
// they don't waste resources on a stalled test.
var ctx context.Context
@@ -759,12 +757,15 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
// Set up the command.
var name string
if len(emulator) == 0 {
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:]...)
emuArgs = append(emuArgs, result.Binary)
args = append(emuArgs, args...)
}
var cmd *exec.Cmd
@@ -779,7 +780,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
// stdout.
cmd.Stdout = stdout
cmd.Stderr = os.Stderr
if len(emulator) != 0 && emulator[0] == "simavr" {
if config.EmulatorName() == "simavr" {
cmd.Stdout = nil // don't print initial load commands
cmd.Stderr = stdout
}
@@ -1572,7 +1573,7 @@ func main() {
os.Exit(1)
return
}
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == nil {
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
// This doesn't look like a regular target file, but rather like
// a parent target (such as targets/cortex-m.json).
continue
+4 -7
View File
@@ -223,7 +223,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
runTest(name, options, t, nil, nil)
})
}
if len(spec.Emulator) == 0 || spec.Emulator[0] != "simavr" {
if !strings.HasPrefix(spec.Emulator, "simavr ") {
t.Run("env.go", func(t *testing.T) {
t.Parallel()
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
@@ -257,8 +257,8 @@ func emuCheck(t *testing.T, options compileopts.Options) {
if err != nil {
t.Fatal("failed to load target spec:", err)
}
if len(spec.Emulator) != 0 {
_, err := exec.LookPath(spec.Emulator[0])
if spec.Emulator != "" {
_, err := exec.LookPath(strings.SplitN(spec.Emulator, " ", 2)[0])
if err != nil {
if errors.Is(err, exec.ErrNotFound) {
t.Skipf("emulator not installed: %q", spec.Emulator[0])
@@ -327,9 +327,6 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
t.Fatal(err)
}
// make sure any special vars in the emulator definition are rewritten
emulator := config.Emulator()
// Build the test binary.
stdout := &bytes.Buffer{}
err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
@@ -345,7 +342,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
if len(emulator) != 0 && emulator[0] == "simavr" {
if config.EmulatorName() == "simavr" {
// Strip simavr log formatting.
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
+1 -1
View File
@@ -6,5 +6,5 @@
"-Wl,--defsym=_stack_size=512"
],
"flash-command": "avrdude -c arduino -p atmega328p -b 57600 -P {port} -U flash:w:{hex}:i",
"emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"]
"emulator": "simavr -m atmega328p -f 16000000 {}"
}
+1 -1
View File
@@ -7,5 +7,5 @@
],
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}:i",
"serial-port": ["acm:2341:0043", "acm:2341:0001", "acm:2a03:0043", "acm:2341:0243"],
"emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"]
"emulator": "simavr -m atmega328p -f 16000000 {}"
}
+1 -1
View File
@@ -13,5 +13,5 @@
"targets/avr.S",
"src/device/avr/atmega1284p.s"
],
"emulator": ["simavr", "-m", "atmega1284p", "-f", "20000000"]
"emulator": "simavr -m atmega1284p -f 20000000 {}"
}
+1 -1
View File
@@ -5,5 +5,5 @@
"extra-files": [
"targets/cortex-m-qemu.s"
],
"emulator": ["qemu-system-arm", "-machine", "lm3s6965evb", "-semihosting", "-nographic", "-kernel"]
"emulator": "qemu-system-arm -machine lm3s6965evb -semihosting -nographic -kernel {}"
}
+1 -1
View File
@@ -6,5 +6,5 @@
"-Wl,--defsym=_stack_size=128"
],
"flash-command": "micronucleus --run {hex}",
"emulator": ["simavr", "-m", "attiny85", "-f", "16000000"]
"emulator": "simavr -m attiny85 -f 16000000 {}"
}
+1 -1
View File
@@ -24,5 +24,5 @@
"src/runtime/gc_arm.S"
],
"gdb": ["gdb-multiarch"],
"emulator": ["mgba", "-3"]
"emulator": "mgba -3 {}"
}
+1 -1
View File
@@ -4,5 +4,5 @@
"serial": "uart",
"default-stack-size": 4096,
"linkerscript": "targets/hifive1-qemu.ld",
"emulator": ["qemu-system-riscv32", "-machine", "sifive_e", "-nographic", "-kernel"]
"emulator": "qemu-system-riscv32 -machine sifive_e -nographic -kernel {}"
}
+1 -1
View File
@@ -4,5 +4,5 @@
"build-tags": ["virt", "qemu"],
"default-stack-size": 4096,
"linkerscript": "targets/riscv-qemu.ld",
"emulator": ["qemu-system-riscv32", "-machine", "virt", "-nographic", "-bios", "none", "-kernel"]
"emulator": "qemu-system-riscv32 -machine virt -nographic -bios none -kernel {}"
}
+1 -1
View File
@@ -13,6 +13,6 @@
"--stack-first",
"--no-demangle"
],
"emulator": ["wasmtime"],
"emulator": "wasmtime {}",
"wasm-abi": "generic"
}
+1 -1
View File
@@ -13,6 +13,6 @@
"--stack-first",
"--no-demangle"
],
"emulator": ["node", "{root}/targets/wasm_exec.js"],
"emulator": "node {root}/targets/wasm_exec.js {}",
"wasm-abi": "js"
}