refactor: multi format generation and llgo build flags

This commit is contained in:
Li Jie
2025-09-07 13:32:11 +08:00
parent c0afe199c2
commit 16c8402065
12 changed files with 541 additions and 979 deletions

View File

@@ -72,17 +72,36 @@ const (
debugBuild = packages.DebugPackagesLoad
)
// OutFmts contains output format specifications for embedded targets
type OutFmts struct {
Bin bool // Generate binary output (.bin)
Hex bool // Generate Intel hex output (.hex)
Img bool // Generate image output (.img)
Uf2 bool // Generate UF2 output (.uf2)
Zip bool // Generate ZIP/DFU output (.zip)
}
// OutFmtDetails contains detailed output file paths for each format
type OutFmtDetails struct {
Out string // Base output file path
Bin string // Binary output file path (.bin)
Hex string // Intel hex output file path (.hex)
Img string // Image output file path (.img)
Uf2 string // UF2 output file path (.uf2)
Zip string // ZIP/DFU output file path (.zip)
}
type Config struct {
Goos string
Goarch string
Target string // target name (e.g., "rp2040", "wasi") - takes precedence over Goos/Goarch
BinPath string
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 // run in emulator mode
Port string // target port for flashing
BaudRate int // baudrate for serial communication
AppExt string // ".exe" on Windows, empty on Unix
OutFile string // only valid for ModeBuild when len(pkgs) == 1
OutFmts OutFmts // Output format specifications (only for Target != "")
Emulator bool // run in emulator mode
Port string // target port for flashing
BaudRate int // baudrate for serial communication
RunArgs []string
Mode Mode
AbiMode AbiMode
@@ -121,7 +140,6 @@ func NewDefaultConf(mode Mode) *Config {
BinPath: bin,
Mode: mode,
AbiMode: cabi.ModeAllFunc,
AppExt: DefaultAppExt(goos),
}
return conf
}
@@ -137,8 +155,15 @@ func envGOPATH() (string, error) {
return filepath.Join(home, "go"), nil
}
func DefaultAppExt(goos string) string {
switch goos {
func defaultAppExt(conf *Config) string {
if conf.Target != "" {
if strings.HasPrefix(conf.Target, "wasi") || strings.HasPrefix(conf.Target, "wasm") {
return ".wasm"
}
return ".elf"
}
switch conf.Goos {
case "windows":
return ".exe"
case "wasi", "wasip1", "js":
@@ -163,6 +188,9 @@ func Do(args []string, conf *Config) ([]Package, error) {
if conf.Goarch == "" {
conf.Goarch = runtime.GOARCH
}
if conf.AppExt == "" {
conf.AppExt = defaultAppExt(conf)
}
// Handle crosscompile configuration first to set correct GOOS/GOARCH
export, err := crosscompile.Use(conf.Goos, conf.Goarch, IsWasiThreadsEnabled(), conf.Target)
if err != nil {
@@ -241,6 +269,10 @@ func Do(args []string, conf *Config) ([]Package, error) {
if conf.OutFile != "" {
return nil, fmt.Errorf("cannot build multiple packages with -o")
}
case ModeInstall:
if conf.Target != "" {
return nil, fmt.Errorf("cannot install multiple packages to embedded target")
}
case ModeRun:
return nil, fmt.Errorf("cannot run multiple packages")
case ModeTest:
@@ -319,19 +351,24 @@ func Do(args []string, conf *Config) ([]Package, error) {
if needLink(pkg, mode) {
name := path.Base(pkg.PkgPath)
outputCfg, err := genOutputs(conf, name, len(ctx.initial) > 1, &ctx.crossCompile)
// Create output format details
outFmts, err := buildOutFmts(name, conf, len(ctx.initial) > 1, &ctx.crossCompile)
if err != nil {
return nil, err
}
err = linkMainPkg(ctx, pkg, allPkgs, global, outputCfg.IntPath, verbose)
// Link main package using the base output path
err = linkMainPkg(ctx, pkg, allPkgs, global, outFmts.Out, verbose)
if err != nil {
return nil, err
}
finalApp := outputCfg.IntPath
if outputCfg.NeedFwGen || outputCfg.FileFmt != "" {
finalApp, err = convertFormat(ctx, finalApp, &outputCfg)
envMap := outFmts.ToEnvMap()
// Only convert formats when Target is specified
if conf.Target != "" {
// Process format conversions for embedded targets
err = firmware.ConvertFormats(ctx.crossCompile.BinaryFormat, ctx.crossCompile.FormatDetail, envMap)
if err != nil {
return nil, err
}
@@ -344,7 +381,7 @@ func Do(args []string, conf *Config) ([]Package, error) {
case ModeInstall:
// Native already installed in linkMainPkg
if conf.Target != "" {
err = flash.FlashDevice(ctx.crossCompile.Device, finalApp, ctx.buildConf.Port, verbose)
err = flash.FlashDevice(ctx.crossCompile.Device, envMap, ctx.buildConf.Port, verbose)
if err != nil {
return nil, err
}
@@ -352,18 +389,18 @@ func Do(args []string, conf *Config) ([]Package, error) {
case ModeRun, ModeTest, ModeCmpTest:
if conf.Target == "" {
err = runNative(ctx, finalApp, pkg.Dir, pkg.PkgPath, conf, mode)
err = runNative(ctx, outFmts.Out, pkg.Dir, pkg.PkgPath, conf, mode)
} else if conf.Emulator {
err = runInEmulator(ctx.crossCompile.Emulator, finalApp, pkg.Dir, pkg.PkgPath, conf, mode, verbose)
err = runInEmulator(ctx.crossCompile.Emulator, envMap, pkg.Dir, pkg.PkgPath, conf, mode, verbose)
} else {
err = flash.FlashDevice(ctx.crossCompile.Device, finalApp, ctx.buildConf.Port, verbose)
err = flash.FlashDevice(ctx.crossCompile.Device, envMap, ctx.buildConf.Port, verbose)
if err != nil {
return nil, err
}
monitorConfig := monitor.MonitorConfig{
Port: ctx.buildConf.Port,
Target: conf.Target,
Executable: finalApp,
Executable: outFmts.Out,
BaudRate: conf.BaudRate,
SerialPort: ctx.crossCompile.Device.SerialPort,
}
@@ -756,60 +793,6 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
return linkObjFiles(ctx, outputPath, objFiles, linkArgs, verbose)
}
func convertFormat(ctx *context, inputFile string, outputCfg *OutputCfg) (string, error) {
app := outputCfg.OutPath
currentApp := inputFile
if ctx.buildConf.Verbose {
fmt.Fprintf(os.Stderr, "Converting output format: %s -> %s\n", currentApp, app)
}
if outputCfg.NeedFwGen {
if outputCfg.DirectGen {
err := firmware.MakeFirmwareImage(currentApp, app, outputCfg.BinFmt, ctx.crossCompile.FormatDetail)
if err != nil {
return "", err
}
currentApp = app
} else {
binExt := firmware.BinaryExt(ctx.crossCompile.BinaryFormat)
tmpFile, err := os.CreateTemp("", "llgo-*"+binExt)
if err != nil {
return "", err
}
tmpFile.Close()
intermediateApp := tmpFile.Name()
err = firmware.MakeFirmwareImage(currentApp, intermediateApp, ctx.crossCompile.BinaryFormat, ctx.crossCompile.FormatDetail)
if err != nil {
return "", err
}
currentApp = intermediateApp
defer func() {
if _, err := os.Stat(intermediateApp); err == nil {
os.Remove(intermediateApp)
}
}()
}
}
if currentApp != app {
if outputCfg.FileFmt != "" {
binFmt := ctx.crossCompile.BinaryFormat
err := firmware.ConvertOutput(currentApp, app, binFmt, outputCfg.FileFmt)
if err != nil {
return "", err
}
} else {
err := os.Rename(currentApp, app)
if err != nil {
return "", err
}
}
}
return app, nil
}
func linkObjFiles(ctx *context, app string, objFiles, linkArgs []string, verbose bool) error {
buildArgs := []string{"-o", app}
buildArgs = append(buildArgs, linkArgs...)