extract run from linkMainPkg, add flash scaffold
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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,29 +781,52 @@ 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 {
|
||||
ctx.testFail = true
|
||||
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:
|
||||
@@ -784,7 +834,10 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
|
||||
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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user