зеркало из https://github.com/golang/tools.git
go.tools/go/loader: simplify command-line syntax.
Previously, each word could be a package import path or a comma-separated list of *.go file names. Now, if the first word ends with ".go", all words are assumed to be Go source files. This makes it impossible to specify two ad-hoc packages from source files, but no-one needs that. FromArgs also takes a boolean indicating whether tests are wanted or not. Also: ssadump: add -test flag to set that boolean. For the oracle it's always true. LGTM=gri R=gri CC=golang-codereviews https://golang.org/cl/61470047
This commit is contained in:
Родитель
1f29e74bfa
Коммит
08fadac071
|
@ -9,7 +9,6 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
|
@ -32,6 +31,8 @@ L build distinct packages seria[L]ly instead of in parallel.
|
|||
N build [N]aive SSA form: don't replace local loads/stores with registers.
|
||||
`)
|
||||
|
||||
var testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.")
|
||||
|
||||
var runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.")
|
||||
|
||||
var interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter.
|
||||
|
@ -47,12 +48,13 @@ Use -help flag to display options.
|
|||
Examples:
|
||||
% ssadump -build=FPG hello.go # quickly dump SSA form of a single package
|
||||
% ssadump -run -interp=T hello.go # interpret a program, with tracing
|
||||
% ssadump -run unicode -- -test.v # interpret the unicode package's tests, verbosely
|
||||
% ssadump -run -test unicode -- -test.v # interpret the unicode package's tests, verbosely
|
||||
` + loader.FromArgsUsage +
|
||||
`
|
||||
When -run is specified, ssadump will find the first package that
|
||||
defines a main function and run it in the interpreter.
|
||||
If none is found, the tests of each package will be run instead.
|
||||
When -run is specified, ssadump will run the program.
|
||||
The entry point depends on the -test flag:
|
||||
if clear, it runs the first package named main.
|
||||
if set, it runs the tests of each package.
|
||||
`
|
||||
|
||||
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
|
||||
|
@ -70,6 +72,13 @@ func init() {
|
|||
}
|
||||
|
||||
func main() {
|
||||
if err := doMain(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ssadump: %s.\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func doMain() error {
|
||||
flag.Parse()
|
||||
args := flag.Args()
|
||||
|
||||
|
@ -109,7 +118,7 @@ func main() {
|
|||
case 'L':
|
||||
mode |= ssa.BuildSerially
|
||||
default:
|
||||
log.Fatalf("Unknown -build option: '%c'.", c)
|
||||
return fmt.Errorf("unknown -build option: '%c'", c)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +130,8 @@ func main() {
|
|||
case 'R':
|
||||
interpMode |= interp.DisableRecover
|
||||
default:
|
||||
log.Fatalf("Unknown -interp option: '%c'.", c)
|
||||
fmt.Fprintf(os.Stderr, "ssadump: unknown -interp option: '%c'.", c)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,16 +144,17 @@ func main() {
|
|||
if *cpuprofile != "" {
|
||||
f, err := os.Create(*cpuprofile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
pprof.StartCPUProfile(f)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
// Use the initial packages from the command line.
|
||||
args, err := conf.FromArgs(args)
|
||||
args, err := conf.FromArgs(args, *testFlag)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// The interpreter needs the runtime package.
|
||||
|
@ -154,7 +165,7 @@ func main() {
|
|||
// Load, parse and type-check the whole program.
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Create and build SSA-form program representation.
|
||||
|
@ -163,29 +174,38 @@ func main() {
|
|||
|
||||
// Run the interpreter.
|
||||
if *runFlag {
|
||||
// If a package named "main" defines func main, run that.
|
||||
// Otherwise run all packages' tests.
|
||||
var main *ssa.Package
|
||||
pkgs := prog.AllPackages()
|
||||
for _, pkg := range pkgs {
|
||||
if pkg.Object.Name() == "main" && pkg.Func("main") != nil {
|
||||
main = pkg
|
||||
break
|
||||
}
|
||||
}
|
||||
if main == nil && len(pkgs) > 0 {
|
||||
// TODO(adonovan): only run tests if -test flag specified.
|
||||
if *testFlag {
|
||||
// If -test, run all packages' tests.
|
||||
if len(pkgs) > 0 {
|
||||
main = prog.CreateTestMainPackage(pkgs...)
|
||||
}
|
||||
if main == nil {
|
||||
log.Fatal("No main package and no tests")
|
||||
return fmt.Errorf("no tests")
|
||||
}
|
||||
} else {
|
||||
// Otherwise, run main.main.
|
||||
for _, pkg := range pkgs {
|
||||
if pkg.Object.Name() == "main" {
|
||||
main = pkg
|
||||
if main.Func("main") == nil {
|
||||
return fmt.Errorf("no func main() in main package")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if main == nil {
|
||||
return fmt.Errorf("no main package")
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOARCH != conf.Build.GOARCH {
|
||||
log.Fatalf("Cross-interpretation is not yet supported (target has GOARCH %s, interpreter has %s).",
|
||||
conf.Build.GOARCH, runtime.GOARCH)
|
||||
if runtime.GOARCH != build.Default.GOARCH {
|
||||
return fmt.Errorf("cross-interpretation is not yet supported (target has GOARCH %s, interpreter has %s)",
|
||||
build.Default.GOARCH, runtime.GOARCH)
|
||||
}
|
||||
|
||||
interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Object.Path(), args)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
func loadFromArgs(args []string) (prog *loader.Program, rest []string, err error) {
|
||||
conf := &loader.Config{}
|
||||
rest, err = conf.FromArgs(args)
|
||||
rest, err = conf.FromArgs(args, true)
|
||||
if err == nil {
|
||||
prog, err = conf.Load()
|
||||
}
|
||||
|
@ -39,11 +39,10 @@ func TestLoadFromArgs(t *testing.T) {
|
|||
}
|
||||
|
||||
// Successful load.
|
||||
args = []string{"fmt", "errors", "testdata/a.go,testdata/b.go", "--", "surplus"}
|
||||
args = []string{"fmt", "errors", "--", "surplus"}
|
||||
prog, rest, err := loadFromArgs(args)
|
||||
if err != nil {
|
||||
t.Errorf("loadFromArgs(%q) failed: %s", args, err)
|
||||
return
|
||||
t.Fatalf("loadFromArgs(%q) failed: %s", args, err)
|
||||
}
|
||||
if got, want := fmt.Sprint(rest), "[surplus]"; got != want {
|
||||
t.Errorf("loadFromArgs(%q) rest: got %s, want %s", args, got, want)
|
||||
|
@ -54,7 +53,7 @@ func TestLoadFromArgs(t *testing.T) {
|
|||
pkgnames = append(pkgnames, info.Pkg.Path())
|
||||
}
|
||||
// Only the first import path (currently) contributes tests.
|
||||
if got, want := fmt.Sprint(pkgnames), "[fmt_test P]"; got != want {
|
||||
if got, want := fmt.Sprint(pkgnames), "[fmt_test]"; got != want {
|
||||
t.Errorf("Created: got %s, want %s", got, want)
|
||||
}
|
||||
|
||||
|
@ -82,3 +81,25 @@ func TestLoadFromArgs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromArgsSource(t *testing.T) {
|
||||
// mixture of *.go/non-go.
|
||||
args := []string{"testdata/a.go", "fmt"}
|
||||
prog, _, err := loadFromArgs(args)
|
||||
if err == nil {
|
||||
t.Errorf("loadFromArgs(%q) succeeded, want failure", args)
|
||||
} else {
|
||||
// "named files must be .go files: fmt": ok
|
||||
}
|
||||
|
||||
// successful load
|
||||
args = []string{"testdata/a.go", "testdata/b.go"}
|
||||
prog, _, err = loadFromArgs(args)
|
||||
if err != nil {
|
||||
t.Errorf("loadFromArgs(%q) failed: %s", args, err)
|
||||
return
|
||||
}
|
||||
if len(prog.Created) != 1 || prog.Created[0].Pkg.Path() != "P" {
|
||||
t.Errorf("loadFromArgs(%q): got %v, want [P]", prog.Created)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
// // Use the command-line arguments to specify
|
||||
// // a set of initial packages to load from source.
|
||||
// // See FromArgsUsage for help.
|
||||
// rest, err := conf.FromArgs(os.Args[1:])
|
||||
// rest, err := conf.FromArgs(os.Args[1:], wantTests)
|
||||
//
|
||||
// // Parse the specified files and create an ad-hoc package with path "foo".
|
||||
// // All files must have the same 'package' declaration.
|
||||
|
@ -228,15 +228,14 @@ func (conf *Config) ParseFile(filename string, src interface{}, mode parser.Mode
|
|||
// FromArgs may wish to include in their -help output.
|
||||
const FromArgsUsage = `
|
||||
<args> is a list of arguments denoting a set of initial packages.
|
||||
Each argument may take one of two forms:
|
||||
It may take one of two forms:
|
||||
|
||||
1. A comma-separated list of *.go source files.
|
||||
1. A list of *.go source files.
|
||||
|
||||
All of the specified files are loaded, parsed and type-checked
|
||||
as a single package.
|
||||
All the files must belong to the same directory.
|
||||
as a single package. All the files must belong to the same directory.
|
||||
|
||||
2. An import path.
|
||||
2. A list of import paths, each denoting a package.
|
||||
|
||||
The package's directory is found relative to the $GOROOT and
|
||||
$GOPATH using similar logic to 'go build', and the *.go files in
|
||||
|
@ -248,9 +247,8 @@ Each argument may take one of two forms:
|
|||
the non-*_test.go files are included in the primary package. Test
|
||||
files whose package declaration ends with "_test" are type-checked
|
||||
as another package, the 'external' test package, so that a single
|
||||
import path may denote two packages. This behaviour may be
|
||||
disabled by prefixing the import path with "notest:",
|
||||
e.g. "notest:fmt".
|
||||
import path may denote two packages. (Whether this behaviour is
|
||||
enabled is tool-specific, and may depend on additional flags.)
|
||||
|
||||
Due to current limitations in the type-checker, only the first
|
||||
import path of the command line will contribute any tests.
|
||||
|
@ -266,33 +264,40 @@ A '--' argument terminates the list of packages.
|
|||
// set of initial packages to be specified; see FromArgsUsage message
|
||||
// for details.
|
||||
//
|
||||
func (conf *Config) FromArgs(args []string) (rest []string, err error) {
|
||||
for len(args) > 0 {
|
||||
arg := args[0]
|
||||
args = args[1:]
|
||||
func (conf *Config) FromArgs(args []string, xtest bool) (rest []string, err error) {
|
||||
for i, arg := range args {
|
||||
if arg == "--" {
|
||||
rest = args[i+1:]
|
||||
args = args[:i]
|
||||
break // consume "--" and return the remaining args
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasSuffix(arg, ".go") {
|
||||
// Assume arg is a comma-separated list of *.go files
|
||||
if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
|
||||
// Assume args is a list of a *.go files
|
||||
// denoting a single ad-hoc package.
|
||||
err = conf.CreateFromFilenames("", strings.Split(arg, ",")...)
|
||||
} else {
|
||||
// Assume arg is a directory name denoting a
|
||||
// package, perhaps plus an external test
|
||||
// package unless prefixed by "notest:".
|
||||
if path := strings.TrimPrefix(arg, "notest:"); path != arg {
|
||||
conf.Import(path)
|
||||
} else {
|
||||
err = conf.ImportWithTests(path)
|
||||
for _, arg := range args {
|
||||
if !strings.HasSuffix(arg, ".go") {
|
||||
return nil, fmt.Errorf("named files must be .go files: %s", arg)
|
||||
}
|
||||
}
|
||||
err = conf.CreateFromFilenames("", args...)
|
||||
} else {
|
||||
// Assume args are directories each denoting a
|
||||
// package and (perhaps) an external test, iff xtest.
|
||||
for _, arg := range args {
|
||||
if xtest {
|
||||
err = conf.ImportWithTests(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
break
|
||||
}
|
||||
} else {
|
||||
conf.Import(arg)
|
||||
}
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CreateFromFilenames is a convenience function that parses the
|
||||
|
@ -371,7 +376,8 @@ func (conf *Config) Import(path string) {
|
|||
if conf.ImportPkgs == nil {
|
||||
conf.ImportPkgs = make(map[string]bool)
|
||||
}
|
||||
conf.ImportPkgs[path] = false // unaugmented source package
|
||||
// Subtle: adds value 'false' unless value is already true.
|
||||
conf.ImportPkgs[path] = conf.ImportPkgs[path] // unaugmented source package
|
||||
}
|
||||
|
||||
// PathEnclosingInterval returns the PackageInfo and ast.Node that
|
||||
|
|
|
@ -53,7 +53,7 @@ func TestStdlib(t *testing.T) {
|
|||
t0 := time.Now()
|
||||
|
||||
var conf loader.Config
|
||||
if _, err := conf.FromArgs(allPackages()); err != nil {
|
||||
if _, err := conf.FromArgs(allPackages(), true); err != nil {
|
||||
t.Errorf("FromArgs failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -225,7 +225,7 @@ func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *buil
|
|||
conf := loader.Config{Build: buildContext, SourceImports: true}
|
||||
|
||||
// Determine initial packages.
|
||||
args, err := conf.FromArgs(args)
|
||||
args, err := conf.FromArgs(args, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче