From 549beeb1013fea760b1f15ccae73cb16766fd7ef Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sat, 6 Sep 2025 18:58:43 +0800 Subject: [PATCH] test output format for all target/emuator/flash-method --- cmd/llgo/xgo_autogen.go | 9 + internal/build/build.go | 3 +- internal/build/outputs.go | 61 +++++- internal/build/outputs_test.go | 301 +++++++++++++++++++++++--- internal/crosscompile/crosscompile.go | 2 + 5 files changed, 335 insertions(+), 41 deletions(-) diff --git a/cmd/llgo/xgo_autogen.go b/cmd/llgo/xgo_autogen.go index 3833800f..1bf5cb7d 100644 --- a/cmd/llgo/xgo_autogen.go +++ b/cmd/llgo/xgo_autogen.go @@ -52,6 +52,7 @@ type Cmd_version struct { xcmd.Command *App } + //line cmd/llgo/main_app.gox:1 func (this *App) MainEntry() { //line cmd/llgo/main_app.gox:1:1 @@ -68,6 +69,7 @@ func (this *App) Main() { _xgo_obj7 := &Cmd_version{App: this} xcmd.Gopt_App_Main(this, _xgo_obj0, _xgo_obj1, _xgo_obj2, _xgo_obj3, _xgo_obj4, _xgo_obj5, _xgo_obj6, _xgo_obj7) } + //line cmd/llgo/build_cmd.gox:20 func (this *Cmd_build) Main(_xgo_arg0 string) { this.Command.Main(_xgo_arg0) @@ -86,6 +88,7 @@ func (this *Cmd_build) Main(_xgo_arg0 string) { func (this *Cmd_build) Classfname() string { return "build" } + //line cmd/llgo/clean_cmd.gox:20 func (this *Cmd_clean) Main(_xgo_arg0 string) { this.Command.Main(_xgo_arg0) @@ -104,6 +107,7 @@ func (this *Cmd_clean) Main(_xgo_arg0 string) { func (this *Cmd_clean) Classfname() string { return "clean" } + //line cmd/llgo/cmptest_cmd.gox:20 func (this *Cmd_cmptest) Main(_xgo_arg0 string) { this.Command.Main(_xgo_arg0) @@ -122,6 +126,7 @@ func (this *Cmd_cmptest) Main(_xgo_arg0 string) { func (this *Cmd_cmptest) Classfname() string { return "cmptest" } + //line cmd/llgo/get_cmd.gox:16 func (this *Cmd_get) Main(_xgo_arg0 string) { this.Command.Main(_xgo_arg0) @@ -138,6 +143,7 @@ func (this *Cmd_get) Main(_xgo_arg0 string) { func (this *Cmd_get) Classfname() string { return "get" } + //line cmd/llgo/install_cmd.gox:20 func (this *Cmd_install) Main(_xgo_arg0 string) { this.Command.Main(_xgo_arg0) @@ -156,6 +162,7 @@ func (this *Cmd_install) Main(_xgo_arg0 string) { func (this *Cmd_install) Classfname() string { return "install" } + //line cmd/llgo/run_cmd.gox:20 func (this *Cmd_run) Main(_xgo_arg0 string) { this.Command.Main(_xgo_arg0) @@ -174,6 +181,7 @@ func (this *Cmd_run) Main(_xgo_arg0 string) { func (this *Cmd_run) Classfname() string { return "run" } + //line cmd/llgo/test_cmd.gox:20 func (this *Cmd_test) Main(_xgo_arg0 string) { this.Command.Main(_xgo_arg0) @@ -192,6 +200,7 @@ func (this *Cmd_test) Main(_xgo_arg0 string) { func (this *Cmd_test) Classfname() string { return "test" } + //line cmd/llgo/version_cmd.gox:22 func (this *Cmd_version) Main(_xgo_arg0 string) { this.Command.Main(_xgo_arg0) diff --git a/internal/build/build.go b/internal/build/build.go index 073ac45a..0d6c99c4 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -315,9 +315,8 @@ func Do(args []string, conf *Config) ([]Package, error) { for _, pkg := range initial { if needLink(pkg, mode) { name := path.Base(pkg.PkgPath) - binFmt := ctx.crossCompile.BinaryFormat - outputCfg, err := genOutputs(conf, name, len(ctx.initial) > 1, ctx.crossCompile.Emulator, binFmt) + outputCfg, err := genOutputs(conf, name, len(ctx.initial) > 1, &ctx.crossCompile) if err != nil { return nil, err } diff --git a/internal/build/outputs.go b/internal/build/outputs.go index 3716ac8b..0eaa2e6f 100644 --- a/internal/build/outputs.go +++ b/internal/build/outputs.go @@ -3,7 +3,9 @@ package build import ( "os" "path/filepath" + "strings" + "github.com/goplus/llgo/internal/crosscompile" "github.com/goplus/llgo/internal/firmware" ) @@ -19,15 +21,16 @@ type OutputCfg struct { } // genOutputs generates appropriate output paths based on the configuration -func genOutputs(conf *Config, pkgName string, multiPkg bool, emulator, binFmt string) (OutputCfg, error) { +func genOutputs(conf *Config, pkgName string, multiPkg bool, crossCompile *crosscompile.Export) (OutputCfg, error) { var cfg OutputCfg // Calculate binary extension and set up format info + binFmt := crossCompile.BinaryFormat binExt := firmware.BinaryExt(binFmt) cfg.BinFmt = binFmt // Determine output format and extension - cfg.FileFmt, cfg.OutExt = determineFormat(conf, emulator) + cfg.FileFmt, cfg.OutExt = determineFormat(conf, crossCompile) // Handle special .img case and set conversion flags cfg.DirectGen = shouldDirectGen(cfg.OutExt, binExt) @@ -52,22 +55,70 @@ func genOutputs(conf *Config, pkgName string, multiPkg bool, emulator, binFmt st } // determineFormat determines the file format and extension -func determineFormat(conf *Config, emulator string) (format, ext string) { +func determineFormat(conf *Config, crossCompile *crosscompile.Export) (format, ext string) { if conf.FileFormat != "" { // User specified file format return conf.FileFormat, firmware.GetFileExtFromFormat(conf.FileFormat) } - if conf.Mode == ModeRun && conf.Emulator && emulator != "" { + if conf.Mode == ModeRun && conf.Emulator && crossCompile.Emulator != "" { // Emulator mode - extract format from emulator command - if emulatorFmt := firmware.ExtractFileFormatFromEmulator(emulator); emulatorFmt != "" { + if emulatorFmt := firmware.ExtractFileFormatFromEmulator(crossCompile.Emulator); emulatorFmt != "" { return emulatorFmt, firmware.GetFileExtFromFormat(emulatorFmt) } } + // Device flashing - determine format based on flash method and target + if conf.Target != "" && (conf.Mode == ModeInstall || conf.Mode == ModeRun || conf.Mode == ModeTest || conf.Mode == ModeCmpTest) { + if flashExt := determineFlashFormat(crossCompile); flashExt != "" { + return flashExt[1:], flashExt // Remove the dot for format, keep for ext + } + } + return "", "" } +// determineFlashFormat determines the required file format for flashing based on flash method +func determineFlashFormat(crossCompile *crosscompile.Export) string { + if crossCompile == nil { + return "" + } + + flashMethod := crossCompile.Flash.Method + switch flashMethod { + case "command", "": + // Extract format from flash command tokens + flashCommand := crossCompile.Flash.Command + switch { + case strings.Contains(flashCommand, "{hex}"): + return ".hex" + case strings.Contains(flashCommand, "{elf}"): + return ".elf" + case strings.Contains(flashCommand, "{bin}"): + return ".bin" + case strings.Contains(flashCommand, "{uf2}"): + return ".uf2" + case strings.Contains(flashCommand, "{zip}"): + return ".zip" + case strings.Contains(flashCommand, "{img}"): + return ".img" + default: + return "" + } + case "msd": + if crossCompile.MSD.FirmwareName == "" { + return "" + } + return filepath.Ext(crossCompile.MSD.FirmwareName) + case "openocd": + return ".hex" + case "bmp": + return ".elf" + default: + return "" + } +} + // shouldDirectGen determines if direct firmware generation is possible func shouldDirectGen(outExt, binExt string) bool { return outExt == "" || outExt == binExt || outExt == ".img" diff --git a/internal/build/outputs_test.go b/internal/build/outputs_test.go index 725c19a5..c37638dd 100644 --- a/internal/build/outputs_test.go +++ b/internal/build/outputs_test.go @@ -5,6 +5,8 @@ package build import ( "testing" + + "github.com/goplus/llgo/internal/crosscompile" ) func TestGenOutputs(t *testing.T) { @@ -13,7 +15,7 @@ func TestGenOutputs(t *testing.T) { conf *Config pkgName string multiPkg bool - emulator string + crossCompile *crosscompile.Export wantOutPath string // use empty string to indicate temp file wantIntPath string // use empty string to indicate same as outPath wantOutExt string @@ -28,7 +30,10 @@ func TestGenOutputs(t *testing.T) { BinPath: "/go/bin", AppExt: "", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "", + }, wantOutPath: "hello", wantOutExt: "", wantFileFmt: "", @@ -42,7 +47,10 @@ func TestGenOutputs(t *testing.T) { OutFile: "myapp", AppExt: "", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "", + }, wantOutPath: "myapp", wantOutExt: "", wantFileFmt: "", @@ -58,7 +66,10 @@ func TestGenOutputs(t *testing.T) { FileFormat: "bin", Target: "esp32", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + }, wantOutPath: "hello.bin", wantOutExt: ".bin", wantFileFmt: "bin", @@ -74,7 +85,10 @@ func TestGenOutputs(t *testing.T) { FileFormat: "hex", Target: "esp32", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + }, wantOutPath: "myapp.hex", wantOutExt: ".hex", wantFileFmt: "hex", @@ -90,7 +104,10 @@ func TestGenOutputs(t *testing.T) { FileFormat: "hex", Target: "esp32", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + }, wantOutPath: "myapp.hex", wantOutExt: ".hex", wantFileFmt: "hex", @@ -103,7 +120,10 @@ func TestGenOutputs(t *testing.T) { Mode: ModeRun, AppExt: "", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "", + }, wantOutPath: "", // temp file wantOutExt: "", wantFileFmt: "", @@ -117,7 +137,10 @@ func TestGenOutputs(t *testing.T) { AppExt: "", Target: "esp32", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + }, wantOutPath: "", // temp file wantOutExt: "", wantFileFmt: "", @@ -132,8 +155,11 @@ func TestGenOutputs(t *testing.T) { Target: "esp32", Emulator: true, }, - pkgName: "hello", - emulator: "qemu-system-xtensa -machine esp32 -drive file={hex},if=mtd,format=raw", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + Emulator: "qemu-system-xtensa -machine esp32 -drive file={hex},if=mtd,format=raw", + }, wantOutPath: "", // temp file wantOutExt: ".hex", wantFileFmt: "hex", @@ -149,7 +175,10 @@ func TestGenOutputs(t *testing.T) { FileFormat: "img", Target: "esp32", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + }, wantOutPath: "hello.img", wantOutExt: ".img", wantFileFmt: "img", @@ -162,7 +191,10 @@ func TestGenOutputs(t *testing.T) { Mode: ModeTest, AppExt: "", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "", + }, wantOutPath: "", // temp file wantOutExt: "", wantFileFmt: "", @@ -176,7 +208,10 @@ func TestGenOutputs(t *testing.T) { AppExt: "", Target: "esp32", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + }, wantOutPath: "", // temp file wantOutExt: "", wantFileFmt: "", @@ -189,7 +224,10 @@ func TestGenOutputs(t *testing.T) { Mode: ModeCmpTest, AppExt: "", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "", + }, wantOutPath: "", // temp file wantOutExt: "", wantFileFmt: "", @@ -203,7 +241,10 @@ func TestGenOutputs(t *testing.T) { BinPath: "/go/bin", AppExt: "", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "", + }, wantOutPath: "/go/bin/hello", wantOutExt: "", wantFileFmt: "", @@ -218,7 +259,10 @@ func TestGenOutputs(t *testing.T) { AppExt: "", Target: "esp32", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + }, wantOutPath: "", // temp file for flashing wantOutExt: "", wantFileFmt: "", @@ -234,13 +278,138 @@ func TestGenOutputs(t *testing.T) { FileFormat: "hex", Target: "esp32", }, - pkgName: "hello", + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + }, wantOutPath: "", // temp file for flashing wantOutExt: ".hex", wantFileFmt: "hex", wantBinFmt: "esp32", wantDirectGen: false, }, + { + name: "run with target non-emulator (should use .bin from binary format)", + conf: &Config{ + Mode: ModeRun, + AppExt: "", + Target: "esp32", + }, + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + }, + wantOutPath: "", // temp file + wantOutExt: "", + wantFileFmt: "", + wantBinFmt: "esp32", + wantDirectGen: true, + }, + { + name: "run with flash method command - extract hex from command", + conf: &Config{ + Mode: ModeRun, + AppExt: "", + Target: "esp32", + }, + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + Flash: crosscompile.Flash{ + Method: "command", + Command: "esptool.py --chip esp32 write_flash 0x10000 {hex}", + }, + }, + wantOutPath: "", // temp file + wantOutExt: ".hex", + wantFileFmt: "hex", + wantBinFmt: "esp32", + wantDirectGen: false, + }, + { + name: "run with flash method command - extract bin from command", + conf: &Config{ + Mode: ModeRun, + AppExt: "", + Target: "esp32", + }, + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "esp32", + Flash: crosscompile.Flash{ + Method: "command", + Command: "esptool.py --chip esp32 write_flash 0x10000 {bin}", + }, + }, + wantOutPath: "", // temp file + wantOutExt: ".bin", + wantFileFmt: "bin", + wantBinFmt: "esp32", + wantDirectGen: true, + }, + { + name: "run with flash method openocd - should use .hex", + conf: &Config{ + Mode: ModeRun, + AppExt: "", + Target: "stm32", + }, + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "arm", + Flash: crosscompile.Flash{ + Method: "openocd", + }, + }, + wantOutPath: "", // temp file + wantOutExt: ".hex", + wantFileFmt: "hex", + wantBinFmt: "arm", + wantDirectGen: false, + }, + { + name: "run with flash method msd - extract extension from firmware name", + conf: &Config{ + Mode: ModeRun, + AppExt: "", + Target: "rp2040", + }, + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "uf2", + Flash: crosscompile.Flash{ + Method: "msd", + }, + MSD: crosscompile.MSD{ + FirmwareName: "firmware.uf2", + }, + }, + wantOutPath: "", // temp file + wantOutExt: ".uf2", + wantFileFmt: "uf2", + wantBinFmt: "uf2", + wantDirectGen: true, + }, + { + name: "run with flash method bmp - should use .elf", + conf: &Config{ + Mode: ModeRun, + AppExt: "", + Target: "stm32", + }, + pkgName: "hello", + crossCompile: &crosscompile.Export{ + BinaryFormat: "arm", + Flash: crosscompile.Flash{ + Method: "bmp", + }, + }, + wantOutPath: "", // temp file + wantOutExt: ".elf", + wantFileFmt: "elf", + wantBinFmt: "arm", + wantDirectGen: false, + }, } for _, tt := range tests { @@ -315,36 +484,100 @@ func TestGenOutputs(t *testing.T) { func TestDetermineFormat(t *testing.T) { tests := []struct { - name string - conf *Config - emulator string - wantFmt string - wantExt string + name string + conf *Config + crossCompile *crosscompile.Export + wantFmt string + wantExt string }{ { - name: "user specified format", - conf: &Config{FileFormat: "hex"}, + name: "user specified format", + conf: &Config{FileFormat: "hex"}, + crossCompile: &crosscompile.Export{}, + wantFmt: "hex", + wantExt: ".hex", + }, + { + name: "emulator format extraction", + conf: &Config{Mode: ModeRun, Emulator: true}, + crossCompile: &crosscompile.Export{ + Emulator: "qemu-system-xtensa -machine esp32 -drive file={bin},if=mtd,format=raw", + }, + wantFmt: "bin", + wantExt: ".bin", + }, + { + name: "flash method command - extract hex", + conf: &Config{Mode: ModeRun, Target: "esp32"}, + crossCompile: &crosscompile.Export{ + Flash: crosscompile.Flash{ + Method: "command", + Command: "esptool.py --chip esp32 write_flash 0x10000 {hex}", + }, + }, wantFmt: "hex", wantExt: ".hex", }, { - name: "emulator format extraction", - conf: &Config{Mode: ModeRun, Emulator: true}, - emulator: "qemu-system-xtensa -machine esp32 -drive file={bin},if=mtd,format=raw", - wantFmt: "bin", - wantExt: ".bin", + name: "flash method command - extract bin", + conf: &Config{Mode: ModeRun, Target: "esp32"}, + crossCompile: &crosscompile.Export{ + Flash: crosscompile.Flash{ + Method: "command", + Command: "esptool.py --chip esp32 write_flash 0x10000 {bin}", + }, + }, + wantFmt: "bin", + wantExt: ".bin", }, { - name: "no format", - conf: &Config{}, - wantFmt: "", - wantExt: "", + name: "flash method openocd", + conf: &Config{Mode: ModeRun, Target: "stm32"}, + crossCompile: &crosscompile.Export{ + Flash: crosscompile.Flash{ + Method: "openocd", + }, + }, + wantFmt: "hex", + wantExt: ".hex", + }, + { + name: "flash method msd - extract from firmware name", + conf: &Config{Mode: ModeRun, Target: "rp2040"}, + crossCompile: &crosscompile.Export{ + Flash: crosscompile.Flash{ + Method: "msd", + }, + MSD: crosscompile.MSD{ + FirmwareName: "firmware.uf2", + }, + }, + wantFmt: "uf2", + wantExt: ".uf2", + }, + { + name: "flash method bmp", + conf: &Config{Mode: ModeRun, Target: "stm32"}, + crossCompile: &crosscompile.Export{ + Flash: crosscompile.Flash{ + Method: "bmp", + }, + }, + wantFmt: "elf", + wantExt: ".elf", + }, + { + name: "no format", + conf: &Config{}, + crossCompile: &crosscompile.Export{}, + wantFmt: "", + wantExt: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gotFmt, gotExt := determineFormat(tt.conf, tt.emulator) + gotFmt, gotExt := determineFormat(tt.conf, tt.crossCompile) if gotFmt != tt.wantFmt { t.Errorf("determineFormat() format = %v, want %v", gotFmt, tt.wantFmt) } diff --git a/internal/crosscompile/crosscompile.go b/internal/crosscompile/crosscompile.go index 6ce60529..ebbb3e3c 100644 --- a/internal/crosscompile/crosscompile.go +++ b/internal/crosscompile/crosscompile.go @@ -18,6 +18,7 @@ import ( // Flash contains configuration for device flashing type Flash struct { + Method string // Flash method: "command", "openocd", "msd", "bmp" Command string // Flash command template Serial string // Serial communication settings SerialPort []string // Available serial ports @@ -530,6 +531,7 @@ func useTarget(targetName string) (export Export, err error) { // Set flashing/debugging configuration export.Flash = Flash{ + Method: config.FlashMethod, Command: config.FlashCommand, Serial: config.Serial, SerialPort: config.SerialPort,