Skip to content

Commit bd56636

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent 4fe3a37 commit bd56636

15 files changed

+81
-73
lines changed

compileopts/config.go

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"regexp"
1111
"strings"
1212

13+
"github.com/google/shlex"
1314
"github.com/tinygo-org/tinygo/goenv"
1415
)
1516

@@ -494,13 +495,38 @@ func (c *Config) WasmAbi() string {
494495
return c.Target.WasmAbi
495496
}
496497

497-
// Emulator returns the emulator target config
498-
func (c *Config) Emulator() []string {
498+
// EmulatorName is a shorthand to get the command for this emulator, something
499+
// like qemu-system-arm or simavr.
500+
func (c *Config) EmulatorName() string {
501+
parts := strings.SplitN(c.Target.Emulator, " ", 2)
502+
if len(parts) > 1 {
503+
return parts[0]
504+
}
505+
return ""
506+
}
507+
508+
// EmulatorFormat returns the binary format for the emulator and the associated
509+
// file extension. An empty string means to pass directly whatever the linker
510+
// produces directly without conversion.
511+
func (c *Config) EmulatorFormat() (format, fileExt string) {
512+
return "", ""
513+
}
514+
515+
// Emulator returns a ready-to-run command to run the given binary in an
516+
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
517+
// the compiled binary.
518+
func (c *Config) Emulator(format, binary string) ([]string, error) {
519+
parts, err := shlex.Split(c.Target.Emulator)
520+
if err != nil {
521+
return nil, fmt.Errorf("could not parse emulator command: %w", err)
522+
}
499523
var emulator []string
500-
for _, s := range c.Target.Emulator {
501-
emulator = append(emulator, strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")))
524+
for _, s := range parts {
525+
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT"))
526+
s = strings.ReplaceAll(s, "{"+format+"}", binary)
527+
emulator = append(emulator, s)
502528
}
503-
return emulator
529+
return emulator, nil
504530
}
505531

506532
type TestConfig struct {

compileopts/target.go

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ type TargetSpec struct {
4444
LDFlags []string `json:"ldflags"`
4545
LinkerScript string `json:"linkerscript"`
4646
ExtraFiles []string `json:"extra-files"`
47-
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
48-
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
47+
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
48+
Emulator string `json:"emulator"`
4949
FlashCommand string `json:"flash-command"`
5050
GDB []string `json:"gdb"`
5151
PortReset string `json:"flash-1200-bps-reset"`
@@ -90,19 +90,8 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
9090
if !src.IsNil() {
9191
dst.Set(src)
9292
}
93-
case reflect.Slice: // for slices...
94-
if src.Len() > 0 { // ... if not empty ...
95-
switch tag := field.Tag.Get("override"); tag {
96-
case "copy":
97-
// copy the field of child to spec
98-
dst.Set(src)
99-
case "append", "":
100-
// or append the field of child to spec
101-
dst.Set(reflect.AppendSlice(dst, src))
102-
default:
103-
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
104-
}
105-
}
93+
case reflect.Slice: // for slices, append the field
94+
dst.Set(reflect.AppendSlice(dst, src))
10695
default:
10796
panic("unknown field type : " + kind.String())
10897
}
@@ -335,20 +324,20 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
335324
case "386":
336325
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case.
337326
if runtime.GOARCH != "amd64" {
338-
spec.Emulator = []string{"qemu-i386"}
327+
spec.Emulator = "qemu-i386 {}"
339328
}
340329
case "amd64":
341-
spec.Emulator = []string{"qemu-x86_64"}
330+
spec.Emulator = "qemu-x86_64 {}"
342331
case "arm":
343-
spec.Emulator = []string{"qemu-arm"}
332+
spec.Emulator = "qemu-arm {}"
344333
case "arm64":
345-
spec.Emulator = []string{"qemu-aarch64"}
334+
spec.Emulator = "qemu-aarch64 {}"
346335
}
347336
}
348337
}
349338
if goos != runtime.GOOS {
350339
if goos == "windows" {
351-
spec.Emulator = []string{"wine"}
340+
spec.Emulator = "wine {}"
352341
}
353342
}
354343
return &spec, nil

compileopts/target_test.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ func TestOverrideProperties(t *testing.T) {
2929
CPU: "baseCpu",
3030
CFlags: []string{"-base-foo", "-base-bar"},
3131
BuildTags: []string{"bt1", "bt2"},
32-
Emulator: []string{"be1", "be2"},
3332
DefaultStackSize: 42,
3433
AutoStackSize: &baseAutoStackSize,
3534
}
@@ -38,7 +37,6 @@ func TestOverrideProperties(t *testing.T) {
3837
GOOS: "",
3938
CPU: "chlidCpu",
4039
CFlags: []string{"-child-foo", "-child-bar"},
41-
Emulator: []string{"ce1", "ce2"},
4240
AutoStackSize: &childAutoStackSize,
4341
DefaultStackSize: 64,
4442
}
@@ -57,9 +55,6 @@ func TestOverrideProperties(t *testing.T) {
5755
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
5856
t.Errorf("Overriding failed : got %v", base.BuildTags)
5957
}
60-
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
61-
t.Errorf("Overriding failed : got %v", base.Emulator)
62-
}
6358
if *base.AutoStackSize != false {
6459
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
6560
}

