refactor build/install/run pipeline
This commit is contained in:
@@ -18,7 +18,6 @@ package build
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"debug/macho"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
@@ -315,24 +314,51 @@ func Do(args []string, conf *Config) ([]Package, error) {
|
|||||||
|
|
||||||
for _, pkg := range initial {
|
for _, pkg := range initial {
|
||||||
if needLink(pkg, mode) {
|
if needLink(pkg, mode) {
|
||||||
app, err := linkMainPkg(ctx, pkg, allPkgs, global, conf, mode, verbose)
|
name := path.Base(pkg.PkgPath)
|
||||||
|
binFmt := ctx.crossCompile.BinaryFormat
|
||||||
|
|
||||||
|
outputCfg, err := genOutputs(conf, name, len(ctx.initial) > 1, ctx.crossCompile.Emulator, binFmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if slices.Contains([]Mode{ModeRun, ModeCmpTest, ModeTest, ModeInstall}, mode) {
|
err = linkMainPkg(ctx, pkg, allPkgs, global, outputCfg.IntPath, verbose)
|
||||||
// Flash to device if needed (for embedded targets)
|
|
||||||
if !conf.Emulator && conf.Target != "" {
|
|
||||||
err = flash(ctx, app, verbose)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else if mode != ModeInstall {
|
|
||||||
err = run(ctx, app, pkg.PkgPath, pkg.Dir, conf, mode, verbose)
|
finalApp := outputCfg.IntPath
|
||||||
|
if outputCfg.NeedFwGen || outputCfg.FileFmt != "" {
|
||||||
|
finalApp, err = convertFormat(ctx, finalApp, &outputCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case ModeBuild:
|
||||||
|
// Do nothing
|
||||||
|
|
||||||
|
case ModeInstall:
|
||||||
|
// Native already installed in linkMainPkg
|
||||||
|
if conf.Target != "" {
|
||||||
|
err = flash(ctx, finalApp, verbose)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case ModeRun, ModeTest, ModeCmpTest:
|
||||||
|
if conf.Target == "" {
|
||||||
|
err = runNative(ctx, finalApp, pkg.Dir, pkg.PkgPath, conf, mode)
|
||||||
|
} else if conf.Emulator {
|
||||||
|
err = runInEmulator(ctx.crossCompile.Emulator, finalApp, pkg.Dir, pkg.PkgPath, conf, mode, verbose)
|
||||||
|
} else {
|
||||||
|
err = flash(ctx, finalApp, verbose)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -648,19 +674,7 @@ func compileExtraFiles(ctx *context, verbose bool) ([]string, error) {
|
|||||||
return objFiles, nil
|
return objFiles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global llssa.Package, conf *Config, mode Mode, verbose bool) (string, error) {
|
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global llssa.Package, outputPath string, verbose bool) 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)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
app := outputCfg.OutPath
|
|
||||||
orgApp := outputCfg.IntPath
|
|
||||||
|
|
||||||
needRuntime := false
|
needRuntime := false
|
||||||
needPyInit := false
|
needPyInit := false
|
||||||
@@ -689,7 +703,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
|
|||||||
})
|
})
|
||||||
entryObjFile, err := genMainModuleFile(ctx, llssa.PkgRuntime, pkg, needRuntime, needPyInit)
|
entryObjFile, err := genMainModuleFile(ctx, llssa.PkgRuntime, pkg, needRuntime, needPyInit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
// defer os.Remove(entryLLFile)
|
// defer os.Remove(entryLLFile)
|
||||||
objFiles = append(objFiles, entryObjFile)
|
objFiles = append(objFiles, entryObjFile)
|
||||||
@@ -697,14 +711,14 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
|
|||||||
// Compile extra files from target configuration
|
// Compile extra files from target configuration
|
||||||
extraObjFiles, err := compileExtraFiles(ctx, verbose)
|
extraObjFiles, err := compileExtraFiles(ctx, verbose)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
objFiles = append(objFiles, extraObjFiles...)
|
objFiles = append(objFiles, extraObjFiles...)
|
||||||
|
|
||||||
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()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
objFiles = append(objFiles, export)
|
objFiles = append(objFiles, export)
|
||||||
}
|
}
|
||||||
@@ -726,28 +740,24 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = linkObjFiles(ctx, orgApp, objFiles, linkArgs, verbose)
|
return linkObjFiles(ctx, outputPath, objFiles, linkArgs, verbose)
|
||||||
if err != nil {
|
}
|
||||||
return "", err
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle firmware conversion and file format conversion
|
|
||||||
currentApp := orgApp
|
|
||||||
|
|
||||||
// Step 1: Firmware conversion if needed
|
|
||||||
if outputCfg.NeedFwGen {
|
if outputCfg.NeedFwGen {
|
||||||
if outputCfg.DirectGen {
|
if outputCfg.DirectGen {
|
||||||
// Direct conversion to final output (including .img case)
|
err := firmware.MakeFirmwareImage(currentApp, app, outputCfg.BinFmt, ctx.crossCompile.FormatDetail)
|
||||||
if verbose {
|
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
currentApp = app
|
currentApp = app
|
||||||
} else {
|
} else {
|
||||||
// Convert to intermediate file first
|
|
||||||
binExt := firmware.BinaryExt(ctx.crossCompile.BinaryFormat)
|
binExt := firmware.BinaryExt(ctx.crossCompile.BinaryFormat)
|
||||||
tmpFile, err := os.CreateTemp("", "llgo-*"+binExt)
|
tmpFile, err := os.CreateTemp("", "llgo-*"+binExt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -756,16 +766,12 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
|
|||||||
tmpFile.Close()
|
tmpFile.Close()
|
||||||
intermediateApp := tmpFile.Name()
|
intermediateApp := tmpFile.Name()
|
||||||
|
|
||||||
if verbose {
|
|
||||||
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)
|
err = firmware.MakeFirmwareImage(currentApp, intermediateApp, ctx.crossCompile.BinaryFormat, ctx.crossCompile.FormatDetail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
currentApp = intermediateApp
|
currentApp = intermediateApp
|
||||||
defer func() {
|
defer func() {
|
||||||
// Only remove if the intermediate file still exists (wasn't moved)
|
|
||||||
if _, err := os.Stat(intermediateApp); err == nil {
|
if _, err := os.Stat(intermediateApp); err == nil {
|
||||||
os.Remove(intermediateApp)
|
os.Remove(intermediateApp)
|
||||||
}
|
}
|
||||||
@@ -773,23 +779,15 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: File format conversion if needed
|
|
||||||
if currentApp != app {
|
if currentApp != app {
|
||||||
if outputCfg.FileFmt != "" {
|
if outputCfg.FileFmt != "" {
|
||||||
// File format conversion
|
binFmt := ctx.crossCompile.BinaryFormat
|
||||||
if verbose {
|
err := firmware.ConvertOutput(currentApp, app, binFmt, outputCfg.FileFmt)
|
||||||
fmt.Fprintf(os.Stderr, "Converting to file format: %s (%s -> %s)\n", outputCfg.FileFmt, currentApp, app)
|
|
||||||
}
|
|
||||||
err = firmware.ConvertOutput(currentApp, app, binFmt, outputCfg.FileFmt)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Just move/copy the file
|
err := os.Rename(currentApp, app)
|
||||||
if verbose {
|
|
||||||
fmt.Fprintf(os.Stderr, "Moving file: %s -> %s\n", currentApp, app)
|
|
||||||
}
|
|
||||||
err = os.Rename(currentApp, app)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -799,87 +797,6 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
|
|||||||
return app, nil
|
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 = pkgDir
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
if isWasmTarget(conf.Goos) {
|
|
||||||
wasmer := os.ExpandEnv(WasmRuntime())
|
|
||||||
wasmerArgs := strings.Split(wasmer, " ")
|
|
||||||
wasmerCmd := wasmerArgs[0]
|
|
||||||
wasmerArgs = wasmerArgs[1:]
|
|
||||||
switch wasmer {
|
|
||||||
case "wasmtime":
|
|
||||||
args = append(args, "--wasm", "multi-memory=true", app)
|
|
||||||
args = append(args, conf.RunArgs...)
|
|
||||||
case "iwasm":
|
|
||||||
args = append(args, "--stack-size=819200000", "--heap-size=800000000", app)
|
|
||||||
args = append(args, conf.RunArgs...)
|
|
||||||
default:
|
|
||||||
args = append(args, wasmerArgs...)
|
|
||||||
args = append(args, app)
|
|
||||||
args = append(args, conf.RunArgs...)
|
|
||||||
}
|
|
||||||
app = wasmerCmd
|
|
||||||
} else {
|
|
||||||
args = conf.RunArgs
|
|
||||||
}
|
|
||||||
cmd := exec.Command(app, args...)
|
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if s := cmd.ProcessState; s != nil {
|
|
||||||
mockable.Exit(s.ExitCode())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ModeCmpTest:
|
|
||||||
cmpTest(pkgDir, pkgPath, app, conf.GenExpect, conf.RunArgs)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func flash(ctx *context, app string, verbose bool) error {
|
func flash(ctx *context, app string, verbose bool) error {
|
||||||
// TODO: Implement device flashing logic
|
// TODO: Implement device flashing logic
|
||||||
if verbose {
|
if verbose {
|
||||||
@@ -1359,76 +1276,8 @@ func pkgExists(initial []*packages.Package, pkg *packages.Package) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// findDylibDep finds the dylib dependency in the executable. It returns empty
|
|
||||||
// string if not found.
|
|
||||||
func findDylibDep(exe, lib string) string {
|
|
||||||
file, err := macho.Open(exe)
|
|
||||||
check(err)
|
|
||||||
defer file.Close()
|
|
||||||
for _, load := range file.Loads {
|
|
||||||
if dylib, ok := load.(*macho.Dylib); ok {
|
|
||||||
if strings.HasPrefix(filepath.Base(dylib.Name), fmt.Sprintf("lib%s.", lib)) {
|
|
||||||
return dylib.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type none struct{}
|
type none struct{}
|
||||||
|
|
||||||
// runInEmulator runs the application in emulator by formatting the emulator command template
|
|
||||||
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
|
|
||||||
"bin": appPath,
|
|
||||||
"hex": appPath,
|
|
||||||
"zip": appPath,
|
|
||||||
"img": appPath,
|
|
||||||
"uf2": appPath,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expand the emulator command template
|
|
||||||
emulatorCmd := emulatorTemplate
|
|
||||||
for placeholder, path := range envs {
|
|
||||||
var target string
|
|
||||||
if placeholder == "" {
|
|
||||||
target = "{}"
|
|
||||||
} else {
|
|
||||||
target = "{" + placeholder + "}"
|
|
||||||
}
|
|
||||||
emulatorCmd = strings.ReplaceAll(emulatorCmd, target, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
if verbose {
|
|
||||||
fmt.Fprintf(os.Stderr, "Running in emulator: %s\n", emulatorCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse command and arguments
|
|
||||||
cmdParts := strings.Fields(emulatorCmd)
|
|
||||||
if len(cmdParts) == 0 {
|
|
||||||
panic(fmt.Errorf("empty emulator command"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add run arguments to the end
|
|
||||||
cmdParts = append(cmdParts, runArgs...)
|
|
||||||
|
|
||||||
// Execute the emulator command
|
|
||||||
cmd := exec.Command(cmdParts[0], cmdParts[1:]...)
|
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if s := cmd.ProcessState; s != nil {
|
|
||||||
mockable.Exit(s.ExitCode())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func check(err error) {
|
func check(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ type OutputCfg struct {
|
|||||||
DirectGen bool // True if can generate firmware directly without intermediate file
|
DirectGen bool // True if can generate firmware directly without intermediate file
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenOutputs generates appropriate output paths based on the configuration
|
// 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, emulator, binFmt string) (OutputCfg, error) {
|
||||||
var cfg OutputCfg
|
var cfg OutputCfg
|
||||||
|
|
||||||
// Calculate binary extension and set up format info
|
// Calculate binary extension and set up format info
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package build
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -246,12 +245,7 @@ func TestGenOutputs(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
// Determine input binFmt - remove -img suffix if present as it will be added by the code
|
result, err := genOutputs(tt.conf, tt.pkgName, tt.multiPkg, tt.crossCompile)
|
||||||
inputBinFmt := tt.wantBinFmt
|
|
||||||
if strings.HasSuffix(inputBinFmt, "-img") && tt.wantFileFmt == "img" {
|
|
||||||
inputBinFmt = strings.TrimSuffix(inputBinFmt, "-img")
|
|
||||||
}
|
|
||||||
result, err := GenOutputs(tt.conf, tt.pkgName, tt.multiPkg, tt.emulator, inputBinFmt)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("GenOutputs() error = %v", err)
|
t.Fatalf("GenOutputs() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
158
internal/build/run.go
Normal file
158
internal/build/run.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/internal/mockable"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runNative(ctx *context, app, pkgDir, pkgName string, conf *Config, mode Mode) error {
|
||||||
|
switch mode {
|
||||||
|
case ModeRun:
|
||||||
|
args := make([]string, 0, len(conf.RunArgs)+1)
|
||||||
|
if isWasmTarget(conf.Goos) {
|
||||||
|
wasmer := os.ExpandEnv(WasmRuntime())
|
||||||
|
wasmerArgs := strings.Split(wasmer, " ")
|
||||||
|
wasmerCmd := wasmerArgs[0]
|
||||||
|
wasmerArgs = wasmerArgs[1:]
|
||||||
|
switch wasmer {
|
||||||
|
case "wasmtime":
|
||||||
|
args = append(args, "--wasm", "multi-memory=true", app)
|
||||||
|
args = append(args, conf.RunArgs...)
|
||||||
|
case "iwasm":
|
||||||
|
args = append(args, "--stack-size=819200000", "--heap-size=800000000", app)
|
||||||
|
args = append(args, conf.RunArgs...)
|
||||||
|
default:
|
||||||
|
args = append(args, wasmerArgs...)
|
||||||
|
args = append(args, app)
|
||||||
|
args = append(args, conf.RunArgs...)
|
||||||
|
}
|
||||||
|
app = wasmerCmd
|
||||||
|
} else {
|
||||||
|
args = conf.RunArgs
|
||||||
|
}
|
||||||
|
cmd := exec.Command(app, args...)
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s := cmd.ProcessState; s != nil {
|
||||||
|
mockable.Exit(s.ExitCode())
|
||||||
|
}
|
||||||
|
case ModeTest:
|
||||||
|
cmd := exec.Command(app, conf.RunArgs...)
|
||||||
|
cmd.Dir = pkgDir
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: exit code %d\n", app, exitErr.ExitCode())
|
||||||
|
if !ctx.testFail {
|
||||||
|
ctx.testFail = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to run test %s: %v\n", app, err)
|
||||||
|
if !ctx.testFail {
|
||||||
|
ctx.testFail = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ModeCmpTest:
|
||||||
|
cmpTest(pkgDir, pkgName, app, conf.GenExpect, conf.RunArgs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runInEmulator(emulator, app, pkgDir, pkgName string, conf *Config, mode Mode, verbose bool) error {
|
||||||
|
if emulator == "" {
|
||||||
|
return fmt.Errorf("target %s does not have emulator configured", conf.Target)
|
||||||
|
}
|
||||||
|
if verbose {
|
||||||
|
fmt.Fprintf(os.Stderr, "Using emulator: %s\n", emulator)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case ModeRun:
|
||||||
|
return runEmuCmd(app, emulator, conf.RunArgs, verbose)
|
||||||
|
case ModeTest:
|
||||||
|
return runEmuCmd(app, emulator, conf.RunArgs, verbose)
|
||||||
|
case ModeCmpTest:
|
||||||
|
cmpTest(pkgDir, pkgName, app, conf.GenExpect, conf.RunArgs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// runEmuCmd runs the application in emulator by formatting the emulator command template
|
||||||
|
func runEmuCmd(appPath, emulatorTemplate string, runArgs []string, verbose bool) error {
|
||||||
|
// Build environment map for template variable expansion
|
||||||
|
envs := map[string]string{
|
||||||
|
"": appPath, // {} expands to app path
|
||||||
|
"bin": appPath,
|
||||||
|
"hex": appPath,
|
||||||
|
"zip": appPath,
|
||||||
|
"img": appPath,
|
||||||
|
"uf2": appPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand the emulator command template
|
||||||
|
emulatorCmd := emulatorTemplate
|
||||||
|
for placeholder, path := range envs {
|
||||||
|
var target string
|
||||||
|
if placeholder == "" {
|
||||||
|
target = "{}"
|
||||||
|
} else {
|
||||||
|
target = "{" + placeholder + "}"
|
||||||
|
}
|
||||||
|
emulatorCmd = strings.ReplaceAll(emulatorCmd, target, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
fmt.Fprintf(os.Stderr, "Running in emulator: %s\n", emulatorCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse command and arguments
|
||||||
|
cmdParts := strings.Fields(emulatorCmd)
|
||||||
|
if len(cmdParts) == 0 {
|
||||||
|
panic(fmt.Errorf("empty emulator command"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add run arguments to the end
|
||||||
|
cmdParts = append(cmdParts, runArgs...)
|
||||||
|
|
||||||
|
// Execute the emulator command
|
||||||
|
cmd := exec.Command(cmdParts[0], cmdParts[1:]...)
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s := cmd.ProcessState; s != nil {
|
||||||
|
mockable.Exit(s.ExitCode())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user