extract run from linkMainPkg, add flash scaffold

This commit is contained in:
Li Jie
2025-09-05 18:39:47 +08:00
parent 5e5d5c2a83
commit 1033452e8f
8 changed files with 137 additions and 51 deletions

View File

@@ -20,6 +20,7 @@ var BuildEnv string
var Tags string
var Target string
var Emulator bool
var Port string
var AbiMode int
var CheckLinkArgs bool
var CheckLLFiles bool
@@ -40,10 +41,14 @@ func AddBuildFlags(fs *flag.FlagSet) {
var Gen bool
func AddRunFlags(fs *flag.FlagSet) {
func AddEmulatorFlags(fs *flag.FlagSet) {
fs.BoolVar(&Emulator, "emulator", false, "Run in emulator mode")
}
func AddEmbeddedFlags(fs *flag.FlagSet) {
fs.StringVar(&Port, "port", "", "Target port for flashing")
}
func AddCmpTestFlags(fs *flag.FlagSet) {
fs.BoolVar(&Gen, "gen", false, "Generate llgo.expect file")
}
@@ -56,9 +61,14 @@ func UpdateConfig(conf *build.Config) {
case build.ModeBuild:
conf.OutFile = OutputFile
conf.FileFormat = FileFormat
case build.ModeRun:
case build.ModeRun, build.ModeTest:
conf.Emulator = Emulator
conf.Port = Port
case build.ModeInstall:
conf.Port = Port
case build.ModeCmpTest:
conf.Emulator = Emulator
conf.Port = Port
conf.GenExpect = Gen
}
if buildenv.Dev {

View File

@@ -36,6 +36,7 @@ var Cmd = &base.Command{
func init() {
Cmd.Run = runCmd
flags.AddBuildFlags(&Cmd.Flag)
flags.AddEmbeddedFlags(&Cmd.Flag)
}
func runCmd(cmd *base.Command, args []string) {

View File

@@ -49,8 +49,13 @@ func init() {
CmpTestCmd.Run = runCmpTest
base.PassBuildFlags(Cmd)
flags.AddBuildFlags(&Cmd.Flag)
flags.AddEmulatorFlags(&Cmd.Flag)
flags.AddEmbeddedFlags(&Cmd.Flag) // for -target support
base.PassBuildFlags(CmpTestCmd)
flags.AddBuildFlags(&CmpTestCmd.Flag)
flags.AddRunFlags(&Cmd.Flag)
flags.AddEmulatorFlags(&CmpTestCmd.Flag)
flags.AddEmbeddedFlags(&CmpTestCmd.Flag) // for -target support
flags.AddCmpTestFlags(&CmpTestCmd.Flag)
}

View File

@@ -18,6 +18,8 @@ var Cmd = &base.Command{
func init() {
Cmd.Run = runCmd
flags.AddBuildFlags(&Cmd.Flag)
flags.AddEmulatorFlags(&Cmd.Flag)
flags.AddEmbeddedFlags(&Cmd.Flag)
}
func runCmd(cmd *base.Command, args []string) {

View File

@@ -7,7 +7,7 @@
- `-file-format <format>` - Convert to specified format (**requires `-target`**)
- Supported: `elf` (default), `bin`, `hex`, `uf2`, `zip`, `img`
- `-emulator` - Run using emulator (auto-detects required format)
- `-d <device>` - Target device for flashing or testing
- `-port <port>` - Target port for flashing or testing
## Commands
@@ -25,12 +25,12 @@ Compile and run program.
Compile and run tests.
- No `-target`: Run tests locally
- With `-target`: Run tests on device or emulator
- Supports `-emulator` and `-d` flags
- Supports `-emulator` and `-port` flags
### llgo install
Install program or flash to device.
- No `-target`: Install to `$GOPATH/bin`
- With `-target`: Flash to device (use `-d` to specify device)
- With `-target`: Flash to device (use `-port` to specify port)
## Examples
@@ -46,7 +46,7 @@ llgo build -target esp32 hello.go # -> hello (ELF)
llgo build -target esp32 -file-format bin hello.go # -> hello.bin
llgo run -target esp32 hello.go # run on ESP32
llgo run -target esp32 -emulator hello.go # run in emulator
llgo test -target esp32 -d /dev/ttyUSB0 # run tests on device
llgo test -target esp32 -emulator # run tests in emulator
llgo install -target esp32 -d /dev/ttyUSB0 hello.go # flash to specific device
llgo test -target esp32 -port /dev/ttyUSB0 . # run tests on device
llgo test -target esp32 -emulator . # run tests in emulator
llgo install -target esp32 -port /dev/ttyUSB0 hello.go # flash to specific port
```

View File

@@ -79,7 +79,8 @@ type Config struct {
AppExt string // ".exe" on Windows, empty on Unix
OutFile string // only valid for ModeBuild when len(pkgs) == 1
FileFormat string // File format override (e.g., "bin", "hex", "elf", "uf2", "zip") - takes precedence over target's default
Emulator bool // only valid for ModeRun - run in emulator mode
Emulator bool // only valid for ModeRun/ModeTest - run in emulator mode
Port string // only valid for ModeRun/ModeTest/ModeInstall/ModeCmpTest - target port for flashing
RunArgs []string // only valid for ModeRun
Mode Mode
AbiMode AbiMode
@@ -314,7 +315,25 @@ func Do(args []string, conf *Config) ([]Package, error) {
for _, pkg := range initial {
if needLink(pkg, mode) {
linkMainPkg(ctx, pkg, allPkgs, global, conf, mode, verbose)
app, err := linkMainPkg(ctx, pkg, allPkgs, global, conf, mode, verbose)
if err != nil {
return nil, err
}
if slices.Contains([]Mode{ModeRun, ModeCmpTest, ModeTest, ModeInstall}, mode) {
// Flash to device if needed (for embedded targets)
if !conf.Emulator && conf.Target != "" {
err = flash(ctx, app, verbose)
if err != nil {
return nil, err
}
} else if mode != ModeInstall {
err = run(ctx, app, pkg.PkgPath, pkg.Dir, conf, mode, verbose)
if err != nil {
return nil, err
}
}
}
}
}
@@ -629,14 +648,16 @@ func compileExtraFiles(ctx *context, verbose bool) ([]string, error) {
return objFiles, nil
}
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global llssa.Package, conf *Config, mode Mode, verbose bool) {
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global llssa.Package, conf *Config, mode Mode, verbose bool) (string, error) {
pkgPath := pkg.PkgPath
name := path.Base(pkgPath)
binFmt := ctx.crossCompile.BinaryFormat
// Generate output configuration using the centralized function
outputCfg, err := GenOutputs(conf, name, len(ctx.initial) > 1, ctx.crossCompile.Emulator, binFmt)
check(err)
if err != nil {
return "", err
}
app := outputCfg.OutPath
orgApp := outputCfg.IntPath
@@ -667,18 +688,24 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
}
})
entryObjFile, err := genMainModuleFile(ctx, llssa.PkgRuntime, pkg, needRuntime, needPyInit)
check(err)
if err != nil {
return "", err
}
// defer os.Remove(entryLLFile)
objFiles = append(objFiles, entryObjFile)
// Compile extra files from target configuration
extraObjFiles, err := compileExtraFiles(ctx, verbose)
check(err)
if err != nil {
return "", err
}
objFiles = append(objFiles, extraObjFiles...)
if global != nil {
export, err := exportObject(ctx, pkg.PkgPath+".global", pkg.ExportFile+"-global", []byte(global.String()))
check(err)
if err != nil {
return "", err
}
objFiles = append(objFiles, export)
}
@@ -700,19 +727,13 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
}
err = linkObjFiles(ctx, orgApp, objFiles, linkArgs, verbose)
check(err)
if err != nil {
return "", err
}
// Handle firmware conversion and file format conversion
currentApp := orgApp
useEmulator := false
if mode == ModeRun && conf.Emulator {
if ctx.crossCompile.Emulator == "" {
panic(fmt.Errorf("target %s does not have emulator configured", conf.Target))
}
useEmulator = true
}
// Step 1: Firmware conversion if needed
if outputCfg.NeedFwGen {
if outputCfg.DirectGen {
@@ -721,13 +742,17 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
fmt.Fprintf(os.Stderr, "Converting to firmware format: %s (%s -> %s)\n", outputCfg.BinFmt, currentApp, app)
}
err = firmware.MakeFirmwareImage(currentApp, app, outputCfg.BinFmt, ctx.crossCompile.FormatDetail)
check(err)
if err != nil {
return "", err
}
currentApp = app
} else {
// Convert to intermediate file first
binExt := firmware.BinaryExt(ctx.crossCompile.BinaryFormat)
tmpFile, err := os.CreateTemp("", "llgo-*"+binExt)
check(err)
if err != nil {
return "", err
}
tmpFile.Close()
intermediateApp := tmpFile.Name()
@@ -735,7 +760,9 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
fmt.Fprintf(os.Stderr, "Converting to firmware format: %s (%s -> %s)\n", ctx.crossCompile.BinaryFormat, currentApp, intermediateApp)
}
err = firmware.MakeFirmwareImage(currentApp, intermediateApp, ctx.crossCompile.BinaryFormat, ctx.crossCompile.FormatDetail)
check(err)
if err != nil {
return "", err
}
currentApp = intermediateApp
defer func() {
// Only remove if the intermediate file still exists (wasn't moved)
@@ -754,37 +781,63 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
fmt.Fprintf(os.Stderr, "Converting to file format: %s (%s -> %s)\n", outputCfg.FileFmt, currentApp, app)
}
err = firmware.ConvertOutput(currentApp, app, binFmt, outputCfg.FileFmt)
check(err)
if err != nil {
return "", err
}
} else {
// Just move/copy the file
if verbose {
fmt.Fprintf(os.Stderr, "Moving file: %s -> %s\n", currentApp, app)
}
err = os.Rename(currentApp, app)
check(err)
if err != nil {
return "", err
}
}
}
return app, nil
}
func run(ctx *context, app string, pkgPath, pkgDir string, conf *Config, mode Mode, verbose bool) error {
useEmulator := false
if mode == ModeRun && conf.Emulator {
if ctx.crossCompile.Emulator == "" {
return fmt.Errorf("target %s does not have emulator configured", conf.Target)
}
useEmulator = true
}
switch mode {
case ModeTest:
cmd := exec.Command(app, conf.RunArgs...)
cmd.Dir = pkg.Dir
cmd.Dir = pkgDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
if s := cmd.ProcessState; s != nil {
exitCode := s.ExitCode()
fmt.Fprintf(os.Stderr, "%s: exit code %d\n", app, exitCode)
if !ctx.testFail && exitCode != 0 {
if err := cmd.Run(); err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
// The command ran and exited with a non-zero status.
fmt.Fprintf(os.Stderr, "%s: exit code %d\n", app, exitErr.ExitCode())
if !ctx.testFail {
ctx.testFail = true
}
} else {
// The command failed to start or other error.
fmt.Fprintf(os.Stderr, "failed to run test %s: %v\n", app, err)
if !ctx.testFail {
ctx.testFail = true
}
}
}
case ModeRun:
if useEmulator {
if verbose {
fmt.Fprintf(os.Stderr, "Using emulator: %s\n", ctx.crossCompile.Emulator)
}
runInEmulator(app, ctx.crossCompile.Emulator, conf.RunArgs, verbose)
err := runInEmulator(app, ctx.crossCompile.Emulator, conf.RunArgs, verbose)
if err != nil {
return err
}
} else {
args := make([]string, 0, len(conf.RunArgs)+1)
copy(args, conf.RunArgs)
@@ -813,17 +866,28 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
err := cmd.Run()
if err != nil {
panic(err)
return err
}
if s := cmd.ProcessState; s != nil {
mockable.Exit(s.ExitCode())
}
}
case ModeCmpTest:
cmpTest(filepath.Dir(pkg.GoFiles[0]), pkgPath, app, conf.GenExpect, conf.RunArgs)
cmpTest(pkgDir, pkgPath, app, conf.GenExpect, conf.RunArgs)
}
return nil
}
func flash(ctx *context, app string, verbose bool) error {
// TODO: Implement device flashing logic
if verbose {
fmt.Fprintf(os.Stderr, "Flashing %s to port %s\n", app, ctx.buildConf.Port)
}
fmt.Printf("conf: %#v\n", ctx.buildConf)
fmt.Printf("crosscompile: %#v\n", ctx.crossCompile)
return nil
}
func linkObjFiles(ctx *context, app string, objFiles, linkArgs []string, verbose bool) error {
@@ -1314,7 +1378,7 @@ func findDylibDep(exe, lib string) string {
type none struct{}
// runInEmulator runs the application in emulator by formatting the emulator command template
func runInEmulator(appPath, emulatorTemplate string, runArgs []string, verbose bool) {
func runInEmulator(appPath, emulatorTemplate string, runArgs []string, verbose bool) error {
// Build environment map for template variable expansion
envs := map[string]string{
"": appPath, // {} expands to app path
@@ -1357,11 +1421,12 @@ func runInEmulator(appPath, emulatorTemplate string, runArgs []string, verbose b
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
panic(err)
return err
}
if s := cmd.ProcessState; s != nil {
mockable.Exit(s.ExitCode())
}
return nil
}
func check(err error) {

View File

@@ -110,7 +110,10 @@ func genBuildOutputs(conf *Config, pkgName string, multiPkg bool, cfg OutputCfg)
}
} else {
// Direct output
cfg.OutPath = baseName + conf.AppExt
cfg.OutPath = baseName
if filepath.Ext(cfg.OutPath) != conf.AppExt {
cfg.OutPath += conf.AppExt
}
cfg.IntPath = cfg.OutPath
}

View File

@@ -33,7 +33,7 @@ func TestGenOutputs(t *testing.T) {
wantOutPath: "hello",
wantOutExt: "",
wantFileFmt: "",
wantBinFmt: "esp32",
wantBinFmt: "",
wantDirectGen: true,
},
{
@@ -47,7 +47,7 @@ func TestGenOutputs(t *testing.T) {
wantOutPath: "myapp",
wantOutExt: "",
wantFileFmt: "",
wantBinFmt: "esp32",
wantBinFmt: "",
wantDirectGen: true,
},
{
@@ -108,7 +108,7 @@ func TestGenOutputs(t *testing.T) {
wantOutPath: "", // temp file
wantOutExt: "",
wantFileFmt: "",
wantBinFmt: "esp32",
wantBinFmt: "",
wantDirectGen: true,
},
{
@@ -167,7 +167,7 @@ func TestGenOutputs(t *testing.T) {
wantOutPath: "", // temp file
wantOutExt: "",
wantFileFmt: "",
wantBinFmt: "esp32",
wantBinFmt: "",
wantDirectGen: true,
},
{
@@ -194,7 +194,7 @@ func TestGenOutputs(t *testing.T) {
wantOutPath: "", // temp file
wantOutExt: "",
wantFileFmt: "",
wantBinFmt: "esp32",
wantBinFmt: "",
wantDirectGen: true,
},
{