main.go

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,7 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
239239
cmd.Dir = result.MainDir
240240

241241
// Wasmtime needs a few extra flags to work.
242-
emulator := config.Emulator()
243-
if len(emulator) != 0 && emulator[0] == "wasmtime" {
242+
if config.EmulatorName() == "wasmtime" {
244243
// Add directories to the module root, but skip the current working
245244
// directory which is already added by buildAndRun.
246245
dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot)
@@ -484,18 +483,19 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
484483
return err
485484
}
486485

487-
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
486+
format, fileExt := config.EmulatorFormat()
487+
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
488488
// Find a good way to run GDB.
489489
gdbInterface, openocdInterface := config.Programmer()
490490
switch gdbInterface {
491491
case "msd", "command", "":
492-
emulator := config.Emulator()
493-
if len(emulator) != 0 {
494-
if emulator[0] == "mgba" {
492+
emulator := config.EmulatorName()
493+
if emulator != "" {
494+
if emulator == "mgba" {
495495
gdbInterface = "mgba"
496-
} else if emulator[0] == "simavr" {
496+
} else if emulator == "simavr" {
497497
gdbInterface = "simavr"
498-
} else if strings.HasPrefix(emulator[0], "qemu-system-") {
498+
} else if strings.HasPrefix(emulator, "qemu-system-") {
499499
gdbInterface = "qemu"
500500
} else {
501501
// Assume QEMU as an emulator.
@@ -514,6 +514,10 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
514514
port := ""
515515
var gdbCommands []string
516516
var daemon *exec.Cmd
517+
emulator, err := config.Emulator(format, result.Binary)
518+
if err != nil {
519+
return err
520+
}
517521
switch gdbInterface {
518522
case "native":
519523
// Run GDB directly.
@@ -563,33 +567,29 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
563567
}
564568
case "qemu":
565569
port = ":1234"
566-
emulator := config.Emulator()
567570
// Run in an emulator.
568-
args := append(emulator[1:], result.Binary, "-s", "-S")
571+
args := append(emulator[1:], "-s", "-S")
569572
daemon = executeCommand(config.Options, emulator[0], args...)
570573
daemon.Stdout = os.Stdout
571574
daemon.Stderr = os.Stderr
572575
case "qemu-user":
573576
port = ":1234"
574-
emulator := config.Emulator()
575577
// Run in an emulator.
576-
args := append(emulator[1:], "-g", "1234", result.Binary)
578+
args := append(emulator[1:], "-g", "1234")
577579
daemon = executeCommand(config.Options, emulator[0], args...)
578580
daemon.Stdout = os.Stdout
579581
daemon.Stderr = os.Stderr
580582
case "mgba":
581583
port = ":2345"
582-
emulator := config.Emulator()
583584
// Run in an emulator.
584-
args := append(emulator[1:], result.Binary, "-g")
585+
args := append(emulator[1:], "-g")
585586
daemon = executeCommand(config.Options, emulator[0], args...)
586587
daemon.Stdout = os.Stdout
587588
daemon.Stderr = os.Stderr
588589
case "simavr":
589590
port = ":1234"
590-
emulator := config.Emulator()
591591
// Run in an emulator.
592-
args := append(emulator[1:], "-g", result.Binary)
592+
args := append(emulator[1:], "-g")
593593
daemon = executeCommand(config.Options, emulator[0], args...)
594594
daemon.Stdout = os.Stdout
595595
daemon.Stderr = os.Stderr
@@ -666,7 +666,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
666666
cmd.Stdin = os.Stdin
667667
cmd.Stdout = os.Stdout
668668
cmd.Stderr = os.Stderr
669-
err := cmd.Run()
669+
err = cmd.Run()
670670
if err != nil {
671671
return &commandError{"failed to run " + cmdName + " with", result.Binary, err}
672672
}
@@ -694,9 +694,6 @@ func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
694694
// passes command line arguments and evironment variables in a way appropriate
695695
// for the given emulator.
696696
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 {
697-
// make sure any special vars in the emulator definition are rewritten
698-
emulator := config.Emulator()
699-
700697
// Determine whether we're on a system that supports environment variables
701698
// and command line parameters (operating systems, WASI) or not (baremetal,
702699
// 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
728725
"runtime": runtimeGlobals,
729726
}
730727
}
731-
} else if len(emulator) != 0 && emulator[0] == "wasmtime" {
728+
} else if config.EmulatorName() == "wasmtime" {
732729
// Wasmtime needs some special flags to pass environment variables
733730
// and allow reading from the current directory.
734731
args = append(args, "--dir=.")
@@ -747,7 +744,8 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
747744
env = environmentVars
748745
}
749746

750-
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
747+
format, fileExt := config.EmulatorFormat()
748+
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
751749
// If needed, set a timeout on the command. This is done in tests so
752750
// they don't waste resources on a stalled test.
753751
var ctx context.Context
@@ -759,12 +757,15 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
759757

760758
// Set up the command.
761759
var name string
762-
if len(emulator) == 0 {
760+
if config.Target.Emulator == "" {
763761
name = result.Binary
764762
} else {
763+
emulator, err := config.Emulator(format, result.Binary)
764+
if err != nil {
765+
return err
766+
}
765767
name = emulator[0]
766768
emuArgs := append([]string(nil), emulator[1:]...)
767-
emuArgs = append(emuArgs, result.Binary)
768769
args = append(emuArgs, args...)
769770
}
770771
var cmd *exec.Cmd
@@ -779,7 +780,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
779780
// stdout.
780781
cmd.Stdout = stdout
781782
cmd.Stderr = os.Stderr
782-
if len(emulator) != 0 && emulator[0] == "simavr" {
783+
if config.EmulatorName() == "simavr" {
783784
cmd.Stdout = nil // don't print initial load commands
784785
cmd.Stderr = stdout
785786
}
@@ -1572,7 +1573,7 @@ func main() {
15721573
os.Exit(1)
15731574
return
15741575
}
1575-
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == nil {
1576+
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
15761577
// This doesn't look like a regular target file, but rather like
15771578
// a parent target (such as targets/cortex-m.json).
15781579
continue

main_test.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
223223
runTest(name, options, t, nil, nil)
224224
})
225225
}
226-
if len(spec.Emulator) == 0 || spec.Emulator[0] != "simavr" {
226+
if !strings.HasPrefix(spec.Emulator, "simavr ") {
227227
t.Run("env.go", func(t *testing.T) {
228228
t.Parallel()
229229
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) {
257257
if err != nil {
258258
t.Fatal("failed to load target spec:", err)
259259
}
260-
if len(spec.Emulator) != 0 {
261-
_, err := exec.LookPath(spec.Emulator[0])
260+
if spec.Emulator != "" {
261+
_, err := exec.LookPath(strings.SplitN(spec.Emulator, " ", 2)[0])
262262
if err != nil {
263263
if errors.Is(err, exec.ErrNotFound) {
264264
t.Skipf("emulator not installed: %q", spec.Emulator[0])
@@ -327,9 +327,6 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
327327
t.Fatal(err)
328328
}
329329

330-
// make sure any special vars in the emulator definition are rewritten
331-
emulator := config.Emulator()
332-
333330
// Build the test binary.
334331
stdout := &bytes.Buffer{}
335332
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
345342
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
346343
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
347344

348-
if len(emulator) != 0 && emulator[0] == "simavr" {
345+
if config.EmulatorName() == "simavr" {
349346
// Strip simavr log formatting.
350347
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
351348
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)

targets/arduino-nano.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
"-Wl,--defsym=_stack_size=512"
77
],
88
"flash-command": "avrdude -c arduino -p atmega328p -b 57600 -P {port} -U flash:w:{hex}:i",
9-
"emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"]
9+
"emulator": "simavr -m atmega328p -f 16000000 {}"
1010
}

targets/arduino.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
],
88
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}:i",
99
"serial-port": ["acm:2341:0043", "acm:2341:0001", "acm:2a03:0043", "acm:2341:0243"],
10-
"emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"]
10+
"emulator": "simavr -m atmega328p -f 16000000 {}"
1111
}

targets/atmega1284p.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313
"targets/avr.S",
1414
"src/device/avr/atmega1284p.s"
1515
],
16-
"emulator": ["simavr", "-m", "atmega1284p", "-f", "20000000"]
16+
"emulator": "simavr -m atmega1284p -f 20000000 {}"
1717
}

targets/cortex-m-qemu.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
"extra-files": [
66
"targets/cortex-m-qemu.s"
77
],
8-
"emulator": ["qemu-system-arm", "-machine", "lm3s6965evb", "-semihosting", "-nographic", "-kernel"]
8+
"emulator": "qemu-system-arm -machine lm3s6965evb -semihosting -nographic -kernel {}"
99
}

targets/digispark.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
"-Wl,--defsym=_stack_size=128"
77
],
88
"flash-command": "micronucleus --run {hex}",
9-
"emulator": ["simavr", "-m", "attiny85", "-f", "16000000"]
9+
"emulator": "simavr -m attiny85 -f 16000000 {}"
1010
}

0 commit comments

Comments
 (0)