loader: rewrite/refactor much of the code to use go list directly
There were a few problems with the go/packages package. While it is more or less designed for our purpose, it didn't work quite well as it didn't provide access to indirectly imported packages (most importantly the runtime package). This led to a workaround that sometimes broke `tinygo test`. This PR contains a number of related changes: * It uses `go list` directly to retrieve the list of packages/files to compile, instead of relying on the go/packages package. * It replaces our custom TestMain replace code with the standard code for running tests (generated by `go list`). * It adds a dummy runtime/pprof package and modifies the testing package, to get tests to run again with the code generated by `go list`.
Этот коммит содержится в:
родитель
51238fba50
коммит
c810628a20
11 изменённых файлов: 322 добавлений и 347 удалений
|
@ -5,18 +5,15 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
|
||||||
"github.com/tinygo-org/tinygo/ir"
|
"github.com/tinygo-org/tinygo/ir"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
|
@ -172,40 +169,12 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||||
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
||||||
dummyFunc.EraseFromParentAsFunction()
|
dummyFunc.EraseFromParentAsFunction()
|
||||||
|
|
||||||
wd, err := os.Getwd()
|
lprogram, err := loader.Load(c.Config, []string{pkgName}, c.ClangHeaders, types.Config{
|
||||||
if err != nil {
|
|
||||||
return c.mod, nil, nil, []error{err}
|
|
||||||
}
|
|
||||||
goroot, err := loader.GetCachedGoroot(c.Config)
|
|
||||||
if err != nil {
|
|
||||||
return c.mod, nil, nil, []error{err}
|
|
||||||
}
|
|
||||||
lprogram := &loader.Program{
|
|
||||||
Build: &build.Context{
|
|
||||||
GOARCH: c.GOARCH(),
|
|
||||||
GOOS: c.GOOS(),
|
|
||||||
GOROOT: goroot,
|
|
||||||
GOPATH: goenv.Get("GOPATH"),
|
|
||||||
CgoEnabled: c.CgoEnabled(),
|
|
||||||
UseAllFiles: false,
|
|
||||||
Compiler: "gc", // must be one of the recognized compilers
|
|
||||||
BuildTags: c.BuildTags(),
|
|
||||||
},
|
|
||||||
Tests: c.TestConfig.CompileTestBinary,
|
|
||||||
TypeChecker: types.Config{
|
|
||||||
Sizes: &stdSizes{
|
Sizes: &stdSizes{
|
||||||
IntSize: int64(c.targetData.TypeAllocSize(c.intType)),
|
IntSize: int64(c.targetData.TypeAllocSize(c.intType)),
|
||||||
PtrSize: int64(c.targetData.PointerSize()),
|
PtrSize: int64(c.targetData.PointerSize()),
|
||||||
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
|
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
|
||||||
},
|
}})
|
||||||
},
|
|
||||||
Dir: wd,
|
|
||||||
TINYGOROOT: goenv.Get("TINYGOROOT"),
|
|
||||||
CFlags: c.CFlags(),
|
|
||||||
ClangHeaders: c.ClangHeaders,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = lprogram.Load(pkgName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.mod, nil, nil, []error{err}
|
return c.mod, nil, nil, []error{err}
|
||||||
}
|
}
|
||||||
|
@ -361,11 +330,8 @@ 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
|
var extraFiles []string
|
||||||
for _, pkg := range c.ir.LoaderProgram.Sorted() {
|
for _, pkg := range c.ir.LoaderProgram.Sorted() {
|
||||||
for _, file := range pkg.OtherFiles {
|
for _, filename := range pkg.CFiles {
|
||||||
switch strings.ToLower(filepath.Ext(file)) {
|
extraFiles = append(extraFiles, filepath.Join(pkg.Dir, filename))
|
||||||
case ".c":
|
|
||||||
extraFiles = append(extraFiles, file)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
ir/ir.go
4
ir/ir.go
|
@ -67,7 +67,7 @@ func NewProgram(lprogram *loader.Program) *Program {
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
program.Build()
|
program.Build()
|
||||||
|
|
||||||
mainPkg := program.ImportedPackage(lprogram.MainPkg.PkgPath)
|
mainPkg := program.ImportedPackage(lprogram.MainPkg().ImportPath)
|
||||||
if mainPkg == nil {
|
if mainPkg == nil {
|
||||||
panic("could not find main package")
|
panic("could not find main package")
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ func NewProgram(lprogram *loader.Program) *Program {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
p.AddPackage(program.ImportedPackage(pkg.PkgPath))
|
p.AddPackage(program.ImportedPackage(pkg.ImportPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package loader
|
package loader
|
||||||
|
|
||||||
|
import "go/scanner"
|
||||||
|
|
||||||
// Errors contains a list of parser errors or a list of typechecker errors for
|
// Errors contains a list of parser errors or a list of typechecker errors for
|
||||||
// the given package.
|
// the given package.
|
||||||
type Errors struct {
|
type Errors struct {
|
||||||
|
@ -10,3 +12,14 @@ type Errors struct {
|
||||||
func (e Errors) Error() string {
|
func (e Errors) Error() string {
|
||||||
return "could not compile: " + e.Errs[0].Error()
|
return "could not compile: " + e.Errs[0].Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error is a regular error but with an added import stack. This is especially
|
||||||
|
// useful for debugging import cycle errors.
|
||||||
|
type Error struct {
|
||||||
|
ImportStack []string
|
||||||
|
Err scanner.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Error() string {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
|
@ -212,7 +212,7 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
|
||||||
"reflect/": false,
|
"reflect/": false,
|
||||||
"runtime/": false,
|
"runtime/": false,
|
||||||
"sync/": true,
|
"sync/": true,
|
||||||
"testing/": false,
|
"testing/": true,
|
||||||
}
|
}
|
||||||
if needsSyscallPackage {
|
if needsSyscallPackage {
|
||||||
paths["syscall/"] = true // include syscall/js
|
paths["syscall/"] = true // include syscall/js
|
||||||
|
|
30
loader/list.go
Обычный файл
30
loader/list.go
Обычный файл
|
@ -0,0 +1,30 @@
|
||||||
|
package loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List returns a ready-to-run *exec.Cmd for running the `go list` command with
|
||||||
|
// the configuration used for TinyGo.
|
||||||
|
func List(config *compileopts.Config, extraArgs, pkgs []string) (*exec.Cmd, error) {
|
||||||
|
goroot, err := GetCachedGoroot(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
args := append([]string{"list"}, extraArgs...)
|
||||||
|
if len(config.BuildTags()) != 0 {
|
||||||
|
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
|
||||||
|
}
|
||||||
|
args = append(args, pkgs...)
|
||||||
|
cgoEnabled := "0"
|
||||||
|
if config.CgoEnabled() {
|
||||||
|
cgoEnabled = "1"
|
||||||
|
}
|
||||||
|
cmd := exec.Command("go", args...)
|
||||||
|
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED="+cgoEnabled)
|
||||||
|
return cmd, nil
|
||||||
|
}
|
395
loader/loader.go
395
loader/loader.go
|
@ -2,124 +2,147 @@ package loader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"syscall"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/cgo"
|
"github.com/tinygo-org/tinygo/cgo"
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"golang.org/x/tools/go/packages"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Program holds all packages and some metadata about the program as a whole.
|
// Program holds all packages and some metadata about the program as a whole.
|
||||||
type Program struct {
|
type Program struct {
|
||||||
Build *build.Context
|
config *compileopts.Config
|
||||||
Tests bool
|
clangHeaders string
|
||||||
|
typeChecker types.Config
|
||||||
|
goroot string // synthetic GOROOT
|
||||||
|
workingDir string
|
||||||
|
|
||||||
Packages map[string]*Package
|
Packages map[string]*Package
|
||||||
MainPkg *Package
|
|
||||||
sorted []*Package
|
sorted []*Package
|
||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
TypeChecker types.Config
|
|
||||||
Dir string // current working directory (for error reporting)
|
// Information obtained during parsing.
|
||||||
TINYGOROOT string // root of the TinyGo installation or root of the source code
|
|
||||||
CFlags []string
|
|
||||||
LDFlags []string
|
LDFlags []string
|
||||||
ClangHeaders string
|
}
|
||||||
|
|
||||||
|
// PackageJSON is a subset of the JSON struct returned from `go list`.
|
||||||
|
type PackageJSON struct {
|
||||||
|
Dir string
|
||||||
|
ImportPath string
|
||||||
|
ForTest string
|
||||||
|
|
||||||
|
// Source files
|
||||||
|
GoFiles []string
|
||||||
|
CgoFiles []string
|
||||||
|
CFiles []string
|
||||||
|
|
||||||
|
// Dependency information
|
||||||
|
Imports []string
|
||||||
|
ImportMap map[string]string
|
||||||
|
|
||||||
|
// Error information
|
||||||
|
Error *struct {
|
||||||
|
ImportStack []string
|
||||||
|
Pos string
|
||||||
|
Err string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package holds a loaded package, its imports, and its parsed files.
|
// Package holds a loaded package, its imports, and its parsed files.
|
||||||
type Package struct {
|
type Package struct {
|
||||||
*Program
|
PackageJSON
|
||||||
*packages.Package
|
|
||||||
|
program *Program
|
||||||
Files []*ast.File
|
Files []*ast.File
|
||||||
Pkg *types.Package
|
Pkg *types.Package
|
||||||
types.Info
|
info types.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads the given package with all dependencies (including the runtime
|
// Load loads the given package with all dependencies (including the runtime
|
||||||
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
||||||
// processing, if necessary).
|
// processing, if necessary).
|
||||||
func (p *Program) Load(importPath string) error {
|
func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
||||||
if p.Packages == nil {
|
goroot, err := GetCachedGoroot(config)
|
||||||
p.Packages = make(map[string]*Package)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := &Program{
|
||||||
|
config: config,
|
||||||
|
clangHeaders: clangHeaders,
|
||||||
|
typeChecker: typeChecker,
|
||||||
|
goroot: goroot,
|
||||||
|
workingDir: wd,
|
||||||
|
Packages: make(map[string]*Package),
|
||||||
|
fset: token.NewFileSet(),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := p.loadPackage(importPath)
|
// List the dependencies of this package, in raw JSON format.
|
||||||
|
extraArgs := []string{"-json", "-deps"}
|
||||||
|
if config.TestConfig.CompileTestBinary {
|
||||||
|
extraArgs = append(extraArgs, "-test")
|
||||||
|
}
|
||||||
|
cmd, err := List(config, extraArgs, inputPkgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
p.MainPkg = p.sorted[len(p.sorted)-1]
|
buf := &bytes.Buffer{}
|
||||||
if _, ok := p.Packages["runtime"]; !ok {
|
cmd.Stdout = buf
|
||||||
// The runtime package wasn't loaded. Although `go list -deps` seems to
|
cmd.Stderr = os.Stderr
|
||||||
// return the full dependency list, there is no way to get those
|
err = cmd.Run()
|
||||||
// packages from the go/packages package. Therefore load the runtime
|
if err != nil {
|
||||||
// manually and add it to the list of to-be-compiled packages
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
// (duplicates are already filtered).
|
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||||
return p.loadPackage("runtime")
|
os.Exit(status.ExitStatus())
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to run `go list`: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Program) loadPackage(importPath string) error {
|
// Parse the returned json from `go list`.
|
||||||
cgoEnabled := "0"
|
decoder := json.NewDecoder(buf)
|
||||||
if p.Build.CgoEnabled {
|
for {
|
||||||
cgoEnabled = "1"
|
pkg := &Package{
|
||||||
|
program: p,
|
||||||
|
info: types.Info{
|
||||||
|
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||||
|
Defs: make(map[*ast.Ident]types.Object),
|
||||||
|
Uses: make(map[*ast.Ident]types.Object),
|
||||||
|
Implicits: make(map[ast.Node]types.Object),
|
||||||
|
Scopes: make(map[ast.Node]*types.Scope),
|
||||||
|
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
pkgs, err := packages.Load(&packages.Config{
|
err := decoder.Decode(&pkg.PackageJSON)
|
||||||
Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps,
|
|
||||||
Env: append(os.Environ(), "GOROOT="+p.Build.GOROOT, "GOOS="+p.Build.GOOS, "GOARCH="+p.Build.GOARCH, "CGO_ENABLED="+cgoEnabled),
|
|
||||||
BuildFlags: []string{"-tags", strings.Join(p.Build.BuildTags, " ")},
|
|
||||||
Tests: p.Tests,
|
|
||||||
}, importPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
if err == io.EOF {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
var pkg *packages.Package
|
return nil, err
|
||||||
if p.Tests {
|
|
||||||
// We need the second package. Quoting from the docs:
|
|
||||||
// > For example, when using the go command, loading "fmt" with Tests=true
|
|
||||||
// > returns four packages, with IDs "fmt" (the standard package),
|
|
||||||
// > "fmt [fmt.test]" (the package as compiled for the test),
|
|
||||||
// > "fmt_test" (the test functions from source files in package fmt_test),
|
|
||||||
// > and "fmt.test" (the test binary).
|
|
||||||
pkg = pkgs[1]
|
|
||||||
} else {
|
|
||||||
if len(pkgs) != 1 {
|
|
||||||
return fmt.Errorf("expected exactly one package while importing %s, got %d", importPath, len(pkgs))
|
|
||||||
}
|
}
|
||||||
pkg = pkgs[0]
|
if pkg.Error != nil {
|
||||||
}
|
// There was an error while importing (for example, a circular
|
||||||
var importError *Errors
|
// dependency).
|
||||||
var addPackages func(pkg *packages.Package)
|
|
||||||
addPackages = func(pkg *packages.Package) {
|
|
||||||
if _, ok := p.Packages[pkg.PkgPath]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pkg2 := p.newPackage(pkg)
|
|
||||||
p.Packages[pkg.PkgPath] = pkg2
|
|
||||||
if len(pkg.Errors) != 0 {
|
|
||||||
if importError != nil {
|
|
||||||
// There was another error reported already. Do not report
|
|
||||||
// errors from multiple packages at once.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
importError = &Errors{
|
|
||||||
Pkg: pkg2,
|
|
||||||
}
|
|
||||||
for _, err := range pkg.Errors {
|
|
||||||
pos := token.Position{}
|
pos := token.Position{}
|
||||||
fields := strings.Split(err.Pos, ":")
|
fields := strings.Split(pkg.Error.Pos, ":")
|
||||||
if len(fields) >= 2 {
|
if len(fields) >= 2 {
|
||||||
// There is some file/line/column information.
|
// There is some file/line/column information.
|
||||||
if n, err := strconv.Atoi(fields[len(fields)-2]); err == nil {
|
if n, err := strconv.Atoi(fields[len(fields)-2]); err == nil {
|
||||||
|
@ -134,33 +157,23 @@ func (p *Program) loadPackage(importPath string) error {
|
||||||
}
|
}
|
||||||
pos.Filename = p.getOriginalPath(pos.Filename)
|
pos.Filename = p.getOriginalPath(pos.Filename)
|
||||||
}
|
}
|
||||||
importError.Errs = append(importError.Errs, scanner.Error{
|
err := scanner.Error{
|
||||||
Pos: pos,
|
Pos: pos,
|
||||||
Msg: err.Msg,
|
Msg: pkg.Error.Err,
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return
|
if len(pkg.Error.ImportStack) != 0 {
|
||||||
|
return nil, Error{
|
||||||
|
ImportStack: pkg.Error.ImportStack,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.sorted = append(p.sorted, pkg)
|
||||||
|
p.Packages[pkg.ImportPath] = pkg
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the list of imports (sorted alphabetically).
|
return p, nil
|
||||||
names := make([]string, 0, len(pkg.Imports))
|
|
||||||
for name := range pkg.Imports {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
|
|
||||||
// Add all the imports.
|
|
||||||
for _, name := range names {
|
|
||||||
addPackages(pkg.Imports[name])
|
|
||||||
}
|
|
||||||
|
|
||||||
p.sorted = append(p.sorted, pkg2)
|
|
||||||
}
|
|
||||||
addPackages(pkg)
|
|
||||||
if importError != nil {
|
|
||||||
return *importError
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOriginalPath looks whether this path is in the generated GOROOT and if so,
|
// getOriginalPath looks whether this path is in the generated GOROOT and if so,
|
||||||
|
@ -168,23 +181,23 @@ func (p *Program) loadPackage(importPath string) error {
|
||||||
// the input path is returned.
|
// the input path is returned.
|
||||||
func (p *Program) getOriginalPath(path string) string {
|
func (p *Program) getOriginalPath(path string) string {
|
||||||
originalPath := path
|
originalPath := path
|
||||||
if strings.HasPrefix(path, p.Build.GOROOT+string(filepath.Separator)) {
|
if strings.HasPrefix(path, p.goroot+string(filepath.Separator)) {
|
||||||
// If this file is part of the synthetic GOROOT, try to infer the
|
// If this file is part of the synthetic GOROOT, try to infer the
|
||||||
// original path.
|
// original path.
|
||||||
relpath := path[len(filepath.Join(p.Build.GOROOT, "src"))+1:]
|
relpath := path[len(filepath.Join(p.goroot, "src"))+1:]
|
||||||
realgorootPath := filepath.Join(goenv.Get("GOROOT"), "src", relpath)
|
realgorootPath := filepath.Join(goenv.Get("GOROOT"), "src", relpath)
|
||||||
if _, err := os.Stat(realgorootPath); err == nil {
|
if _, err := os.Stat(realgorootPath); err == nil {
|
||||||
originalPath = realgorootPath
|
originalPath = realgorootPath
|
||||||
}
|
}
|
||||||
maybeInTinyGoRoot := false
|
maybeInTinyGoRoot := false
|
||||||
for prefix := range pathsToOverride(needsSyscallPackage(p.Build.BuildTags)) {
|
for prefix := range pathsToOverride(needsSyscallPackage(p.config.BuildTags())) {
|
||||||
if !strings.HasPrefix(relpath, prefix) {
|
if !strings.HasPrefix(relpath, prefix) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
maybeInTinyGoRoot = true
|
maybeInTinyGoRoot = true
|
||||||
}
|
}
|
||||||
if maybeInTinyGoRoot {
|
if maybeInTinyGoRoot {
|
||||||
tinygoPath := filepath.Join(p.TINYGOROOT, "src", relpath)
|
tinygoPath := filepath.Join(goenv.Get("TINYGOROOT"), "src", relpath)
|
||||||
if _, err := os.Stat(tinygoPath); err == nil {
|
if _, err := os.Stat(tinygoPath); err == nil {
|
||||||
originalPath = tinygoPath
|
originalPath = tinygoPath
|
||||||
}
|
}
|
||||||
|
@ -193,28 +206,18 @@ func (p *Program) getOriginalPath(path string) string {
|
||||||
return originalPath
|
return originalPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPackage instantiates a new *Package object with initialized members.
|
|
||||||
func (p *Program) newPackage(pkg *packages.Package) *Package {
|
|
||||||
return &Package{
|
|
||||||
Program: p,
|
|
||||||
Package: pkg,
|
|
||||||
Info: types.Info{
|
|
||||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
|
||||||
Defs: make(map[*ast.Ident]types.Object),
|
|
||||||
Uses: make(map[*ast.Ident]types.Object),
|
|
||||||
Implicits: make(map[ast.Node]types.Object),
|
|
||||||
Scopes: make(map[ast.Node]*types.Scope),
|
|
||||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sorted returns a list of all packages, sorted in a way that no packages come
|
// Sorted returns a list of all packages, sorted in a way that no packages come
|
||||||
// before the packages they depend upon.
|
// before the packages they depend upon.
|
||||||
func (p *Program) Sorted() []*Package {
|
func (p *Program) Sorted() []*Package {
|
||||||
return p.sorted
|
return p.sorted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MainPkg returns the last package in the Sorted() slice. This is the main
|
||||||
|
// package of the program.
|
||||||
|
func (p *Program) MainPkg() *Package {
|
||||||
|
return p.sorted[len(p.sorted)-1]
|
||||||
|
}
|
||||||
|
|
||||||
// Parse parses all packages and typechecks them.
|
// Parse parses all packages and typechecks them.
|
||||||
//
|
//
|
||||||
// The returned error may be an Errors error, which contains a list of errors.
|
// The returned error may be an Errors error, which contains a list of errors.
|
||||||
|
@ -222,22 +225,16 @@ func (p *Program) Sorted() []*Package {
|
||||||
// Idempotent.
|
// Idempotent.
|
||||||
func (p *Program) Parse() error {
|
func (p *Program) Parse() error {
|
||||||
// Parse all packages.
|
// Parse all packages.
|
||||||
for _, pkg := range p.Sorted() {
|
// TODO: do this in parallel.
|
||||||
|
for _, pkg := range p.sorted {
|
||||||
err := pkg.Parse()
|
err := pkg.Parse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Tests {
|
|
||||||
err := p.swapTestMain()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typecheck all packages.
|
// Typecheck all packages.
|
||||||
for _, pkg := range p.Sorted() {
|
for _, pkg := range p.sorted {
|
||||||
err := pkg.Check()
|
err := pkg.Check()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -247,82 +244,6 @@ func (p *Program) Parse() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Program) swapTestMain() error {
|
|
||||||
var tests []string
|
|
||||||
|
|
||||||
isTestFunc := func(f *ast.FuncDecl) bool {
|
|
||||||
// TODO: improve signature check
|
|
||||||
if strings.HasPrefix(f.Name.Name, "Test") && f.Name.Name != "TestMain" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, f := range p.MainPkg.Files {
|
|
||||||
for i, d := range f.Decls {
|
|
||||||
switch v := d.(type) {
|
|
||||||
case *ast.FuncDecl:
|
|
||||||
if isTestFunc(v) {
|
|
||||||
tests = append(tests, v.Name.Name)
|
|
||||||
}
|
|
||||||
if v.Name.Name == "main" {
|
|
||||||
// Remove main
|
|
||||||
if len(f.Decls) == 1 {
|
|
||||||
f.Decls = make([]ast.Decl, 0)
|
|
||||||
} else {
|
|
||||||
f.Decls[i] = f.Decls[len(f.Decls)-1]
|
|
||||||
f.Decls = f.Decls[:len(f.Decls)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Check if they defined a TestMain and call it instead of testing.TestMain
|
|
||||||
const mainBody = `package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main () {
|
|
||||||
m := &testing.M{
|
|
||||||
Tests: []testing.TestToCall{
|
|
||||||
{{range .TestFunctions}}
|
|
||||||
{Name: "{{.}}", Func: {{.}}},
|
|
||||||
{{end}}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
testing.TestMain(m)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
tmpl := template.Must(template.New("testmain").Parse(mainBody))
|
|
||||||
b := bytes.Buffer{}
|
|
||||||
tmplData := struct {
|
|
||||||
TestFunctions []string
|
|
||||||
}{
|
|
||||||
TestFunctions: tests,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := tmpl.Execute(&b, tmplData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
path := filepath.Join(p.MainPkg.Dir, "$testmain.go")
|
|
||||||
|
|
||||||
if p.fset == nil {
|
|
||||||
p.fset = token.NewFileSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
newMain, err := parser.ParseFile(p.fset, path, b.Bytes(), parser.AllErrors)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p.MainPkg.Files = append(p.MainPkg.Files, newMain)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseFile is a wrapper around parser.ParseFile.
|
// parseFile is a wrapper around parser.ParseFile.
|
||||||
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||||
if p.fset == nil {
|
if p.fset == nil {
|
||||||
|
@ -342,14 +263,13 @@ func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||||
// Idempotent.
|
// Idempotent.
|
||||||
func (p *Package) Parse() error {
|
func (p *Package) Parse() error {
|
||||||
if len(p.Files) != 0 {
|
if len(p.Files) != 0 {
|
||||||
return nil
|
return nil // nothing to do (?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the AST.
|
// Load the AST.
|
||||||
// TODO: do this in parallel.
|
if p.ImportPath == "unsafe" {
|
||||||
if p.PkgPath == "unsafe" {
|
// Special case for the unsafe package, which is defined internally by
|
||||||
// Special case for the unsafe package. Don't even bother loading
|
// the types package.
|
||||||
// the files.
|
|
||||||
p.Pkg = types.Unsafe
|
p.Pkg = types.Unsafe
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -369,11 +289,11 @@ func (p *Package) Parse() error {
|
||||||
// Idempotent.
|
// Idempotent.
|
||||||
func (p *Package) Check() error {
|
func (p *Package) Check() error {
|
||||||
if p.Pkg != nil {
|
if p.Pkg != nil {
|
||||||
return nil
|
return nil // already typechecked
|
||||||
}
|
}
|
||||||
|
|
||||||
var typeErrors []error
|
var typeErrors []error
|
||||||
checker := p.TypeChecker
|
checker := p.program.typeChecker // make a copy, because it will be modified
|
||||||
checker.Error = func(err error) {
|
checker.Error = func(err error) {
|
||||||
typeErrors = append(typeErrors, err)
|
typeErrors = append(typeErrors, err)
|
||||||
}
|
}
|
||||||
|
@ -381,7 +301,7 @@ func (p *Package) Check() error {
|
||||||
// Do typechecking of the package.
|
// Do typechecking of the package.
|
||||||
checker.Importer = p
|
checker.Importer = p
|
||||||
|
|
||||||
typesPkg, err := checker.Check(p.PkgPath, p.fset, p.Files, &p.Info)
|
typesPkg, err := checker.Check(p.ImportPath, p.program.fset, p.Files, &p.info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err, ok := err.(Errors); ok {
|
if err, ok := err.(Errors); ok {
|
||||||
return err
|
return err
|
||||||
|
@ -394,40 +314,46 @@ func (p *Package) Check() error {
|
||||||
|
|
||||||
// parseFiles parses the loaded list of files and returns this list.
|
// parseFiles parses the loaded list of files and returns this list.
|
||||||
func (p *Package) parseFiles() ([]*ast.File, error) {
|
func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||||
// TODO: do this concurrently.
|
|
||||||
var files []*ast.File
|
var files []*ast.File
|
||||||
var fileErrs []error
|
var fileErrs []error
|
||||||
|
|
||||||
var cgoFiles []*ast.File
|
// Parse all files (incuding CgoFiles).
|
||||||
for _, file := range p.GoFiles {
|
parseFile := func(file string) {
|
||||||
f, err := p.parseFile(file, parser.ParseComments)
|
if !filepath.IsAbs(file) {
|
||||||
|
file = filepath.Join(p.Dir, file)
|
||||||
|
}
|
||||||
|
f, err := p.program.parseFile(file, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fileErrs = append(fileErrs, err)
|
fileErrs = append(fileErrs, err)
|
||||||
continue
|
return
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
fileErrs = append(fileErrs, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, importSpec := range f.Imports {
|
|
||||||
if importSpec.Path.Value == `"C"` {
|
|
||||||
cgoFiles = append(cgoFiles, f)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
if len(cgoFiles) != 0 {
|
for _, file := range p.GoFiles {
|
||||||
cflags := append(p.CFlags, "-I"+filepath.Dir(p.GoFiles[0]))
|
parseFile(file)
|
||||||
if p.ClangHeaders != "" {
|
|
||||||
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.ClangHeaders)
|
|
||||||
}
|
}
|
||||||
generated, ldflags, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags)
|
for _, file := range p.CgoFiles {
|
||||||
|
parseFile(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do CGo processing.
|
||||||
|
if len(p.CgoFiles) != 0 {
|
||||||
|
var cflags []string
|
||||||
|
cflags = append(cflags, p.program.config.CFlags()...)
|
||||||
|
cflags = append(cflags, "-I"+p.Dir)
|
||||||
|
if p.program.clangHeaders != "" {
|
||||||
|
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
|
||||||
|
}
|
||||||
|
generated, ldflags, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
fileErrs = append(fileErrs, errs...)
|
fileErrs = append(fileErrs, errs...)
|
||||||
}
|
}
|
||||||
files = append(files, generated)
|
files = append(files, generated)
|
||||||
p.LDFlags = append(p.LDFlags, ldflags...)
|
p.program.LDFlags = append(p.program.LDFlags, ldflags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only return an error after CGo processing, so that errors in parsing and
|
||||||
|
// CGo can be reported together.
|
||||||
if len(fileErrs) != 0 {
|
if len(fileErrs) != 0 {
|
||||||
return nil, Errors{p, fileErrs}
|
return nil, Errors{p, fileErrs}
|
||||||
}
|
}
|
||||||
|
@ -441,8 +367,13 @@ func (p *Package) Import(to string) (*types.Package, error) {
|
||||||
if to == "unsafe" {
|
if to == "unsafe" {
|
||||||
return types.Unsafe, nil
|
return types.Unsafe, nil
|
||||||
}
|
}
|
||||||
if _, ok := p.Imports[to]; ok {
|
if replace, ok := p.ImportMap[to]; ok {
|
||||||
return p.Packages[p.Imports[to].PkgPath].Pkg, nil
|
// This import path should be replaced by another import path, according
|
||||||
|
// to `go list`.
|
||||||
|
to = replace
|
||||||
|
}
|
||||||
|
if imported, ok := p.program.Packages[to]; ok {
|
||||||
|
return imported.Pkg, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("package not imported: " + to)
|
return nil, errors.New("package not imported: " + to)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
func (p *Program) LoadSSA() *ssa.Program {
|
func (p *Program) LoadSSA() *ssa.Program {
|
||||||
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
||||||
|
|
||||||
for _, pkg := range p.Sorted() {
|
for _, pkg := range p.sorted {
|
||||||
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.Info, true)
|
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return prog
|
return prog
|
||||||
|
|
66
main.go
66
main.go
|
@ -126,12 +126,6 @@ func Test(pkgName string, options *compileopts.Options) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add test build tag. This is incorrect: `go test` only looks at the
|
|
||||||
// _test.go file suffix but does not add the test build tag in the process.
|
|
||||||
// However, it's a simple fix right now.
|
|
||||||
// For details: https://github.com/golang/go/issues/21360
|
|
||||||
config.Target.BuildTags = append(config.Target.BuildTags, "test")
|
|
||||||
|
|
||||||
return builder.Build(pkgName, ".elf", config, func(tmppath string) error {
|
return builder.Build(pkgName, ".elf", config, func(tmppath string) error {
|
||||||
cmd := exec.Command(tmppath)
|
cmd := exec.Command(tmppath)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
|
@ -648,36 +642,6 @@ func getDefaultPort() (port string, err error) {
|
||||||
return d[0], nil
|
return d[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runGoList runs the `go list` command but using the configuration used for
|
|
||||||
// TinyGo.
|
|
||||||
func runGoList(config *compileopts.Config, flagJSON, flagDeps bool, pkgs []string) error {
|
|
||||||
goroot, err := loader.GetCachedGoroot(config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
args := []string{"list"}
|
|
||||||
if flagJSON {
|
|
||||||
args = append(args, "-json")
|
|
||||||
}
|
|
||||||
if flagDeps {
|
|
||||||
args = append(args, "-deps")
|
|
||||||
}
|
|
||||||
if len(config.BuildTags()) != 0 {
|
|
||||||
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
|
|
||||||
}
|
|
||||||
args = append(args, pkgs...)
|
|
||||||
cgoEnabled := "0"
|
|
||||||
if config.CgoEnabled() {
|
|
||||||
cgoEnabled = "1"
|
|
||||||
}
|
|
||||||
cmd := exec.Command("go", args...)
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED="+cgoEnabled)
|
|
||||||
cmd.Run()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||||
fmt.Fprintln(os.Stderr, "version:", goenv.Version)
|
fmt.Fprintln(os.Stderr, "version:", goenv.Version)
|
||||||
|
@ -751,10 +715,16 @@ func printCompilerError(logln func(...interface{}), err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case loader.Errors:
|
case loader.Errors:
|
||||||
logln("#", err.Pkg.PkgPath)
|
logln("#", err.Pkg.ImportPath)
|
||||||
for _, err := range err.Errs {
|
for _, err := range err.Errs {
|
||||||
printCompilerError(logln, err)
|
printCompilerError(logln, err)
|
||||||
}
|
}
|
||||||
|
case loader.Error:
|
||||||
|
logln(err.Err.Error())
|
||||||
|
logln("package", err.ImportStack[0])
|
||||||
|
for _, pkgPath := range err.ImportStack[1:] {
|
||||||
|
logln("\timports", pkgPath)
|
||||||
|
}
|
||||||
case *builder.MultiError:
|
case *builder.MultiError:
|
||||||
for _, err := range err.Errs {
|
for _, err := range err.Errs {
|
||||||
printCompilerError(logln, err)
|
printCompilerError(logln, err)
|
||||||
|
@ -982,11 +952,31 @@ func main() {
|
||||||
usage()
|
usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err = runGoList(config, *flagJSON, *flagDeps, flag.Args())
|
var extraArgs []string
|
||||||
|
if *flagJSON {
|
||||||
|
extraArgs = append(extraArgs, "-json")
|
||||||
|
}
|
||||||
|
if *flagDeps {
|
||||||
|
extraArgs = append(extraArgs, "-deps")
|
||||||
|
}
|
||||||
|
cmd, err := loader.List(config, extraArgs, flag.Args())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
|
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||||
|
os.Exit(status.ExitStatus())
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
case "clean":
|
case "clean":
|
||||||
// remove cache directory
|
// remove cache directory
|
||||||
err := os.RemoveAll(goenv.Get("GOCACHE"))
|
err := os.RemoveAll(goenv.Get("GOCACHE"))
|
||||||
|
|
29
src/runtime/pprof/pprof.go
Обычный файл
29
src/runtime/pprof/pprof.go
Обычный файл
|
@ -0,0 +1,29 @@
|
||||||
|
package pprof
|
||||||
|
|
||||||
|
// TinyGo does not implement pprof. However, a dummy shell is needed for the
|
||||||
|
// testing package (and testing/internal/pprof).
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrUnimplemented = errors.New("runtime/pprof: unimplemented")
|
||||||
|
|
||||||
|
type Profile struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartCPUProfile(w io.Writer) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func StopCPUProfile() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func Lookup(name string) *Profile {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Profile) WriteTo(w io.Writer, debug int) error {
|
||||||
|
return ErrUnimplemented
|
||||||
|
}
|
|
@ -15,3 +15,8 @@ type B struct {
|
||||||
common
|
common
|
||||||
N int
|
N int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InternalBenchmark struct {
|
||||||
|
Name string
|
||||||
|
F func(b *B)
|
||||||
|
}
|
||||||
|
|
|
@ -154,18 +154,16 @@ func (c *common) Skipped() bool {
|
||||||
return c.skipped
|
return c.skipped
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestToCall is a reference to a test that should be called during a test suite run.
|
// InternalTest is a reference to a test that should be called during a test suite run.
|
||||||
type TestToCall struct {
|
type InternalTest struct {
|
||||||
// Name of the test to call.
|
|
||||||
Name string
|
Name string
|
||||||
// Function reference to the test.
|
F func(*T)
|
||||||
Func func(*T)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// M is a test suite.
|
// M is a test suite.
|
||||||
type M struct {
|
type M struct {
|
||||||
// tests is a list of the test names to execute
|
// tests is a list of the test names to execute
|
||||||
Tests []TestToCall
|
Tests []InternalTest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the test suite.
|
// Run the test suite.
|
||||||
|
@ -180,7 +178,7 @@ func (m *M) Run() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("=== RUN %s\n", test.Name)
|
fmt.Printf("=== RUN %s\n", test.Name)
|
||||||
test.Func(t)
|
test.F(t)
|
||||||
|
|
||||||
if t.failed {
|
if t.failed {
|
||||||
fmt.Printf("--- FAIL: %s\n", test.Name)
|
fmt.Printf("--- FAIL: %s\n", test.Name)
|
||||||
|
@ -204,3 +202,16 @@ func (m *M) Run() int {
|
||||||
func TestMain(m *M) {
|
func TestMain(m *M) {
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
|
||||||
|
return &M{
|
||||||
|
Tests: tests,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type InternalExample struct {
|
||||||
|
Name string
|
||||||
|
F func()
|
||||||
|
Output string
|
||||||
|
Unordered bool
|
||||||
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче