diff --git a/go/packages/gopackages/main.go b/go/packages/gopackages/main.go index bff298f7898..f202dff71da 100644 --- a/go/packages/gopackages/main.go +++ b/go/packages/gopackages/main.go @@ -9,116 +9,73 @@ package main import ( + "context" "encoding/json" "flag" "fmt" "go/types" - "log" "os" - "runtime" - "runtime/pprof" - "runtime/trace" "sort" "strings" "golang.org/x/tools/go/packages" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/tool" ) -// flags -var ( - depsFlag = flag.Bool("deps", false, "show dependencies too") - testFlag = flag.Bool("test", false, "include any tests implied by the patterns") - mode = flag.String("mode", "imports", "mode (one of files, imports, types, syntax, allsyntax)") - private = flag.Bool("private", false, "show non-exported declarations too") - printJSON = flag.Bool("json", false, "print package in JSON form") - - cpuprofile = flag.String("cpuprofile", "", "write CPU profile to this file") - memprofile = flag.String("memprofile", "", "write memory profile to this file") - traceFlag = flag.String("trace", "", "write trace log to this file") - - buildFlags stringListValue -) - -func init() { - flag.Var(&buildFlags, "buildflag", "pass argument to underlying build system (may be repeated)") +func main() { + tool.Main(context.Background(), &application{Mode: "imports"}, os.Args[1:]) } -func usage() { - fmt.Fprintln(os.Stderr, `Usage: gopackages [-deps] [-cgo] [-mode=...] [-private] package... +type application struct { + // Embed the basic profiling flags supported by the tool package + tool.Profile -The gopackages command loads, parses, type-checks, -and prints one or more Go packages. + Deps bool `flag:"deps" help:"show dependencies too"` + Test bool `flag:"test" help:"include any tests implied by the patterns"` + Mode string `flag:"mode" help:"mode (one of files, imports, types, syntax, allsyntax)"` + Private bool `flag:"private" help:"show non-exported declarations too"` + PrintJSON bool `flag:"json" help:"print package in JSON form"` + BuildFlags stringListValue `flag:"buildflag" help:"pass argument to underlying build system (may be repeated)"` +} +// Name implements tool.Application returning the binary name. +func (app *application) Name() string { return "gopackages" } + +// Usage implements tool.Application returning empty extra argument usage. +func (app *application) Usage() string { return "package..." } + +// ShortHelp implements tool.Application returning the main binary help. +func (app *application) ShortHelp() string { + return "gopackages loads, parses, type-checks, and prints one or more Go packages." +} + +// DetailedHelp implements tool.Application returning the main binary help. +func (app *application) DetailedHelp(f *flag.FlagSet) { + fmt.Fprint(f.Output(), ` Packages are specified using the notation of "go list", or other underlying build system. -Flags:`) - flag.PrintDefaults() +Flags: +`) + f.PrintDefaults() } -func main() { - log.SetPrefix("gopackages: ") - log.SetFlags(0) - flag.Usage = usage - flag.Parse() - - if len(flag.Args()) == 0 { - usage() - os.Exit(1) - } - - if *cpuprofile != "" { - f, err := os.Create(*cpuprofile) - if err != nil { - log.Fatal(err) - } - if err := pprof.StartCPUProfile(f); err != nil { - log.Fatal(err) - } - // NB: profile won't be written in case of error. - defer pprof.StopCPUProfile() - } - - if *traceFlag != "" { - f, err := os.Create(*traceFlag) - if err != nil { - log.Fatal(err) - } - if err := trace.Start(f); err != nil { - log.Fatal(err) - } - // NB: trace log won't be written in case of error. - defer func() { - trace.Stop() - log.Printf("To view the trace, run:\n$ go tool trace view %s", *traceFlag) - }() - } - - if *memprofile != "" { - f, err := os.Create(*memprofile) - if err != nil { - log.Fatal(err) - } - // NB: memprofile won't be written in case of error. - defer func() { - runtime.GC() // get up-to-date statistics - if err := pprof.WriteHeapProfile(f); err != nil { - log.Fatalf("Writing memory profile: %v", err) - } - f.Close() - }() +// Run takes the args after flag processing and performs the specified query. +func (app *application) Run(ctx context.Context, args ...string) error { + if len(args) == 0 { + return tool.CommandLineErrorf("not enough arguments") } // Load, parse, and type-check the packages named on the command line. cfg := &packages.Config{ Mode: packages.LoadSyntax, - Tests: *testFlag, - BuildFlags: buildFlags, + Tests: app.Test, + BuildFlags: app.BuildFlags, } // -mode flag - switch strings.ToLower(*mode) { + switch strings.ToLower(app.Mode) { case "files": cfg.Mode = packages.LoadFiles case "imports": @@ -130,16 +87,16 @@ func main() { case "allsyntax": cfg.Mode = packages.LoadAllSyntax default: - log.Fatalf("invalid mode: %s", *mode) + return tool.CommandLineErrorf("invalid mode: %s", app.Mode) } - lpkgs, err := packages.Load(cfg, flag.Args()...) + lpkgs, err := packages.Load(cfg, args...) if err != nil { - log.Fatal(err) + return err } // -deps: print dependencies too. - if *depsFlag { + if app.Deps { // We can't use packages.All because // we need an ordered traversal. var all []*packages.Package // postorder @@ -169,12 +126,13 @@ func main() { } for _, lpkg := range lpkgs { - print(lpkg) + app.print(lpkg) } + return nil } -func print(lpkg *packages.Package) { - if *printJSON { +func (app *application) print(lpkg *packages.Package) { + if app.PrintJSON { data, _ := json.MarshalIndent(lpkg, "", "\t") os.Stdout.Write(data) return @@ -237,14 +195,14 @@ func print(lpkg *packages.Package) { scope := lpkg.Types.Scope() for _, name := range scope.Names() { obj := scope.Lookup(name) - if !obj.Exported() && !*private { + if !obj.Exported() && !app.Private { continue // skip unexported names } fmt.Printf("\t%s\n", types.ObjectString(obj, qual)) if _, ok := obj.(*types.TypeName); ok { for _, meth := range typeutil.IntuitiveMethodSet(obj.Type(), nil) { - if !meth.Obj().Exported() && !*private { + if !meth.Obj().Exported() && !app.Private { continue // skip unexported names } fmt.Printf("\t%s\n", types.SelectionString(meth, qual))