feat(cmd): enable -target parameter for build, run, and test commands

- Update build command: llgo build -target platform
- Update run command: llgo run -target platform
- Update test command: llgo test -target platform
- Wire target flag to build configuration
- Update usage documentation for new parameter

Examples:
- llgo build -target rp2040 ./firmware
- llgo run -target wasi ./main.go
- llgo test -target cortex-m ./tests

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Li Jie
2025-07-29 15:12:25 +08:00
parent 0136344282
commit daf0d7e56e
4 changed files with 45 additions and 21 deletions

View File

@@ -29,7 +29,7 @@ import (
// llgo build // llgo build
var Cmd = &base.Command{ var Cmd = &base.Command{
UsageLine: "llgo build [-o output] [build flags] [packages]", UsageLine: "llgo build [-o output] [-target platform] [build flags] [packages]",
Short: "Compile packages and dependencies", Short: "Compile packages and dependencies",
} }
@@ -50,6 +50,7 @@ func runCmd(cmd *base.Command, args []string) {
conf.Tags = flags.Tags conf.Tags = flags.Tags
conf.Verbose = flags.Verbose conf.Verbose = flags.Verbose
conf.OutFile = flags.OutputFile conf.OutFile = flags.OutputFile
conf.Target = flags.Target
args = cmd.Flag.Args() args = cmd.Flag.Args()

View File

@@ -34,7 +34,7 @@ var (
// llgo run // llgo run
var Cmd = &base.Command{ var Cmd = &base.Command{
UsageLine: "llgo run [build flags] package [arguments...]", UsageLine: "llgo run [-target platform] [build flags] package [arguments...]",
Short: "Compile and run Go program", Short: "Compile and run Go program",
} }
@@ -54,11 +54,11 @@ func init() {
} }
func runCmd(cmd *base.Command, args []string) { func runCmd(cmd *base.Command, args []string) {
runCmdEx(cmd, args, build.ModeRun) runCmdEx(cmd, args, build.ModeRun) // support target
} }
func runCmpTest(cmd *base.Command, args []string) { func runCmpTest(cmd *base.Command, args []string) {
runCmdEx(cmd, args, build.ModeCmpTest) runCmdEx(cmd, args, build.ModeCmpTest) // no target support
} }
func runCmdEx(cmd *base.Command, args []string, mode build.Mode) { func runCmdEx(cmd *base.Command, args []string, mode build.Mode) {
@@ -71,6 +71,7 @@ func runCmdEx(cmd *base.Command, args []string, mode build.Mode) {
conf.Tags = flags.Tags conf.Tags = flags.Tags
conf.Verbose = flags.Verbose conf.Verbose = flags.Verbose
conf.GenExpect = flags.Gen conf.GenExpect = flags.Gen
conf.Target = flags.Target
args = cmd.Flag.Args() args = cmd.Flag.Args()
args, runArgs, err := parseRunArgs(args) args, runArgs, err := parseRunArgs(args)

View File

@@ -11,7 +11,7 @@ import (
// llgo test // llgo test
var Cmd = &base.Command{ var Cmd = &base.Command{
UsageLine: "llgo test [build flags] package [arguments...]", UsageLine: "llgo test [-target platform] [build flags] package [arguments...]",
Short: "Compile and run Go test", Short: "Compile and run Go test",
} }
@@ -29,6 +29,7 @@ func runCmd(cmd *base.Command, args []string) {
conf := build.NewDefaultConf(build.ModeTest) conf := build.NewDefaultConf(build.ModeTest)
conf.Tags = flags.Tags conf.Tags = flags.Tags
conf.Verbose = flags.Verbose conf.Verbose = flags.Verbose
conf.Target = flags.Target
args = cmd.Flag.Args() args = cmd.Flag.Args()
_, err := build.Do(args, conf) _, err := build.Do(args, conf)

View File

@@ -150,6 +150,20 @@ func Do(args []string, conf *Config) ([]Package, error) {
if conf.Goarch == "" { if conf.Goarch == "" {
conf.Goarch = runtime.GOARCH conf.Goarch = runtime.GOARCH
} }
// Handle crosscompile configuration first to set correct GOOS/GOARCH
export, err := crosscompile.UseWithTarget(conf.Goos, conf.Goarch, IsWasiThreadsEnabled(), conf.Target)
if err != nil {
return nil, fmt.Errorf("failed to setup crosscompile: %w", err)
}
// Update GOOS/GOARCH from export if target was used
if conf.Target != "" && export.GOOS != "" {
conf.Goos = export.GOOS
}
if conf.Target != "" && export.GOARCH != "" {
conf.Goarch = export.GOARCH
}
verbose := conf.Verbose verbose := conf.Verbose
patterns := args patterns := args
tags := "llgo" tags := "llgo"
@@ -161,6 +175,7 @@ func Do(args []string, conf *Config) ([]Package, error) {
BuildFlags: []string{"-tags=" + tags}, BuildFlags: []string{"-tags=" + tags},
Fset: token.NewFileSet(), Fset: token.NewFileSet(),
Tests: conf.Mode == ModeTest, Tests: conf.Mode == ModeTest,
Env: append(slices.Clone(os.Environ()), "GOOS="+conf.Goos, "GOARCH="+conf.Goarch),
} }
if conf.Mode == ModeTest { if conf.Mode == ModeTest {
cfg.Mode |= packages.NeedForTest cfg.Mode |= packages.NeedForTest
@@ -252,8 +267,6 @@ func Do(args []string, conf *Config) ([]Package, error) {
os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows
output := conf.OutFile != "" output := conf.OutFile != ""
export, err := crosscompile.UseWithTarget(conf.Goos, conf.Goarch, IsWasiThreadsEnabled(), conf.Target)
check(err)
ctx := &context{env: env, conf: cfg, progSSA: progSSA, prog: prog, dedup: dedup, ctx := &context{env: env, conf: cfg, progSSA: progSSA, prog: prog, dedup: dedup,
patches: patches, built: make(map[string]none), initial: initial, mode: mode, patches: patches, built: make(map[string]none), initial: initial, mode: mode,
output: output, output: output,
@@ -371,6 +384,15 @@ func (c *context) compiler() *clang.Cmd {
return cmd return cmd
} }
func (c *context) linker() *clang.Cmd {
cmd := c.env.Clang()
if c.crossCompile.Linker != "" {
cmd = clang.New(c.crossCompile.Linker)
}
cmd.Verbose = c.buildConf.Verbose
return cmd
}
func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs []*aPackage, err error) { func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs []*aPackage, err error) {
pkgs, errPkgs := allPkgs(ctx, initial, verbose) pkgs, errPkgs := allPkgs(ctx, initial, verbose)
for _, errPkg := range errPkgs { for _, errPkg := range errPkgs {
@@ -541,14 +563,14 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
pkgsMap[v.Package] = v pkgsMap[v.Package] = v
allPkgs = append(allPkgs, v.Package) allPkgs = append(allPkgs, v.Package)
} }
var llFiles []string var objFiles []string
var linkArgs []string var linkArgs []string
packages.Visit(allPkgs, nil, func(p *packages.Package) { packages.Visit(allPkgs, nil, func(p *packages.Package) {
aPkg := pkgsMap[p] aPkg := pkgsMap[p]
if p.ExportFile != "" && aPkg != nil { // skip packages that only contain declarations if p.ExportFile != "" && aPkg != nil { // skip packages that only contain declarations
linkArgs = append(linkArgs, aPkg.LinkArgs...) linkArgs = append(linkArgs, aPkg.LinkArgs...)
llFiles = append(llFiles, aPkg.LLFiles...) objFiles = append(objFiles, aPkg.LLFiles...)
llFiles = append(llFiles, aPkg.ExportFile) objFiles = append(objFiles, aPkg.ExportFile)
need1, need2 := isNeedRuntimeOrPyInit(ctx, p) need1, need2 := isNeedRuntimeOrPyInit(ctx, p)
if !needRuntime { if !needRuntime {
needRuntime = need1 needRuntime = need1
@@ -558,18 +580,19 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
} }
} }
}) })
entryLLFile, err := genMainModuleFile(ctx, llssa.PkgRuntime, pkg, needRuntime, needPyInit) entryObjFile, err := genMainModuleFile(ctx, llssa.PkgRuntime, pkg, needRuntime, needPyInit)
check(err) check(err)
// defer os.Remove(entryLLFile) // defer os.Remove(entryLLFile)
llFiles = append(llFiles, entryLLFile) objFiles = append(objFiles, entryObjFile)
if global != nil { if global != nil {
export, err := exportObject(ctx, pkg.PkgPath+".global", pkg.ExportFile+"-global", []byte(global.String())) export, err := exportObject(ctx, pkg.PkgPath+".global", pkg.ExportFile+"-global", []byte(global.String()))
check(err) check(err)
llFiles = append(llFiles, export) objFiles = append(objFiles, export)
} }
err = compileAndLinkLLFiles(ctx, app, llFiles, linkArgs, verbose) err = linkObjFiles(ctx, app, objFiles, linkArgs, verbose)
err = linkObjFiles(ctx, app, objFiles, linkArgs, verbose)
check(err) check(err)
switch mode { switch mode {
@@ -626,7 +649,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
} }
} }
func compileAndLinkLLFiles(ctx *context, app string, llFiles, linkArgs []string, verbose bool) error { func linkObjFiles(ctx *context, app string, objFiles, linkArgs []string, verbose bool) error {
buildArgs := []string{"-o", app} buildArgs := []string{"-o", app}
buildArgs = append(buildArgs, linkArgs...) buildArgs = append(buildArgs, linkArgs...)
@@ -638,12 +661,9 @@ func compileAndLinkLLFiles(ctx *context, app string, llFiles, linkArgs []string,
buildArgs = append(buildArgs, ctx.crossCompile.CCFLAGS...) buildArgs = append(buildArgs, ctx.crossCompile.CCFLAGS...)
buildArgs = append(buildArgs, ctx.crossCompile.LDFLAGS...) buildArgs = append(buildArgs, ctx.crossCompile.LDFLAGS...)
buildArgs = append(buildArgs, ctx.crossCompile.EXTRAFLAGS...) buildArgs = append(buildArgs, ctx.crossCompile.EXTRAFLAGS...)
buildArgs = append(buildArgs, llFiles...) buildArgs = append(buildArgs, objFiles...)
if verbose {
buildArgs = append(buildArgs, "-v")
}
cmd := ctx.compiler() cmd := ctx.linker()
cmd.Verbose = verbose cmd.Verbose = verbose
return cmd.Link(buildArgs...) return cmd.Link(buildArgs...)
} }
@@ -820,6 +840,7 @@ func exportObject(ctx *context, pkgPath string, exportFile string, data []byte)
exportFile += ".o" exportFile += ".o"
args := []string{"-o", exportFile, "-c", f.Name(), "-Wno-override-module"} args := []string{"-o", exportFile, "-c", f.Name(), "-Wno-override-module"}
args = append(args, ctx.crossCompile.CCFLAGS...) args = append(args, ctx.crossCompile.CCFLAGS...)
args = append(args, ctx.crossCompile.CFLAGS...)
if ctx.buildConf.Verbose { if ctx.buildConf.Verbose {
fmt.Fprintln(os.Stderr, "clang", args) fmt.Fprintln(os.Stderr, "clang", args)
} }