all: run test binaries in the correct directory
Test binaries must be run in the source directory of the package to be tested. This wasn't done, leading to a few "file not found" errors. This commit implements this. Unfortunately, it does not allow more packages to be tested as both affected packages (debug/macho and debug/plan9obj) will still fail with this patch even though the "file not found" errors are gone.
Этот коммит содержится в:
родитель
57a5b833b2
коммит
88fd2823df
3 изменённых файлов: 80 добавлений и 39 удалений
|
@ -24,22 +24,35 @@ import (
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BuildResult is the output of a build. This includes the binary itself and
|
||||||
|
// some other metadata that is obtained while building the binary.
|
||||||
|
type BuildResult struct {
|
||||||
|
// A path to the output binary. It will be removed after Build returns, so
|
||||||
|
// if it should be kept it must be copied or moved away.
|
||||||
|
Binary string
|
||||||
|
|
||||||
|
// The directory of the main package. This is useful for testing as the test
|
||||||
|
// binary must be run in the directory of the tested package.
|
||||||
|
MainDir string
|
||||||
|
}
|
||||||
|
|
||||||
// Build performs a single package to executable Go build. It takes in a package
|
// Build performs a single package to executable Go build. It takes in a package
|
||||||
// name, an output path, and set of compile options and from that it manages the
|
// name, an output path, and set of compile options and from that it manages the
|
||||||
// whole compilation process.
|
// whole compilation process.
|
||||||
//
|
//
|
||||||
// The error value may be of type *MultiError. Callers will likely want to check
|
// The error value may be of type *MultiError. Callers will likely want to check
|
||||||
// for this case and print such errors individually.
|
// for this case and print such errors individually.
|
||||||
func Build(pkgName, outpath string, config *compileopts.Config, action func(string) error) error {
|
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
||||||
// Compile Go code to IR.
|
// Compile Go code to IR.
|
||||||
machine, err := compiler.NewTargetMachine(config)
|
machine, err := compiler.NewTargetMachine(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mod, extraFiles, extraLDFlags, errs := compiler.Compile(pkgName, machine, config)
|
buildOutput, errs := compiler.Compile(pkgName, machine, config)
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
return newMultiError(errs)
|
return newMultiError(errs)
|
||||||
}
|
}
|
||||||
|
mod := buildOutput.Mod
|
||||||
|
|
||||||
if config.Options.PrintIR {
|
if config.Options.PrintIR {
|
||||||
fmt.Println("; Generated LLVM IR:")
|
fmt.Println("; Generated LLVM IR:")
|
||||||
|
@ -196,7 +209,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile C files in packages.
|
// Compile C files in packages.
|
||||||
for i, file := range extraFiles {
|
for i, file := range buildOutput.ExtraFiles {
|
||||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o")
|
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o")
|
||||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
|
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -205,8 +218,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||||
ldflags = append(ldflags, outpath)
|
ldflags = append(ldflags, outpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(extraLDFlags) > 0 {
|
if len(buildOutput.ExtraLDFlags) > 0 {
|
||||||
ldflags = append(ldflags, extraLDFlags...)
|
ldflags = append(ldflags, buildOutput.ExtraLDFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link the object files together.
|
// Link the object files together.
|
||||||
|
@ -289,7 +302,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
|
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
|
||||||
}
|
}
|
||||||
return action(tmppath)
|
return action(BuildResult{
|
||||||
|
Binary: tmppath,
|
||||||
|
MainDir: buildOutput.MainDir,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,28 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
||||||
return machine, nil
|
return machine, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CompilerOutput is returned from the Compile() call. It contains the compile
|
||||||
|
// output and information necessary to continue to compile and link the program.
|
||||||
|
type CompilerOutput struct {
|
||||||
|
// The LLVM module that contains the compiled but not optimized LLVM module
|
||||||
|
// for all the Go code in the program.
|
||||||
|
Mod llvm.Module
|
||||||
|
|
||||||
|
// ExtraFiles is a list of C source files included in packages that should
|
||||||
|
// be built and linked together with the main executable to form one
|
||||||
|
// program. They can be used from CGo, for example.
|
||||||
|
ExtraFiles []string
|
||||||
|
|
||||||
|
// ExtraLDFlags are linker flags obtained during CGo processing. These flags
|
||||||
|
// must be passed to the linker which links the entire executable.
|
||||||
|
ExtraLDFlags []string
|
||||||
|
|
||||||
|
// MainDir is the absolute directory path to the directory of the main
|
||||||
|
// package. This is useful for testing: tests must be run in the package
|
||||||
|
// directory that is being tested.
|
||||||
|
MainDir string
|
||||||
|
}
|
||||||
|
|
||||||
// Compile the given package path or .go file path. Return an error when this
|
// Compile the given package path or .go file path. Return an error when this
|
||||||
// fails (in any stage). If successful it returns the LLVM module and a list of
|
// fails (in any stage). If successful it returns the LLVM module and a list of
|
||||||
// extra C files to be compiled. If not, one or more errors will be returned.
|
// extra C files to be compiled. If not, one or more errors will be returned.
|
||||||
|
@ -135,7 +157,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
||||||
// violation. Eventually, this Compile function should only compile a single
|
// violation. Eventually, this Compile function should only compile a single
|
||||||
// package and not the whole program, and loading of the program (including CGo
|
// package and not the whole program, and loading of the program (including CGo
|
||||||
// processing) should be moved outside the compiler package.
|
// processing) should be moved outside the compiler package.
|
||||||
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (mod llvm.Module, extrafiles []string, extraldflags []string, errors []error) {
|
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (output CompilerOutput, errors []error) {
|
||||||
c := &compilerContext{
|
c := &compilerContext{
|
||||||
Config: config,
|
Config: config,
|
||||||
difiles: make(map[string]llvm.Metadata),
|
difiles: make(map[string]llvm.Metadata),
|
||||||
|
@ -151,6 +173,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||||
if c.Debug() {
|
if c.Debug() {
|
||||||
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||||
}
|
}
|
||||||
|
output.Mod = c.mod
|
||||||
|
|
||||||
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
|
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
|
||||||
if c.targetData.PointerSize() <= 4 {
|
if c.targetData.PointerSize() <= 4 {
|
||||||
|
@ -176,20 +199,22 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||||
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
|
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
|
||||||
}})
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.mod, nil, nil, []error{err}
|
return output, []error{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = lprogram.Parse()
|
err = lprogram.Parse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.mod, nil, nil, []error{err}
|
return output, []error{err}
|
||||||
}
|
}
|
||||||
|
output.ExtraLDFlags = lprogram.LDFlags
|
||||||
|
output.MainDir = lprogram.MainPkg().Dir
|
||||||
|
|
||||||
c.ir = ir.NewProgram(lprogram)
|
c.ir = ir.NewProgram(lprogram)
|
||||||
|
|
||||||
// Run a simple dead code elimination pass.
|
// Run a simple dead code elimination pass.
|
||||||
err = c.ir.SimpleDCE()
|
err = c.ir.SimpleDCE()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.mod, nil, nil, []error{err}
|
return output, []error{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize debug information.
|
// Initialize debug information.
|
||||||
|
@ -328,14 +353,13 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather the list of (C) file paths that should be included in the build.
|
// Gather the list of (C) file paths that should be included in the build.
|
||||||
var extraFiles []string
|
|
||||||
for _, pkg := range c.ir.LoaderProgram.Sorted() {
|
for _, pkg := range c.ir.LoaderProgram.Sorted() {
|
||||||
for _, filename := range pkg.CFiles {
|
for _, filename := range pkg.CFiles {
|
||||||
extraFiles = append(extraFiles, filepath.Join(pkg.Dir, filename))
|
output.ExtraFiles = append(output.ExtraFiles, filepath.Join(pkg.Dir, filename))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.mod, extraFiles, lprogram.LDFlags, c.diagnostics
|
return output, c.diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||||
|
|
53
main.go
53
main.go
|
@ -90,10 +90,10 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, outpath, config, func(tmppath string) error {
|
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||||
if err := os.Rename(tmppath, outpath); err != nil {
|
if err := os.Rename(result.Binary, outpath); err != nil {
|
||||||
// Moving failed. Do a file copy.
|
// Moving failed. Do a file copy.
|
||||||
inf, err := os.Open(tmppath)
|
inf, err := os.Open(result.Binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -126,10 +126,11 @@ func Test(pkgName string, options *compileopts.Options) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, ".elf", config, func(tmppath string) error {
|
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
|
||||||
cmd := exec.Command(tmppath)
|
cmd := exec.Command(result.Binary)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Dir = result.MainDir
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Propagate the exit code
|
// Propagate the exit code
|
||||||
|
@ -139,7 +140,7 @@ func Test(pkgName string, options *compileopts.Options) error {
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
return &commandError{"failed to run compiled binary", tmppath, err}
|
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -183,7 +184,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||||
return errors.New("unknown flash method: " + flashMethod)
|
return errors.New("unknown flash method: " + flashMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, fileExt, config, func(tmppath string) error {
|
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||||
// do we need port reset to put MCU into bootloader mode?
|
// do we need port reset to put MCU into bootloader mode?
|
||||||
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
||||||
if port == "" {
|
if port == "" {
|
||||||
|
@ -196,7 +197,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||||
|
|
||||||
err := touchSerialPortAt1200bps(port)
|
err := touchSerialPortAt1200bps(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to reset port", tmppath, err}
|
return &commandError{"failed to reset port", result.Binary, err}
|
||||||
}
|
}
|
||||||
// give the target MCU a chance to restart into bootloader
|
// give the target MCU a chance to restart into bootloader
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
|
@ -208,7 +209,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||||
// Create the command.
|
// Create the command.
|
||||||
flashCmd := config.Target.FlashCommand
|
flashCmd := config.Target.FlashCommand
|
||||||
fileToken := "{" + fileExt[1:] + "}"
|
fileToken := "{" + fileExt[1:] + "}"
|
||||||
flashCmd = strings.Replace(flashCmd, fileToken, tmppath, -1)
|
flashCmd = strings.Replace(flashCmd, fileToken, result.Binary, -1)
|
||||||
|
|
||||||
if port == "" && strings.Contains(flashCmd, "{port}") {
|
if port == "" && strings.Contains(flashCmd, "{port}") {
|
||||||
var err error
|
var err error
|
||||||
|
@ -238,21 +239,21 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
cmd.Dir = goenv.Get("TINYGOROOT")
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to flash", tmppath, err}
|
return &commandError{"failed to flash", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case "msd":
|
case "msd":
|
||||||
switch fileExt {
|
switch fileExt {
|
||||||
case ".uf2":
|
case ".uf2":
|
||||||
err := flashUF2UsingMSD(config.Target.FlashVolume, tmppath)
|
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to flash", tmppath, err}
|
return &commandError{"failed to flash", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case ".hex":
|
case ".hex":
|
||||||
err := flashHexUsingMSD(config.Target.FlashVolume, tmppath)
|
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to flash", tmppath, err}
|
return &commandError{"failed to flash", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
|
@ -263,13 +264,13 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
args = append(args, "-c", "program "+filepath.ToSlash(tmppath)+" reset exit")
|
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
|
||||||
cmd := exec.Command("openocd", args...)
|
cmd := exec.Command("openocd", args...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to flash", tmppath, err}
|
return &commandError{"failed to flash", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
|
@ -294,7 +295,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||||
return errors.New("gdb not configured in the target specification")
|
return errors.New("gdb not configured in the target specification")
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, "", config, func(tmppath string) error {
|
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
|
||||||
// Find a good way to run GDB.
|
// Find a good way to run GDB.
|
||||||
gdbInterface, openocdInterface := config.Programmer()
|
gdbInterface, openocdInterface := config.Programmer()
|
||||||
switch gdbInterface {
|
switch gdbInterface {
|
||||||
|
@ -357,7 +358,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||||
gdbCommands = append(gdbCommands, "target remote :1234")
|
gdbCommands = append(gdbCommands, "target remote :1234")
|
||||||
|
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
args := append(config.Target.Emulator[1:], tmppath, "-s", "-S")
|
args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S")
|
||||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||||
daemon.Stdout = os.Stdout
|
daemon.Stdout = os.Stdout
|
||||||
daemon.Stderr = os.Stderr
|
daemon.Stderr = os.Stderr
|
||||||
|
@ -365,7 +366,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||||
gdbCommands = append(gdbCommands, "target remote :2345")
|
gdbCommands = append(gdbCommands, "target remote :2345")
|
||||||
|
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
args := append(config.Target.Emulator[1:], tmppath, "-g")
|
args := append(config.Target.Emulator[1:], result.Binary, "-g")
|
||||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||||
daemon.Stdout = os.Stdout
|
daemon.Stdout = os.Stdout
|
||||||
daemon.Stderr = os.Stderr
|
daemon.Stderr = os.Stderr
|
||||||
|
@ -403,7 +404,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||||
// Construct and execute a gdb command.
|
// Construct and execute a gdb command.
|
||||||
// By default: gdb -ex run <binary>
|
// By default: gdb -ex run <binary>
|
||||||
// Exit GDB with Ctrl-D.
|
// Exit GDB with Ctrl-D.
|
||||||
params := []string{tmppath}
|
params := []string{result.Binary}
|
||||||
for _, cmd := range gdbCommands {
|
for _, cmd := range gdbCommands {
|
||||||
params = append(params, "-ex", cmd)
|
params = append(params, "-ex", cmd)
|
||||||
}
|
}
|
||||||
|
@ -413,7 +414,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to run gdb with", tmppath, err}
|
return &commandError{"failed to run gdb with", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -429,10 +430,10 @@ func Run(pkgName string, options *compileopts.Options) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, ".elf", config, func(tmppath string) error {
|
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
|
||||||
if len(config.Target.Emulator) == 0 {
|
if len(config.Target.Emulator) == 0 {
|
||||||
// Run directly.
|
// Run directly.
|
||||||
cmd := exec.Command(tmppath)
|
cmd := exec.Command(result.Binary)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
|
@ -441,12 +442,12 @@ func Run(pkgName string, options *compileopts.Options) error {
|
||||||
// Workaround for QEMU which always exits with an error.
|
// Workaround for QEMU which always exits with an error.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &commandError{"failed to run compiled binary", tmppath, err}
|
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
args := append(config.Target.Emulator[1:], tmppath)
|
args := append(config.Target.Emulator[1:], result.Binary)
|
||||||
cmd := exec.Command(config.Target.Emulator[0], args...)
|
cmd := exec.Command(config.Target.Emulator[0], args...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
@ -456,7 +457,7 @@ func Run(pkgName string, options *compileopts.Options) error {
|
||||||
// Workaround for QEMU which always exits with an error.
|
// Workaround for QEMU which always exits with an error.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &commandError{"failed to run emulator with", tmppath, err}
|
return &commandError{"failed to run emulator with", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче