1
0
mirror of https://github.com/golang/go synced 2024-10-01 11:28:34 -06:00

cmd/go: split out cmd/go/internal/work

This is one CL in a long sequence of changes to break up the
go command from one package into a plausible group of packages.

This sequence is concerned only with moving code, not changing
or cleaning up code. There will still be more cleanup after this sequence.

The entire sequence will be submitted together: it is not a goal
for the tree to build at every step.

For #18653.

Change-Id: Icdd181098f9f0e81f68bf201e6867cdd8f820300
Reviewed-on: https://go-review.googlesource.com/36197
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Russ Cox 2017-01-18 00:06:08 -05:00
parent eb93b20c2e
commit 3c667ef421
18 changed files with 878 additions and 784 deletions

View File

@ -1,44 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"os"
"reflect"
"testing"
)
func TestRemoveDevNull(t *testing.T) {
fi, err := os.Lstat(os.DevNull)
if err != nil {
t.Skip(err)
}
if fi.Mode().IsRegular() {
t.Errorf("Lstat(%s).Mode().IsRegular() = true; expected false", os.DevNull)
}
mayberemovefile(os.DevNull)
_, err = os.Lstat(os.DevNull)
if err != nil {
t.Errorf("mayberemovefile(%s) did remove it; oops", os.DevNull)
}
}
func TestSplitPkgConfigOutput(t *testing.T) {
for _, test := range []struct {
in []byte
want []string
}{
{[]byte(`-r:foo -L/usr/white\ space/lib -lfoo\ bar -lbar\ baz`), []string{"-r:foo", "-L/usr/white space/lib", "-lfoo bar", "-lbar baz"}},
{[]byte(`-lextra\ fun\ arg\\`), []string{`-lextra fun arg\`}},
{[]byte(`broken flag\`), []string{"broken", "flag"}},
{[]byte("\textra whitespace\r\n"), []string{"extra", "whitespace"}},
{[]byte(" \r\n "), nil},
} {
got := splitPkgConfigOutput(test.in)
if !reflect.DeepEqual(got, test.want) {
t.Errorf("splitPkgConfigOutput(%v) = %v; want %v", test.in, got, test.want)
}
}
}

View File

@ -5,14 +5,16 @@
package main
import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/work"
)
var cmdClean = &base.Command{
@ -74,7 +76,7 @@ func init() {
// mentioned explicitly in the docs but they
// are part of the build flags.
addBuildFlags(cmdClean)
work.AddBuildFlags(cmdClean)
}
func runClean(cmd *base.Command, args []string) {
@ -124,8 +126,8 @@ func clean(p *load.Package) {
return
}
var b builder
b.print = fmt.Print
var b work.Builder
b.Print = fmt.Print
packageFile := map[string]bool{}
if p.Name != "main" {
@ -176,7 +178,7 @@ func clean(p *load.Package) {
}
if cfg.BuildN || cfg.BuildX {
b.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " "))
b.Showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " "))
}
toRemove := map[string]bool{}
@ -189,7 +191,7 @@ func clean(p *load.Package) {
// TODO: Remove once Makefiles are forgotten.
if cleanDir[name] {
if cfg.BuildN || cfg.BuildX {
b.showcmd(p.Dir, "rm -r %s", name)
b.Showcmd(p.Dir, "rm -r %s", name)
if cfg.BuildN {
continue
}
@ -212,7 +214,7 @@ func clean(p *load.Package) {
if cleanI && p.Internal.Target != "" {
if cfg.BuildN || cfg.BuildX {
b.showcmd("", "rm -f %s", p.Internal.Target)
b.Showcmd("", "rm -f %s", p.Internal.Target)
}
if !cfg.BuildN {
removeFile(p.Internal.Target)

View File

@ -5,13 +5,15 @@
package main
import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"fmt"
"os"
"runtime"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/work"
)
var cmdEnv = &base.Command{
@ -29,8 +31,8 @@ each named variable on its own line.
}
func mkEnv() []cfg.EnvVar {
var b builder
b.init()
var b work.Builder
b.Init()
env := []cfg.EnvVar{
{"GOARCH", cfg.Goarch},
@ -48,10 +50,10 @@ func mkEnv() []cfg.EnvVar {
{"TERM", "dumb"},
}
if gccgoBin != "" {
env = append(env, cfg.EnvVar{"GCCGO", gccgoBin})
if work.GccgoBin != "" {
env = append(env, cfg.EnvVar{"GCCGO", work.GccgoBin})
} else {
env = append(env, cfg.EnvVar{"GCCGO", gccgoName})
env = append(env, cfg.EnvVar{"GCCGO", work.GccgoName})
}
switch cfg.Goarch {
@ -61,10 +63,10 @@ func mkEnv() []cfg.EnvVar {
env = append(env, cfg.EnvVar{"GO386", os.Getenv("GO386")})
}
cmd := b.gccCmd(".")
cmd := b.GccCmd(".")
env = append(env, cfg.EnvVar{"CC", cmd[0]})
env = append(env, cfg.EnvVar{"GOGCCFLAGS", strings.Join(cmd[3:], " ")})
cmd = b.gxxCmd(".")
cmd = b.GxxCmd(".")
env = append(env, cfg.EnvVar{"CXX", cmd[0]})
if cfg.BuildContext.CgoEnabled {
@ -87,11 +89,11 @@ func findEnv(env []cfg.EnvVar, name string) string {
// extraEnvVars returns environment variables that should not leak into child processes.
func extraEnvVars() []cfg.EnvVar {
var b builder
b.init()
cppflags, cflags, cxxflags, fflags, ldflags := b.cflags(&load.Package{})
var b work.Builder
b.Init()
cppflags, cflags, cxxflags, fflags, ldflags := b.CFlags(&load.Package{})
return []cfg.EnvVar{
{"PKG_CONFIG", b.pkgconfigCmd()},
{"PKG_CONFIG", b.PkgconfigCmd()},
{"CGO_CFLAGS", strings.Join(cflags, " ")},
{"CGO_CPPFLAGS", strings.Join(cppflags, " ")},
{"CGO_CXXFLAGS", strings.Join(cxxflags, " ")},

View File

@ -10,6 +10,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/work"
"fmt"
"io"
"log"
@ -134,7 +135,7 @@ var (
)
func init() {
addBuildFlags(cmdGenerate)
work.AddBuildFlags(cmdGenerate)
cmdGenerate.Flag.StringVar(&generateRunFlag, "run", "", "")
}

View File

@ -9,6 +9,7 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
"cmd/go/internal/work"
"fmt"
"go/build"
"os"
@ -83,7 +84,7 @@ var getFix = cmdGet.Flag.Bool("fix", false, "")
var getInsecure = cmdGet.Flag.Bool("insecure", false, "")
func init() {
addBuildFlags(cmdGet)
work.AddBuildFlags(cmdGet)
cmdGet.Run = runGet // break init loop
}
@ -157,7 +158,7 @@ func runGet(cmd *base.Command, args []string) {
return
}
installPackages(args, true)
work.InstallPackages(args, true)
}
// downloadPaths prepares the list of paths to pass to download.

View File

@ -0,0 +1,37 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package base
import "strings"
// envForDir returns a copy of the environment
// suitable for running in the given directory.
// The environment is the current process's environment
// but with an updated $PWD, so that an os.Getwd in the
// child will be faster.
func EnvForDir(dir string, base []string) []string {
// Internally we only use rooted paths, so dir is rooted.
// Even if dir is not rooted, no harm done.
return MergeEnvLists([]string{"PWD=" + dir}, base)
}
// MergeEnvLists merges the two environment lists such that
// variables with the same name in "in" replace those in "out".
// This always returns a newly allocated slice.
func MergeEnvLists(in, out []string) []string {
out = append([]string(nil), out...)
NextVar:
for _, inkv := range in {
k := strings.SplitAfterN(inkv, "=", 2)[0]
for i, outkv := range out {
if strings.HasPrefix(outkv, k) {
out[i] = inkv
continue NextVar
}
}
out = append(out, inkv)
}
return out
}

View File

@ -0,0 +1,33 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package base
import (
"cmd/go/internal/str"
"flag"
)
// A StringsFlag is a command-line flag that interprets its argument
// as a space-separated list of possibly-quoted strings.
type StringsFlag []string
func (v *StringsFlag) Set(s string) error {
var err error
*v, err = str.SplitQuotedFields(s)
if *v == nil {
*v = []string{}
}
return err
}
func (v *StringsFlag) String() string {
return "<StringsFlag>"
}
// AddBuildFlagsNX adds the -n and -x build flags to the flag set.
func AddBuildFlagsNX(flags *flag.FlagSet) {
flags.BoolVar(&BuildN, "n", false, "")
flags.BoolVar(&BuildX, "x", false, "")
}

View File

@ -7,6 +7,7 @@ package base
import (
"os"
"path/filepath"
"strings"
)
var Cwd, _ = os.Getwd()
@ -34,3 +35,10 @@ func RelPaths(paths []string) []string {
}
return out
}
// IsTestFile reports whether the source file is a set of tests and should therefore
// be excluded from coverage analysis.
func IsTestFile(file string) bool {
// We don't cover tests, only the code they test.
return strings.HasSuffix(file, "_test.go")
}

View File

@ -95,3 +95,47 @@ func Contains(x []string, s string) bool {
}
return false
}
func isSpaceByte(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}
// SplitQuotedFields splits s into a list of fields,
// allowing single or double quotes around elements.
// There is no unescaping or other processing within
// quoted fields.
func SplitQuotedFields(s string) ([]string, error) {
// Split fields allowing '' or "" around elements.
// Quotes further inside the string do not count.
var f []string
for len(s) > 0 {
for len(s) > 0 && isSpaceByte(s[0]) {
s = s[1:]
}
if len(s) == 0 {
break
}
// Accepted quoted string. No unescaping inside.
if s[0] == '"' || s[0] == '\'' {
quote := s[0]
s = s[1:]
i := 0
for i < len(s) && s[i] != quote {
i++
}
if i >= len(s) {
return nil, fmt.Errorf("unterminated %c string", quote)
}
f = append(f, s[:i])
s = s[i+1:]
continue
}
i := 0
for i < len(s) && !isSpaceByte(s[i]) {
i++
}
f = append(f, s[:i])
s = s[i:]
}
return f, nil
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,167 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package work
import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
)
func TestRemoveDevNull(t *testing.T) {
fi, err := os.Lstat(os.DevNull)
if err != nil {
t.Skip(err)
}
if fi.Mode().IsRegular() {
t.Errorf("Lstat(%s).Mode().IsRegular() = true; expected false", os.DevNull)
}
mayberemovefile(os.DevNull)
_, err = os.Lstat(os.DevNull)
if err != nil {
t.Errorf("mayberemovefile(%s) did remove it; oops", os.DevNull)
}
}
func TestSplitPkgConfigOutput(t *testing.T) {
for _, test := range []struct {
in []byte
want []string
}{
{[]byte(`-r:foo -L/usr/white\ space/lib -lfoo\ bar -lbar\ baz`), []string{"-r:foo", "-L/usr/white space/lib", "-lfoo bar", "-lbar baz"}},
{[]byte(`-lextra\ fun\ arg\\`), []string{`-lextra fun arg\`}},
{[]byte(`broken flag\`), []string{"broken", "flag"}},
{[]byte("\textra whitespace\r\n"), []string{"extra", "whitespace"}},
{[]byte(" \r\n "), nil},
} {
got := splitPkgConfigOutput(test.in)
if !reflect.DeepEqual(got, test.want) {
t.Errorf("splitPkgConfigOutput(%v) = %v; want %v", test.in, got, test.want)
}
}
}
func TestSharedLibName(t *testing.T) {
// TODO(avdva) - make these values platform-specific
prefix := "lib"
suffix := ".so"
testData := []struct {
args []string
pkgs []*load.Package
expected string
expectErr bool
rootedAt string
}{
{
args: []string{"std"},
pkgs: []*load.Package{},
expected: "std",
},
{
args: []string{"std", "cmd"},
pkgs: []*load.Package{},
expected: "std,cmd",
},
{
args: []string{},
pkgs: []*load.Package{pkgImportPath("gopkg.in/somelib")},
expected: "gopkg.in-somelib",
},
{
args: []string{"./..."},
pkgs: []*load.Package{pkgImportPath("somelib")},
expected: "somelib",
rootedAt: "somelib",
},
{
args: []string{"../somelib", "../somelib"},
pkgs: []*load.Package{pkgImportPath("somelib")},
expected: "somelib",
},
{
args: []string{"../lib1", "../lib2"},
pkgs: []*load.Package{pkgImportPath("gopkg.in/lib1"), pkgImportPath("gopkg.in/lib2")},
expected: "gopkg.in-lib1,gopkg.in-lib2",
},
{
args: []string{"./..."},
pkgs: []*load.Package{
pkgImportPath("gopkg.in/dir/lib1"),
pkgImportPath("gopkg.in/lib2"),
pkgImportPath("gopkg.in/lib3"),
},
expected: "gopkg.in",
rootedAt: "gopkg.in",
},
{
args: []string{"std", "../lib2"},
pkgs: []*load.Package{},
expectErr: true,
},
{
args: []string{"all", "./"},
pkgs: []*load.Package{},
expectErr: true,
},
{
args: []string{"cmd", "fmt"},
pkgs: []*load.Package{},
expectErr: true,
},
}
for _, data := range testData {
func() {
if data.rootedAt != "" {
tmpGopath, err := ioutil.TempDir("", "gopath")
if err != nil {
t.Fatal(err)
}
oldGopath := cfg.BuildContext.GOPATH
defer func() {
cfg.BuildContext.GOPATH = oldGopath
os.Chdir(base.Cwd)
err := os.RemoveAll(tmpGopath)
if err != nil {
t.Error(err)
}
}()
root := filepath.Join(tmpGopath, "src", data.rootedAt)
err = os.MkdirAll(root, 0755)
if err != nil {
t.Fatal(err)
}
cfg.BuildContext.GOPATH = tmpGopath
os.Chdir(root)
}
computed, err := libname(data.args, data.pkgs)
if err != nil {
if !data.expectErr {
t.Errorf("libname returned an error %q, expected a name", err.Error())
}
} else if data.expectErr {
t.Errorf("libname returned %q, expected an error", computed)
} else {
expected := prefix + data.expected + suffix
if expected != computed {
t.Errorf("libname returned %q, expected %q", computed, expected)
}
}
}()
}
}
func pkgImportPath(pkgpath string) *load.Package {
return &load.Package{
PackagePublic: load.PackagePublic{
ImportPath: pkgpath,
},
}
}

View File

@ -9,6 +9,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/work"
"encoding/json"
"io"
"os"
@ -140,7 +141,7 @@ For more about specifying packages, see 'go help packages'.
func init() {
cmdList.Run = runList // break init cycle
addBuildFlags(cmdList)
work.AddBuildFlags(cmdList)
}
var listE = cmdList.Flag.Bool("e", false, "")
@ -149,7 +150,7 @@ var listJson = cmdList.Flag.Bool("json", false, "")
var nl = []byte{'\n'}
func runList(cmd *base.Command, args []string) {
buildModeInit()
work.BuildModeInit()
out := newTrackingWriter(os.Stdout)
defer out.w.Flush()

View File

@ -16,11 +16,12 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/help"
"cmd/go/internal/work"
)
func init() {
base.Commands = []*base.Command{
cmdBuild,
work.CmdBuild,
cmdClean,
cmdDoc,
cmdEnv,
@ -29,7 +30,7 @@ func init() {
cmdFmt,
cmdGenerate,
cmdGet,
cmdInstall,
work.CmdInstall,
cmdList,
cmdRun,
cmdTest,

View File

@ -5,13 +5,8 @@
package main
import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
@ -95,112 +90,3 @@ func pkgImportPath(path string) *load.Package {
},
}
}
func TestSharedLibName(t *testing.T) {
// TODO(avdva) - make these values platform-specific
prefix := "lib"
suffix := ".so"
testData := []struct {
args []string
pkgs []*load.Package
expected string
expectErr bool
rootedAt string
}{
{
args: []string{"std"},
pkgs: []*load.Package{},
expected: "std",
},
{
args: []string{"std", "cmd"},
pkgs: []*load.Package{},
expected: "std,cmd",
},
{
args: []string{},
pkgs: []*load.Package{pkgImportPath("gopkg.in/somelib")},
expected: "gopkg.in-somelib",
},
{
args: []string{"./..."},
pkgs: []*load.Package{pkgImportPath("somelib")},
expected: "somelib",
rootedAt: "somelib",
},
{
args: []string{"../somelib", "../somelib"},
pkgs: []*load.Package{pkgImportPath("somelib")},
expected: "somelib",
},
{
args: []string{"../lib1", "../lib2"},
pkgs: []*load.Package{pkgImportPath("gopkg.in/lib1"), pkgImportPath("gopkg.in/lib2")},
expected: "gopkg.in-lib1,gopkg.in-lib2",
},
{
args: []string{"./..."},
pkgs: []*load.Package{
pkgImportPath("gopkg.in/dir/lib1"),
pkgImportPath("gopkg.in/lib2"),
pkgImportPath("gopkg.in/lib3"),
},
expected: "gopkg.in",
rootedAt: "gopkg.in",
},
{
args: []string{"std", "../lib2"},
pkgs: []*load.Package{},
expectErr: true,
},
{
args: []string{"all", "./"},
pkgs: []*load.Package{},
expectErr: true,
},
{
args: []string{"cmd", "fmt"},
pkgs: []*load.Package{},
expectErr: true,
},
}
for _, data := range testData {
func() {
if data.rootedAt != "" {
tmpGopath, err := ioutil.TempDir("", "gopath")
if err != nil {
t.Fatal(err)
}
oldGopath := cfg.BuildContext.GOPATH
defer func() {
cfg.BuildContext.GOPATH = oldGopath
os.Chdir(base.Cwd)
err := os.RemoveAll(tmpGopath)
if err != nil {
t.Error(err)
}
}()
root := filepath.Join(tmpGopath, "src", data.rootedAt)
err = os.MkdirAll(root, 0755)
if err != nil {
t.Fatal(err)
}
cfg.BuildContext.GOPATH = tmpGopath
os.Chdir(root)
}
computed, err := libname(data.args, data.pkgs)
if err != nil {
if !data.expectErr {
t.Errorf("libname returned an error %q, expected a name", err.Error())
}
} else if data.expectErr {
t.Errorf("libname returned %q, expected an error", computed)
} else {
expected := prefix + data.expected + suffix
if expected != computed {
t.Errorf("libname returned %q, expected %q", computed, expected)
}
}
}()
}
}

View File

@ -5,15 +5,17 @@
package main
import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
"cmd/go/internal/work"
)
var execCmd []string // -exec flag, for run and test
@ -59,8 +61,8 @@ See also: go build.
func init() {
cmdRun.Run = runRun // break init loop
addBuildFlags(cmdRun)
cmdRun.Flag.Var((*stringsFlag)(&execCmd), "exec", "")
work.AddBuildFlags(cmdRun)
cmdRun.Flag.Var((*base.StringsFlag)(&execCmd), "exec", "")
}
func printStderr(args ...interface{}) (int, error) {
@ -68,11 +70,11 @@ func printStderr(args ...interface{}) (int, error) {
}
func runRun(cmd *base.Command, args []string) {
instrumentInit()
buildModeInit()
var b builder
b.init()
b.print = printStderr
work.InstrumentInit()
work.BuildModeInit()
var b work.Builder
b.Init()
b.Print = printStderr
i := 0
for i < len(args) && strings.HasSuffix(args[i], ".go") {
i++
@ -126,17 +128,17 @@ func runRun(cmd *base.Command, args []string) {
base.Fatalf("go run: no suitable source files%s", hint)
}
p.Internal.ExeName = src[:len(src)-len(".go")] // name temporary executable for first go file
a1 := b.action(modeBuild, modeBuild, p)
a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}}
b.do(a)
a1 := b.Action(work.ModeBuild, work.ModeBuild, p)
a := &work.Action{Func: buildRunProgram, Args: cmdArgs, Deps: []*work.Action{a1}}
b.Do(a)
}
// runProgram is the action for running a binary that has already
// buildRunProgram is the action for running a binary that has already
// been compiled. We ignore exit status.
func (b *builder) runProgram(a *action) error {
cmdline := str.StringList(findExecCmd(), a.deps[0].target, a.args)
func buildRunProgram(b *work.Builder, a *work.Action) error {
cmdline := str.StringList(findExecCmd(), a.Deps[0].Target, a.Args)
if cfg.BuildN || cfg.BuildX {
b.showcmd("", "%s", strings.Join(cmdline, " "))
b.Showcmd("", "%s", strings.Join(cmdline, " "))
if cfg.BuildN {
return nil
}

View File

@ -10,6 +10,7 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
"cmd/go/internal/work"
"errors"
"fmt"
"go/ast"
@ -409,8 +410,8 @@ func runTest(cmd *base.Command, args []string) {
findExecCmd() // initialize cached result
instrumentInit()
buildModeInit()
work.InstrumentInit()
work.BuildModeInit()
pkgs := load.PackagesForBuild(pkgArgs)
if len(pkgs) == 0 {
base.Fatalf("no packages to test")
@ -454,8 +455,8 @@ func runTest(cmd *base.Command, args []string) {
testC = true
}
var b builder
b.init()
var b work.Builder
b.Init()
if cfg.BuildI {
cfg.BuildV = testV
@ -497,18 +498,18 @@ func runTest(cmd *base.Command, args []string) {
}
sort.Strings(all)
a := &action{}
a := &work.Action{}
for _, p := range load.PackagesForBuild(all) {
a.deps = append(a.deps, b.action(modeInstall, modeInstall, p))
a.Deps = append(a.Deps, b.Action(work.ModeInstall, work.ModeInstall, p))
}
b.do(a)
if !testC || a.failed {
b.Do(a)
if !testC || a.Failed {
return
}
b.init()
b.Init()
}
var builds, runs, prints []*action
var builds, runs, prints []*work.Action
if testCoverPaths != nil {
// Load packages that were asked about for coverage.
@ -570,13 +571,13 @@ func runTest(cmd *base.Command, args []string) {
}
// Ultimately the goal is to print the output.
root := &action{deps: prints}
root := &work.Action{Deps: prints}
// Force the printing of results to happen in order,
// one at a time.
for i, a := range prints {
if i > 0 {
a.deps = append(a.deps, prints[i-1])
a.Deps = append(a.Deps, prints[i-1])
}
}
@ -586,9 +587,9 @@ func runTest(cmd *base.Command, args []string) {
// Later runs must wait for the previous run's print.
for i, run := range runs {
if i == 0 {
run.deps = append(run.deps, builds...)
run.Deps = append(run.Deps, builds...)
} else {
run.deps = append(run.deps, prints[i-1])
run.Deps = append(run.Deps, prints[i-1])
}
}
}
@ -600,26 +601,26 @@ func runTest(cmd *base.Command, args []string) {
okBuild[p] = true
}
warned := false
for _, a := range actionList(root) {
if a.p == nil || okBuild[a.p] {
for _, a := range work.ActionList(root) {
if a.Package == nil || okBuild[a.Package] {
continue
}
okBuild[a.p] = true // warn at most once
okBuild[a.Package] = true // warn at most once
// Don't warn about packages being rebuilt because of
// things like coverage analysis.
for _, p1 := range a.p.Internal.Imports {
for _, p1 := range a.Package.Internal.Imports {
if p1.Internal.Fake {
a.p.Internal.Fake = true
a.Package.Internal.Fake = true
}
}
if a.f != nil && !okBuild[a.p] && !a.p.Internal.Fake && !a.p.Internal.Local {
if a.Func != nil && !okBuild[a.Package] && !a.Package.Internal.Fake && !a.Package.Internal.Local {
if !warned {
fmt.Fprintf(os.Stderr, "warning: building out-of-date packages:\n")
warned = true
}
fmt.Fprintf(os.Stderr, "\t%s\n", a.p.ImportPath)
fmt.Fprintf(os.Stderr, "\t%s\n", a.Package.ImportPath)
}
}
if warned {
@ -637,7 +638,7 @@ func runTest(cmd *base.Command, args []string) {
fmt.Fprintf(os.Stderr, "installing these packages with 'go test %s-i%s' will speed future tests.\n\n", extraOpts, args)
}
b.do(root)
b.Do(root)
}
var windowsBadWords = []string{
@ -647,11 +648,11 @@ var windowsBadWords = []string{
"update",
}
func builderTest(b *builder, p *load.Package) (buildAction, runAction, printAction *action, err error) {
func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, printAction *work.Action, err error) {
if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
build := b.action(modeBuild, modeBuild, p)
run := &action{p: p, deps: []*action{build}}
print := &action{f: builderNoTest, p: p, deps: []*action{run}}
build := b.Action(work.ModeBuild, work.ModeBuild, p)
run := &work.Action{Package: p, Deps: []*work.Action{build}}
print := &work.Action{Func: builderNoTest, Package: p, Deps: []*work.Action{run}}
return build, run, print, nil
}
@ -736,12 +737,12 @@ func builderTest(b *builder, p *load.Package) (buildAction, runAction, printActi
// $WORK/unicode/utf8/_test/unicode/utf8.a.
// We write the external test package archive to
// $WORK/unicode/utf8/_test/unicode/utf8_test.a.
testDir := filepath.Join(b.work, filepath.FromSlash(p.ImportPath+"/_test"))
ptestObj := buildToolchain.pkgpath(testDir, p)
testDir := filepath.Join(b.WorkDir, filepath.FromSlash(p.ImportPath+"/_test"))
ptestObj := work.BuildToolchain.Pkgpath(testDir, p)
// Create the directory for the .a files.
ptestDir, _ := filepath.Split(ptestObj)
if err := b.mkdir(ptestDir); err != nil {
if err := b.Mkdir(ptestDir); err != nil {
return nil, nil, nil, err
}
@ -898,7 +899,7 @@ func builderTest(b *builder, p *load.Package) (buildAction, runAction, printActi
if cfg.BuildContext.GOOS == "darwin" {
if cfg.BuildContext.GOARCH == "arm" || cfg.BuildContext.GOARCH == "arm64" {
t.IsIOS = true
t.NeedCgo = true
t.NeedOS = true
}
}
if t.TestMain == nil {
@ -922,24 +923,24 @@ func builderTest(b *builder, p *load.Package) (buildAction, runAction, printActi
load.ComputeStale(pmain)
if ptest != p {
a := b.action(modeBuild, modeBuild, ptest)
a.objdir = testDir + string(filepath.Separator) + "_obj_test" + string(filepath.Separator)
a.objpkg = ptestObj
a.target = ptestObj
a.link = false
a := b.Action(work.ModeBuild, work.ModeBuild, ptest)
a.Objdir = testDir + string(filepath.Separator) + "_obj_test" + string(filepath.Separator)
a.Objpkg = ptestObj
a.Target = ptestObj
a.Link = false
}
if pxtest != nil {
a := b.action(modeBuild, modeBuild, pxtest)
a.objdir = testDir + string(filepath.Separator) + "_obj_xtest" + string(filepath.Separator)
a.objpkg = buildToolchain.pkgpath(testDir, pxtest)
a.target = a.objpkg
a := b.Action(work.ModeBuild, work.ModeBuild, pxtest)
a.Objdir = testDir + string(filepath.Separator) + "_obj_xtest" + string(filepath.Separator)
a.Objpkg = work.BuildToolchain.Pkgpath(testDir, pxtest)
a.Target = a.Objpkg
}
a := b.action(modeBuild, modeBuild, pmain)
a.objdir = testDir + string(filepath.Separator)
a.objpkg = filepath.Join(testDir, "main.a")
a.target = filepath.Join(testDir, testBinary) + cfg.ExeSuffix
a := b.Action(work.ModeBuild, work.ModeBuild, pmain)
a.Objdir = testDir + string(filepath.Separator)
a.Objpkg = filepath.Join(testDir, "main.a")
a.Target = filepath.Join(testDir, testBinary) + cfg.ExeSuffix
if cfg.Goos == "windows" {
// There are many reserved words on Windows that,
// if used in the name of an executable, cause Windows
@ -965,7 +966,7 @@ func builderTest(b *builder, p *load.Package) (buildAction, runAction, printActi
// we could just do this always on Windows.
for _, bad := range windowsBadWords {
if strings.Contains(testBinary, bad) {
a.target = filepath.Join(testDir, "test.test") + cfg.ExeSuffix
a.Target = filepath.Join(testDir, "test.test") + cfg.ExeSuffix
break
}
}
@ -981,33 +982,33 @@ func builderTest(b *builder, p *load.Package) (buildAction, runAction, printActi
target = filepath.Join(base.Cwd, target)
}
}
buildAction = &action{
f: (*builder).install,
deps: []*action{buildAction},
p: pmain,
target: target,
buildAction = &work.Action{
Func: work.BuildInstallFunc,
Deps: []*work.Action{buildAction},
Package: pmain,
Target: target,
}
runAction = buildAction // make sure runAction != nil even if not running test
}
if testC {
printAction = &action{p: p, deps: []*action{runAction}} // nop
printAction = &work.Action{Package: p, Deps: []*work.Action{runAction}} // nop
} else {
// run test
runAction = &action{
f: builderRunTest,
deps: []*action{buildAction},
p: p,
ignoreFail: true,
runAction = &work.Action{
Func: builderRunTest,
Deps: []*work.Action{buildAction},
Package: p,
IgnoreFail: true,
}
cleanAction := &action{
f: builderCleanTest,
deps: []*action{runAction},
p: p,
cleanAction := &work.Action{
Func: builderCleanTest,
Deps: []*work.Action{runAction},
Package: p,
}
printAction = &action{
f: builderPrintTest,
deps: []*action{cleanAction},
p: p,
printAction = &work.Action{
Func: builderPrintTest,
Deps: []*work.Action{cleanAction},
Package: p,
}
}
@ -1060,7 +1061,7 @@ func recompileForTest(pmain, preal, ptest *load.Package, testDir string) {
}
}
// Update p.deps and p.Internal.Imports to use at test copies.
// Update p.Deps and p.Internal.Imports to use at test copies.
for i, dep := range p.Internal.Deps {
if p1 := testCopy[dep]; p1 != nil && p1 != dep {
split()
@ -1105,27 +1106,27 @@ func declareCoverVars(importPath string, files ...string) map[string]*load.Cover
var noTestsToRun = []byte("\ntesting: warning: no tests to run\n")
// builderRunTest is the action for running a test binary.
func builderRunTest(b *builder, a *action) error {
args := str.StringList(findExecCmd(), a.deps[0].target, testArgs)
a.testOutput = new(bytes.Buffer)
func builderRunTest(b *work.Builder, a *work.Action) error {
args := str.StringList(findExecCmd(), a.Deps[0].Target, testArgs)
a.TestOutput = new(bytes.Buffer)
if cfg.BuildN || cfg.BuildX {
b.showcmd("", "%s", strings.Join(args, " "))
b.Showcmd("", "%s", strings.Join(args, " "))
if cfg.BuildN {
return nil
}
}
if a.failed {
if a.Failed {
// We were unable to build the binary.
a.failed = false
fmt.Fprintf(a.testOutput, "FAIL\t%s [build failed]\n", a.p.ImportPath)
a.Failed = false
fmt.Fprintf(a.TestOutput, "FAIL\t%s [build failed]\n", a.Package.ImportPath)
base.SetExitStatus(1)
return nil
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = a.p.Dir
cmd.Dir = a.Package.Dir
cmd.Env = envForDir(cmd.Dir, cfg.OrigEnv)
var buf bytes.Buffer
if testStreamOutput {
@ -1138,7 +1139,7 @@ func builderRunTest(b *builder, a *action) error {
// If there are any local SWIG dependencies, we want to load
// the shared library from the build directory.
if a.p.UsesSwig() {
if a.Package.UsesSwig() {
env := cmd.Env
found := false
prefix := "LD_LIBRARY_PATH="
@ -1196,23 +1197,23 @@ func builderRunTest(b *builder, a *action) error {
if err == nil {
norun := ""
if testShowPass {
a.testOutput.Write(out)
a.TestOutput.Write(out)
}
if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
norun = " [no tests to run]"
}
fmt.Fprintf(a.testOutput, "ok \t%s\t%s%s%s\n", a.p.ImportPath, t, coveragePercentage(out), norun)
fmt.Fprintf(a.TestOutput, "ok \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun)
return nil
}
base.SetExitStatus(1)
if len(out) > 0 {
a.testOutput.Write(out)
a.TestOutput.Write(out)
// assume printing the test binary's exit status is superfluous
} else {
fmt.Fprintf(a.testOutput, "%s\n", err)
fmt.Fprintf(a.TestOutput, "%s\n", err)
}
fmt.Fprintf(a.testOutput, "FAIL\t%s\t%s\n", a.p.ImportPath, t)
fmt.Fprintf(a.TestOutput, "FAIL\t%s\t%s\n", a.Package.ImportPath, t)
return nil
}
@ -1237,28 +1238,28 @@ func coveragePercentage(out []byte) string {
}
// builderCleanTest is the action for cleaning up after a test.
func builderCleanTest(b *builder, a *action) error {
func builderCleanTest(b *work.Builder, a *work.Action) error {
if cfg.BuildWork {
return nil
}
run := a.deps[0]
testDir := filepath.Join(b.work, filepath.FromSlash(run.p.ImportPath+"/_test"))
run := a.Deps[0]
testDir := filepath.Join(b.WorkDir, filepath.FromSlash(run.Package.ImportPath+"/_test"))
os.RemoveAll(testDir)
return nil
}
// builderPrintTest is the action for printing a test result.
func builderPrintTest(b *builder, a *action) error {
clean := a.deps[0]
run := clean.deps[0]
os.Stdout.Write(run.testOutput.Bytes())
run.testOutput = nil
func builderPrintTest(b *work.Builder, a *work.Action) error {
clean := a.Deps[0]
run := clean.Deps[0]
os.Stdout.Write(run.TestOutput.Bytes())
run.TestOutput = nil
return nil
}
// builderNoTest is the action for testing a package with no test files.
func builderNoTest(b *builder, a *action) error {
fmt.Printf("? \t%s\t[no test files]\n", a.p.ImportPath)
func builderNoTest(b *work.Builder, a *work.Action) error {
fmt.Printf("? \t%s\t[no test files]\n", a.Package.ImportPath)
return nil
}

View File

@ -5,13 +5,16 @@
package main
import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"flag"
"fmt"
"os"
"strconv"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/str"
"cmd/go/internal/work"
)
// The flag handling part of go test is large and distracting.
@ -66,7 +69,7 @@ var testFlagDefn = []*testFlagSpec{
// add build flags to testFlagDefn
func init() {
var cmd base.Command
addBuildFlags(&cmd)
work.AddBuildFlags(&cmd)
cmd.Flag.VisitAll(func(f *flag.Flag) {
if f.Name == "v" {
// test overrides the build -v flag
@ -144,7 +147,7 @@ func testFlags(args []string) (packageNames, passToTest []string) {
testO = value
testNeedBinary = true
case "exec":
execCmd, err = splitQuotedFields(value)
execCmd, err = str.SplitQuotedFields(value)
if err != nil {
base.Fatalf("invalid flag argument for -%s: %v", f.name, err)
}

View File

@ -11,10 +11,11 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
"cmd/go/internal/work"
)
func init() {
addBuildFlags(cmdVet)
work.AddBuildFlags(cmdVet)
}
var cmdVet = &base.Command{