test: print package name when compilation failed

Before this patch, a compile error would prevent the 'ok' or 'FAIL' line
to be printed. That's unexpected. This patch changes the code in such a
way that it's obvious a test result line is printed in all cases.

To be able to also print the package name, I had to make sure the build
result is passed through everywhere even on all the failure paths. This
results in a bit of churn, but it's all relatively straightforward.

Found while working on Go 1.20.
Этот коммит содержится в:
Ayke van Laethem 2023-01-14 17:16:42 +01:00 коммит произвёл Ron Evans
родитель 911ce3a4bc
коммит 80077ef276
3 изменённых файлов: 67 добавлений и 72 удалений

Просмотреть файл

@ -190,12 +190,21 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{ lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine), Sizes: compiler.Sizes(machine),
}) })
if err != nil { result := BuildResult{
return BuildResult{}, err ModuleRoot: lprogram.MainPkg().Module.Dir,
MainDir: lprogram.MainPkg().Dir,
ImportPath: lprogram.MainPkg().ImportPath,
}
if result.ModuleRoot == "" {
// If there is no module root, just the regular root.
result.ModuleRoot = lprogram.MainPkg().Root
}
if err != nil { // failed to load AST
return result, err
} }
err = lprogram.Parse() err = lprogram.Parse()
if err != nil { if err != nil {
return BuildResult{}, err return result, err
} }
// Create the *ssa.Program. This does not yet build the entire SSA of the // Create the *ssa.Program. This does not yet build the entire SSA of the
@ -278,7 +287,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
for _, imported := range pkg.Pkg.Imports() { for _, imported := range pkg.Pkg.Imports() {
job, ok := packageActionIDJobs[imported.Path()] job, ok := packageActionIDJobs[imported.Path()]
if !ok { if !ok {
return BuildResult{}, fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path()) return result, fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
} }
importedPackages = append(importedPackages, job) importedPackages = append(importedPackages, job)
actionIDDependencies = append(actionIDDependencies, job) actionIDDependencies = append(actionIDDependencies, job)
@ -573,17 +582,17 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Run jobs to produce the LLVM module. // Run jobs to produce the LLVM module.
err := runJobs(programJob, config.Options.Semaphore) err := runJobs(programJob, config.Options.Semaphore)
if err != nil { if err != nil {
return BuildResult{}, err return result, err
} }
// Generate output. // Generate output.
switch outext { switch outext {
case ".o": case ".o":
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile) llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil { if err != nil {
return BuildResult{}, err return result, err
} }
defer llvmBuf.Dispose() defer llvmBuf.Dispose()
return BuildResult{}, os.WriteFile(outpath, llvmBuf.Bytes(), 0666) return result, os.WriteFile(outpath, llvmBuf.Bytes(), 0666)
case ".bc": case ".bc":
var buf llvm.MemoryBuffer var buf llvm.MemoryBuffer
if config.UseThinLTO() { if config.UseThinLTO() {
@ -592,10 +601,10 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
buf = llvm.WriteBitcodeToMemoryBuffer(mod) buf = llvm.WriteBitcodeToMemoryBuffer(mod)
} }
defer buf.Dispose() defer buf.Dispose()
return BuildResult{}, os.WriteFile(outpath, buf.Bytes(), 0666) return result, os.WriteFile(outpath, buf.Bytes(), 0666)
case ".ll": case ".ll":
data := []byte(mod.String()) data := []byte(mod.String())
return BuildResult{}, os.WriteFile(outpath, data, 0666) return result, os.WriteFile(outpath, data, 0666)
default: default:
panic("unreachable") panic("unreachable")
} }
@ -629,19 +638,19 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Prepare link command. // Prepare link command.
linkerDependencies := []*compileJob{outputObjectFileJob} linkerDependencies := []*compileJob{outputObjectFileJob}
executable := filepath.Join(tmpdir, "main") result.Executable = filepath.Join(tmpdir, "main")
if config.GOOS() == "windows" { if config.GOOS() == "windows" {
executable += ".exe" result.Executable += ".exe"
} }
tmppath := executable // final file result.Binary = result.Executable // final file
ldflags := append(config.LDFlags(), "-o", executable) ldflags := append(config.LDFlags(), "-o", result.Executable)
// Add compiler-rt dependency if needed. Usually this is a simple load from // Add compiler-rt dependency if needed. Usually this is a simple load from
// a cache. // a cache.
if config.Target.RTLib == "compiler-rt" { if config.Target.RTLib == "compiler-rt" {
job, unlock, err := CompilerRT.load(config, tmpdir) job, unlock, err := CompilerRT.load(config, tmpdir)
if err != nil { if err != nil {
return BuildResult{}, err return result, err
} }
defer unlock() defer unlock()
linkerDependencies = append(linkerDependencies, job) linkerDependencies = append(linkerDependencies, job)
@ -716,7 +725,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
ldflags = append(ldflags, "--strip-debug") ldflags = append(ldflags, "--strip-debug")
} else { } else {
// Other linkers may have different flags. // Other linkers may have different flags.
return BuildResult{}, errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker) return result, errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker)
} }
} }
@ -768,7 +777,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
} }
err = link(config.Target.Linker, ldflags...) err = link(config.Target.Linker, ldflags...)
if err != nil { if err != nil {
return &commandError{"failed to link", executable, err} return &commandError{"failed to link", result.Executable, err}
} }
var calculatedStacks []string var calculatedStacks []string
@ -777,7 +786,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Try to determine stack sizes at compile time. // Try to determine stack sizes at compile time.
// Don't do this by default as it usually doesn't work on // Don't do this by default as it usually doesn't work on
// unsupported architectures. // unsupported architectures.
calculatedStacks, stackSizes, err = determineStackSizes(mod, executable) calculatedStacks, stackSizes, err = determineStackSizes(mod, result.Executable)
if err != nil { if err != nil {
return err return err
} }
@ -787,14 +796,14 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if config.AutomaticStackSize() { if config.AutomaticStackSize() {
// Modify the .tinygo_stacksizes section that contains a stack size // Modify the .tinygo_stacksizes section that contains a stack size
// for each goroutine. // for each goroutine.
err = modifyStackSizes(executable, stackSizeLoads, stackSizes) err = modifyStackSizes(result.Executable, stackSizeLoads, stackSizes)
if err != nil { if err != nil {
return fmt.Errorf("could not modify stack sizes: %w", err) return fmt.Errorf("could not modify stack sizes: %w", err)
} }
} }
if config.RP2040BootPatch() { if config.RP2040BootPatch() {
// Patch the second stage bootloader CRC into the .boot2 section // Patch the second stage bootloader CRC into the .boot2 section
err = patchRP2040BootCRC(executable) err = patchRP2040BootCRC(result.Executable)
if err != nil { if err != nil {
return fmt.Errorf("could not patch RP2040 second stage boot loader: %w", err) return fmt.Errorf("could not patch RP2040 second stage boot loader: %w", err)
} }
@ -827,8 +836,8 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
args = append(args, args = append(args,
opt, opt,
"-g", "-g",
executable, result.Executable,
"--output", executable, "--output", result.Executable,
) )
cmd := exec.Command(goenv.Get("WASMOPT"), args...) cmd := exec.Command(goenv.Get("WASMOPT"), args...)
@ -847,7 +856,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
for _, pkg := range lprogram.Sorted() { for _, pkg := range lprogram.Sorted() {
packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path() packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
} }
sizes, err := loadProgramSize(executable, packagePathMap) sizes, err := loadProgramSize(result.Executable, packagePathMap)
if err != nil { if err != nil {
return err return err
} }
@ -883,7 +892,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// is simpler and cannot be parallelized. // is simpler and cannot be parallelized.
err = runJobs(linkJob, config.Options.Semaphore) err = runJobs(linkJob, config.Options.Semaphore)
if err != nil { if err != nil {
return BuildResult{}, err return result, err
} }
// Get an Intel .hex file or .bin file from the .elf file. // Get an Intel .hex file or .bin file from the .elf file.
@ -894,56 +903,43 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
case "hex", "bin": case "hex", "bin":
// Extract raw binary, either encoding it as a hex file or as a raw // Extract raw binary, either encoding it as a hex file or as a raw
// firmware file. // firmware file.
tmppath = filepath.Join(tmpdir, "main"+outext) result.Binary = filepath.Join(tmpdir, "main"+outext)
err := objcopy(executable, tmppath, outputBinaryFormat) err := objcopy(result.Executable, result.Binary, outputBinaryFormat)
if err != nil { if err != nil {
return BuildResult{}, err return result, err
} }
case "uf2": case "uf2":
// Get UF2 from the .elf file. // Get UF2 from the .elf file.
tmppath = filepath.Join(tmpdir, "main"+outext) result.Binary = filepath.Join(tmpdir, "main"+outext)
err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID) err := convertELFFileToUF2File(result.Executable, result.Binary, config.Target.UF2FamilyID)
if err != nil { if err != nil {
return BuildResult{}, err return result, err
} }
case "esp32", "esp32-img", "esp32c3", "esp8266": case "esp32", "esp32-img", "esp32c3", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM // Special format for the ESP family of chips (parsed by the ROM
// bootloader). // bootloader).
tmppath = filepath.Join(tmpdir, "main"+outext) result.Binary = filepath.Join(tmpdir, "main"+outext)
err := makeESPFirmareImage(executable, tmppath, outputBinaryFormat) err := makeESPFirmareImage(result.Executable, result.Binary, outputBinaryFormat)
if err != nil { if err != nil {
return BuildResult{}, err return result, err
} }
case "nrf-dfu": case "nrf-dfu":
// special format for nrfutil for Nordic chips // special format for nrfutil for Nordic chips
tmphexpath := filepath.Join(tmpdir, "main.hex") tmphexpath := filepath.Join(tmpdir, "main.hex")
err := objcopy(executable, tmphexpath, "hex") err := objcopy(result.Executable, tmphexpath, "hex")
if err != nil { if err != nil {
return BuildResult{}, err return result, err
} }
tmppath = filepath.Join(tmpdir, "main"+outext) result.Binary = filepath.Join(tmpdir, "main"+outext)
err = makeDFUFirmwareImage(config.Options, tmphexpath, tmppath) err = makeDFUFirmwareImage(config.Options, tmphexpath, result.Binary)
if err != nil { if err != nil {
return BuildResult{}, err return result, err
} }
default: default:
return BuildResult{}, fmt.Errorf("unknown output binary format: %s", outputBinaryFormat) return result, fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
} }
// If there's a module root, use that. return result, nil
moduleroot := lprogram.MainPkg().Module.Dir
if moduleroot == "" {
// if not, just the regular root
moduleroot = lprogram.MainPkg().Root
}
return BuildResult{
Executable: executable,
Binary: tmppath,
MainDir: lprogram.MainPkg().Dir,
ModuleRoot: moduleroot,
ImportPath: lprogram.MainPkg().ImportPath,
}, nil
} }
// createEmbedObjectFile creates a new object file with the given contents, for // createEmbedObjectFile creates a new object file with the given contents, for

