From 88fd2823dff2d79c3d2447bb30fa5c8ea262b700 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 4 Sep 2020 00:25:11 +0200 Subject: [PATCH] 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. --- builder/build.go | 28 ++++++++++++++++++----- compiler/compiler.go | 38 +++++++++++++++++++++++++------ main.go | 53 ++++++++++++++++++++++---------------------- 3 files changed, 80 insertions(+), 39 deletions(-) diff --git a/builder/build.go b/builder/build.go index a7995ddf..b26fff25 100644 --- a/builder/build.go +++ b/builder/build.go @@ -24,22 +24,35 @@ import ( "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 // name, an output path, and set of compile options and from that it manages the // whole compilation process. // // The error value may be of type *MultiError. Callers will likely want to check // 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. machine, err := compiler.NewTargetMachine(config) if err != nil { return err } - mod, extraFiles, extraLDFlags, errs := compiler.Compile(pkgName, machine, config) + buildOutput, errs := compiler.Compile(pkgName, machine, config) if errs != nil { return newMultiError(errs) } + mod := buildOutput.Mod if config.Options.PrintIR { 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. - for i, file := range extraFiles { + for i, file := range buildOutput.ExtraFiles { outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o") err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...) if err != nil { @@ -205,8 +218,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri ldflags = append(ldflags, outpath) } - if len(extraLDFlags) > 0 { - ldflags = append(ldflags, extraLDFlags...) + if len(buildOutput.ExtraLDFlags) > 0 { + ldflags = append(ldflags, buildOutput.ExtraLDFlags...) } // Link the object files together. @@ -289,7 +302,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri default: return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat) } - return action(tmppath) + return action(BuildResult{ + Binary: tmppath, + MainDir: buildOutput.MainDir, + }) } } diff --git a/compiler/compiler.go b/compiler/compiler.go index 3bc35d02..5fd46cd9 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -127,6 +127,28 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) { 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 // 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. @@ -135,7 +157,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) { // violation. Eventually, this Compile function should only compile a single // package and not the whole program, and loading of the program (including CGo // 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{ Config: config, difiles: make(map[string]llvm.Metadata), @@ -151,6 +173,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con if c.Debug() { c.dibuilder = llvm.NewDIBuilder(c.mod) } + output.Mod = c.mod c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8) 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)), }}) if err != nil { - return c.mod, nil, nil, []error{err} + return output, []error{err} } err = lprogram.Parse() 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) // Run a simple dead code elimination pass. err = c.ir.SimpleDCE() if err != nil { - return c.mod, nil, nil, []error{err} + return output, []error{err} } // 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. - var extraFiles []string for _, pkg := range c.ir.LoaderProgram.Sorted() { 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 diff --git a/main.go b/main.go index 7906c47f..218f5507 100644 --- a/main.go +++ b/main.go @@ -90,10 +90,10 @@ func Build(pkgName, outpath string, options *compileopts.Options) error { return err } - return builder.Build(pkgName, outpath, config, func(tmppath string) error { - if err := os.Rename(tmppath, outpath); err != nil { + return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error { + if err := os.Rename(result.Binary, outpath); err != nil { // Moving failed. Do a file copy. - inf, err := os.Open(tmppath) + inf, err := os.Open(result.Binary) if err != nil { return err } @@ -126,10 +126,11 @@ func Test(pkgName string, options *compileopts.Options) error { return err } - return builder.Build(pkgName, ".elf", config, func(tmppath string) error { - cmd := exec.Command(tmppath) + return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error { + cmd := exec.Command(result.Binary) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + cmd.Dir = result.MainDir err := cmd.Run() if err != nil { // Propagate the exit code @@ -139,7 +140,7 @@ func Test(pkgName string, options *compileopts.Options) error { } os.Exit(1) } - return &commandError{"failed to run compiled binary", tmppath, err} + return &commandError{"failed to run compiled binary", result.Binary, err} } return nil }) @@ -183,7 +184,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error { 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? if config.Target.PortReset == "true" && flashMethod != "openocd" { if port == "" { @@ -196,7 +197,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error { err := touchSerialPortAt1200bps(port) 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 time.Sleep(3 * time.Second) @@ -208,7 +209,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error { // Create the command. flashCmd := config.Target.FlashCommand fileToken := "{" + fileExt[1:] + "}" - flashCmd = strings.Replace(flashCmd, fileToken, tmppath, -1) + flashCmd = strings.Replace(flashCmd, fileToken, result.Binary, -1) if port == "" && strings.Contains(flashCmd, "{port}") { var err error @@ -238,21 +239,21 @@ func Flash(pkgName, port string, options *compileopts.Options) error { cmd.Dir = goenv.Get("TINYGOROOT") err := cmd.Run() if err != nil { - return &commandError{"failed to flash", tmppath, err} + return &commandError{"failed to flash", result.Binary, err} } return nil case "msd": switch fileExt { case ".uf2": - err := flashUF2UsingMSD(config.Target.FlashVolume, tmppath) + err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary) if err != nil { - return &commandError{"failed to flash", tmppath, err} + return &commandError{"failed to flash", result.Binary, err} } return nil case ".hex": - err := flashHexUsingMSD(config.Target.FlashVolume, tmppath) + err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary) if err != nil { - return &commandError{"failed to flash", tmppath, err} + return &commandError{"failed to flash", result.Binary, err} } return nil default: @@ -263,13 +264,13 @@ func Flash(pkgName, port string, options *compileopts.Options) error { if err != nil { 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.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() if err != nil { - return &commandError{"failed to flash", tmppath, err} + return &commandError{"failed to flash", result.Binary, err} } return nil 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 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. gdbInterface, openocdInterface := config.Programmer() switch gdbInterface { @@ -357,7 +358,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro gdbCommands = append(gdbCommands, "target remote :1234") // 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.Stdout = os.Stdout daemon.Stderr = os.Stderr @@ -365,7 +366,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro gdbCommands = append(gdbCommands, "target remote :2345") // 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.Stdout = os.Stdout daemon.Stderr = os.Stderr @@ -403,7 +404,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro // Construct and execute a gdb command. // By default: gdb -ex run // Exit GDB with Ctrl-D. - params := []string{tmppath} + params := []string{result.Binary} for _, cmd := range gdbCommands { params = append(params, "-ex", cmd) } @@ -413,7 +414,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { - return &commandError{"failed to run gdb with", tmppath, err} + return &commandError{"failed to run gdb with", result.Binary, err} } return nil }) @@ -429,10 +430,10 @@ func Run(pkgName string, options *compileopts.Options) error { 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 { // Run directly. - cmd := exec.Command(tmppath) + cmd := exec.Command(result.Binary) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() @@ -441,12 +442,12 @@ func Run(pkgName string, options *compileopts.Options) error { // Workaround for QEMU which always exits with an error. return nil } - return &commandError{"failed to run compiled binary", tmppath, err} + return &commandError{"failed to run compiled binary", result.Binary, err} } return nil } else { // 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.Stdout = os.Stdout 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. return nil } - return &commandError{"failed to run emulator with", tmppath, err} + return &commandError{"failed to run emulator with", result.Binary, err} } return nil }