37
main.go
Просмотреть файл

@ -246,7 +246,8 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
buf := bytes.Buffer{} buf := bytes.Buffer{}
passed := false passed := false
err = buildAndRun(pkgName, config, &buf, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error { var duration time.Duration
result, err := buildAndRun(pkgName, config, &buf, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
if testCompileOnly || outpath != "" { if testCompileOnly || outpath != "" {
// Write test binary to the specified file name. // Write test binary to the specified file name.
if outpath == "" { if outpath == "" {
@ -312,10 +313,7 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
// Run the test. // Run the test.
start := time.Now() start := time.Now()
err = cmd.Run() err = cmd.Run()
duration := time.Since(start) duration = time.Since(start)
// Print the result.
importPath := strings.TrimSuffix(result.ImportPath, ".test")
passed = err == nil passed = err == nil
// print the test output if // print the test output if
@ -326,23 +324,23 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
buf.WriteTo(stdout) buf.WriteTo(stdout)
} }
// final status line
if passed {
fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
} else {
fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
}
if _, ok := err.(*exec.ExitError); ok { if _, ok := err.(*exec.ExitError); ok {
// Binary exited with a non-zero exit code, which means the test // Binary exited with a non-zero exit code, which means the test
// failed. // failed. Return nil to avoid printing a useless "exited with
// error" error message.
return nil return nil
} }
return err return err
}) })
importPath := strings.TrimSuffix(result.ImportPath, ".test")
if err, ok := err.(loader.NoTestFilesError); ok { if err, ok := err.(loader.NoTestFilesError); ok {
fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath) fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath)
// Pretend the test passed - it at least didn't fail. // Pretend the test passed - it at least didn't fail.
return true, nil return true, nil
} else if passed {
fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
} else {
fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
} }
return passed, err return passed, err
} }
@ -765,16 +763,17 @@ func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
return err return err
} }
return buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error { _, err = buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
return cmd.Run() return cmd.Run()
}) })
return err
} }
// buildAndRun builds and runs the given program, writing output to stdout and // buildAndRun builds and runs the given program, writing output to stdout and
// errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and // errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and
// passes command line arguments and evironment variables in a way appropriate // passes command line arguments and evironment variables in a way appropriate
// for the given emulator. // for the given emulator.
func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) error { func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) (builder.BuildResult, error) {
// Determine whether we're on a system that supports environment variables // Determine whether we're on a system that supports environment variables
// and command line parameters (operating systems, WASI) or not (baremetal, // and command line parameters (operating systems, WASI) or not (baremetal,
// WebAssembly in the browser). If we're on a system without an environment, // WebAssembly in the browser). If we're on a system without an environment,
@ -828,7 +827,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
// Create a temporary directory for intermediary files. // Create a temporary directory for intermediary files.
tmpdir, err := os.MkdirTemp("", "tinygo") tmpdir, err := os.MkdirTemp("", "tinygo")
if err != nil { if err != nil {
return err return builder.BuildResult{}, err
} }
if !config.Options.Work { if !config.Options.Work {
defer os.RemoveAll(tmpdir) defer os.RemoveAll(tmpdir)
@ -838,7 +837,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
format, fileExt := config.EmulatorFormat() format, fileExt := config.EmulatorFormat()
result, err := builder.Build(pkgName, fileExt, tmpdir, config) result, err := builder.Build(pkgName, fileExt, tmpdir, config)
if err != nil { if err != nil {
return err return result, err
} }
// If needed, set a timeout on the command. This is done in tests so // If needed, set a timeout on the command. This is done in tests so
@ -857,7 +856,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
} else { } else {
emulator, err := config.Emulator(format, result.Binary) emulator, err := config.Emulator(format, result.Binary)
if err != nil { if err != nil {
return err return result, err
} }
name = emulator[0] name = emulator[0]
emuArgs := append([]string(nil), emulator[1:]...) emuArgs := append([]string(nil), emulator[1:]...)
@ -898,9 +897,9 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
stdout.Write([]byte(fmt.Sprintf("--- timeout of %s exceeded, terminating...\n", timeout))) stdout.Write([]byte(fmt.Sprintf("--- timeout of %s exceeded, terminating...\n", timeout)))
err = ctx.Err() err = ctx.Err()
} }
return &commandError{"failed to run compiled binary", result.Binary, err} return result, &commandError{"failed to run compiled binary", result.Binary, err}
} }
return nil return result, nil
} }
func touchSerialPortAt1200bps(port string) (err error) { func touchSerialPortAt1200bps(port string) (err error) {

Просмотреть файл

@ -329,7 +329,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
// Build the test binary. // Build the test binary.
stdout := &bytes.Buffer{} stdout := &bytes.Buffer{}
err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error { _, err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
return cmd.Run() return cmd.Run()
}) })
if err != nil { if err != nil {