wire: use subcommands package, improving help (#173)
This commit is contained in:
265
cmd/wire/main.go
265
cmd/wire/main.go
@@ -19,90 +19,105 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/subcommands"
|
||||||
"github.com/google/wire/internal/wire"
|
"github.com/google/wire/internal/wire"
|
||||||
"github.com/pmezard/go-difflib/difflib"
|
"github.com/pmezard/go-difflib/difflib"
|
||||||
"golang.org/x/tools/go/types/typeutil"
|
"golang.org/x/tools/go/types/typeutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = "usage: wire [gen|diff|show|check] [...]"
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
subcommands.Register(subcommands.CommandsCommand(), "")
|
||||||
exitCode = 0
|
subcommands.Register(subcommands.FlagsCommand(), "")
|
||||||
err error
|
subcommands.Register(subcommands.HelpCommand(), "")
|
||||||
)
|
subcommands.Register(&checkCmd{}, "")
|
||||||
switch {
|
subcommands.Register(&diffCmd{}, "")
|
||||||
case len(os.Args) == 2 && (os.Args[1] == "help" || os.Args[1] == "-h" || os.Args[1] == "-help" || os.Args[1] == "--help"):
|
subcommands.Register(&genCmd{}, "")
|
||||||
fmt.Fprintln(os.Stderr, usage)
|
subcommands.Register(&showCmd{}, "")
|
||||||
os.Exit(0)
|
flag.Parse()
|
||||||
case len(os.Args) == 2 && os.Args[1] == "show":
|
|
||||||
err = show(".")
|
// Initialize the default logger to log to stderr.
|
||||||
case len(os.Args) > 2 && os.Args[1] == "show":
|
log.SetFlags(0)
|
||||||
err = show(os.Args[2:]...)
|
log.SetPrefix("wire: ")
|
||||||
case len(os.Args) == 2 && os.Args[1] == "check":
|
log.SetOutput(os.Stderr)
|
||||||
err = check(".")
|
|
||||||
case len(os.Args) > 2 && os.Args[1] == "check":
|
// TODO(rvangent): Use subcommands's VisitCommands instead of hardcoded map,
|
||||||
err = check(os.Args[2:]...)
|
// once there is a release that contains it:
|
||||||
case len(os.Args) == 2 && os.Args[1] == "diff":
|
// allCmds := map[string]bool{}
|
||||||
exitCode, err = diff(".")
|
// subcommands.DefaultCommander.VisitCommands(func(_ *subcommands.CommandGroup, cmd subcommands.Command) { allCmds[cmd.Name()] = true })
|
||||||
case len(os.Args) > 2 && os.Args[1] == "diff":
|
allCmds := map[string]bool{
|
||||||
exitCode, err = diff(os.Args[2:]...)
|
"commands": true, // builtin
|
||||||
case len(os.Args) == 2 && os.Args[1] == "gen":
|
"help": true, // builtin
|
||||||
err = generate(".")
|
"flags": true, // builtin
|
||||||
case len(os.Args) > 2 && os.Args[1] == "gen":
|
"check": true,
|
||||||
err = generate(os.Args[2:]...)
|
"diff": true,
|
||||||
// No explicit command given, assume "gen".
|
"gen": true,
|
||||||
case len(os.Args) == 1:
|
"show": true,
|
||||||
err = generate(".")
|
|
||||||
case len(os.Args) > 1:
|
|
||||||
err = generate(os.Args[1:]...)
|
|
||||||
default:
|
|
||||||
fmt.Fprintln(os.Stderr, usage)
|
|
||||||
exitCode = 64
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
// Default to running the "gen" command.
|
||||||
fmt.Fprintln(os.Stderr, "wire:", err)
|
if args := flag.Args(); len(args) == 0 || !allCmds[args[0]] {
|
||||||
// Don't override more specific error codes from above
|
genCmd := &genCmd{}
|
||||||
// (e.g., diff returns 2 on error).
|
os.Exit(int(genCmd.Execute(context.Background(), flag.CommandLine)))
|
||||||
if exitCode == 0 {
|
|
||||||
exitCode = 1
|
|
||||||
}
|
}
|
||||||
}
|
os.Exit(int(subcommands.Execute(context.Background())))
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate runs the gen subcommand.
|
// packages returns the slice of packages to run wire over based on f.
|
||||||
//
|
// It defaults to ".".
|
||||||
// Given one or more packages, gen will create the wire_gen.go file for each.
|
func packages(f *flag.FlagSet) []string {
|
||||||
func generate(pkgs ...string) error {
|
pkgs := f.Args()
|
||||||
|
if len(pkgs) == 0 {
|
||||||
|
pkgs = []string{"."}
|
||||||
|
}
|
||||||
|
return pkgs
|
||||||
|
}
|
||||||
|
|
||||||
|
type genCmd struct{}
|
||||||
|
|
||||||
|
func (*genCmd) Name() string { return "gen" }
|
||||||
|
func (*genCmd) Synopsis() string {
|
||||||
|
return "generate the wire_gen.go file for each package"
|
||||||
|
}
|
||||||
|
func (*genCmd) Usage() string {
|
||||||
|
return `gen [packages]
|
||||||
|
|
||||||
|
Given one or more packages, gen creates the wire_gen.go file for each.
|
||||||
|
|
||||||
|
If no packages are listed, it defaults to ".".
|
||||||
|
`
|
||||||
|
}
|
||||||
|
func (*genCmd) SetFlags(_ *flag.FlagSet) {}
|
||||||
|
func (*genCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Println("failed to get working directory: ", err)
|
||||||
|
return subcommands.ExitFailure
|
||||||
}
|
}
|
||||||
outs, errs := wire.Generate(context.Background(), wd, os.Environ(), pkgs)
|
outs, errs := wire.Generate(ctx, wd, os.Environ(), packages(f))
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
logErrors(errs)
|
logErrors(errs)
|
||||||
return errors.New("generate failed")
|
log.Println("generate failed")
|
||||||
|
return subcommands.ExitFailure
|
||||||
}
|
}
|
||||||
if len(outs) == 0 {
|
if len(outs) == 0 {
|
||||||
return nil
|
return subcommands.ExitSuccess
|
||||||
}
|
}
|
||||||
success := true
|
success := true
|
||||||
for _, out := range outs {
|
for _, out := range outs {
|
||||||
if len(out.Errs) > 0 {
|
if len(out.Errs) > 0 {
|
||||||
fmt.Fprintf(os.Stderr, "%s: generate failed\n", out.PkgPath)
|
|
||||||
logErrors(out.Errs)
|
logErrors(out.Errs)
|
||||||
|
log.Printf("%s: generate failed\n", out.PkgPath)
|
||||||
success = false
|
success = false
|
||||||
}
|
}
|
||||||
if len(out.Content) == 0 {
|
if len(out.Content) == 0 {
|
||||||
@@ -110,53 +125,63 @@ func generate(pkgs ...string) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := out.Commit(); err == nil {
|
if err := out.Commit(); err == nil {
|
||||||
fmt.Fprintf(os.Stderr, "%s: wrote %s\n", out.PkgPath, out.OutputPath)
|
log.Printf("%s: wrote %s\n", out.PkgPath, out.OutputPath)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(os.Stderr, "%s: failed to write %s: %v\n", out.PkgPath, out.OutputPath, err)
|
log.Printf("%s: failed to write %s: %v\n", out.PkgPath, out.OutputPath, err)
|
||||||
success = false
|
success = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !success {
|
if !success {
|
||||||
return errors.New("at least one generate failure")
|
log.Println("at least one generate failure")
|
||||||
|
return subcommands.ExitFailure
|
||||||
}
|
}
|
||||||
return nil
|
return subcommands.ExitSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
// diff runs the diff subcommand.
|
type diffCmd struct{}
|
||||||
//
|
|
||||||
// Given one or more packages, diff will generate the content for the
|
func (*diffCmd) Name() string { return "diff" }
|
||||||
// wire_gen.go file, and output the diff against the existing file.
|
func (*diffCmd) Synopsis() string {
|
||||||
//
|
return "output a diff between existing wire_gen.go files and what gen would generate"
|
||||||
// Similar to the diff command, it returns 0 if no diff, 1 if different, 2
|
}
|
||||||
// plus an error if trouble.
|
func (*diffCmd) Usage() string {
|
||||||
func diff(pkgs ...string) (int, error) {
|
return `diff [packages]
|
||||||
errReturn := func(err error) (int, error) {
|
|
||||||
return 2, err
|
Given one or more packages, diff generates the content for their wire_gen.go
|
||||||
}
|
files and outputs the diff against the existing files.
|
||||||
okReturn := func(hadDiff bool) (int, error) {
|
|
||||||
if hadDiff {
|
If no packages are listed, it defaults to ".".
|
||||||
return 1, nil
|
|
||||||
}
|
Similar to the diff command, it returns 0 if no diff, 1 if different, 2
|
||||||
return 0, nil
|
plus an error if trouble.
|
||||||
}
|
`
|
||||||
|
}
|
||||||
|
func (*diffCmd) SetFlags(_ *flag.FlagSet) {}
|
||||||
|
func (*diffCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||||
|
const (
|
||||||
|
errReturn = subcommands.ExitStatus(2)
|
||||||
|
diffReturn = subcommands.ExitStatus(1)
|
||||||
|
)
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errReturn(err)
|
log.Println("failed to get working directory: ", err)
|
||||||
|
return errReturn
|
||||||
}
|
}
|
||||||
outs, errs := wire.Generate(context.Background(), wd, os.Environ(), pkgs)
|
outs, errs := wire.Generate(ctx, wd, os.Environ(), packages(f))
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
logErrors(errs)
|
logErrors(errs)
|
||||||
return errReturn(errors.New("generate failed"))
|
log.Println("generate failed")
|
||||||
|
return errReturn
|
||||||
}
|
}
|
||||||
if len(outs) == 0 {
|
if len(outs) == 0 {
|
||||||
return okReturn(false)
|
return subcommands.ExitSuccess
|
||||||
}
|
}
|
||||||
success := true
|
success := true
|
||||||
hadDiff := false
|
hadDiff := false
|
||||||
for _, out := range outs {
|
for _, out := range outs {
|
||||||
if len(out.Errs) > 0 {
|
if len(out.Errs) > 0 {
|
||||||
fmt.Fprintf(os.Stderr, "%s: generate failed\n", out.PkgPath)
|
|
||||||
logErrors(out.Errs)
|
logErrors(out.Errs)
|
||||||
|
log.Printf("%s: generate failed\n", out.PkgPath)
|
||||||
success = false
|
success = false
|
||||||
}
|
}
|
||||||
if len(out.Content) == 0 {
|
if len(out.Content) == 0 {
|
||||||
@@ -170,32 +195,50 @@ func diff(pkgs ...string) (int, error) {
|
|||||||
B: difflib.SplitLines(string(out.Content)),
|
B: difflib.SplitLines(string(out.Content)),
|
||||||
}); err == nil {
|
}); err == nil {
|
||||||
if diff != "" {
|
if diff != "" {
|
||||||
fmt.Fprintf(os.Stdout, "%s: diff from %s:\n%s", out.PkgPath, out.OutputPath, diff)
|
// Print the actual diff to stdout, not stderr.
|
||||||
|
fmt.Printf("%s: diff from %s:\n%s\n", out.PkgPath, out.OutputPath, diff)
|
||||||
hadDiff = true
|
hadDiff = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(os.Stderr, "%s: failed to diff %s: %v\n", out.PkgPath, out.OutputPath, err)
|
log.Printf("%s: failed to diff %s: %v\n", out.PkgPath, out.OutputPath, err)
|
||||||
success = false
|
success = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !success {
|
if !success {
|
||||||
return errReturn(errors.New("at least one generate failure"))
|
log.Println("at least one generate failure")
|
||||||
|
return errReturn
|
||||||
}
|
}
|
||||||
return okReturn(hadDiff)
|
if hadDiff {
|
||||||
|
return diffReturn
|
||||||
|
}
|
||||||
|
return subcommands.ExitSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
// show runs the show subcommand.
|
type showCmd struct{}
|
||||||
//
|
|
||||||
// Given one or more packages, show will find all the provider sets
|
func (*showCmd) Name() string { return "show" }
|
||||||
// declared as top-level variables and print what other provider sets it
|
func (*showCmd) Synopsis() string {
|
||||||
// imports and what outputs it can produce, given possible inputs.
|
return "describe all top-level provider sets"
|
||||||
// It also lists any injector functions defined in the package.
|
}
|
||||||
func show(pkgs ...string) error {
|
func (*showCmd) Usage() string {
|
||||||
|
return `show [packages]
|
||||||
|
|
||||||
|
Given one or more packages, show finds all the provider sets declared as
|
||||||
|
top-level variables and prints what other provider sets they import and what
|
||||||
|
outputs they can produce, given possible inputs. It also lists any injector
|
||||||
|
functions defined in the package.
|
||||||
|
|
||||||
|
If no packages are listed, it defaults to ".".
|
||||||
|
`
|
||||||
|
}
|
||||||
|
func (*showCmd) SetFlags(_ *flag.FlagSet) {}
|
||||||
|
func (*showCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Println("failed to get working directory: ", err)
|
||||||
|
return subcommands.ExitFailure
|
||||||
}
|
}
|
||||||
info, errs := wire.Load(context.Background(), wd, os.Environ(), pkgs)
|
info, errs := wire.Load(ctx, wd, os.Environ(), packages(f))
|
||||||
if info != nil {
|
if info != nil {
|
||||||
keys := make([]wire.ProviderSetID, 0, len(info.Sets))
|
keys := make([]wire.ProviderSetID, 0, len(info.Sets))
|
||||||
for k := range info.Sets {
|
for k := range info.Sets {
|
||||||
@@ -261,27 +304,41 @@ func show(pkgs ...string) error {
|
|||||||
}
|
}
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
logErrors(errs)
|
logErrors(errs)
|
||||||
return errors.New("error loading packages")
|
log.Println("error loading packages")
|
||||||
|
return subcommands.ExitFailure
|
||||||
}
|
}
|
||||||
return nil
|
return subcommands.ExitSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
// check runs the check subcommand.
|
type checkCmd struct{}
|
||||||
//
|
|
||||||
// Given one or more packages, check will print any type-checking or
|
func (*checkCmd) Name() string { return "check" }
|
||||||
// Wire errors found with top-level variable provider sets or injector
|
func (*checkCmd) Synopsis() string {
|
||||||
// functions.
|
return "print any Wire errors found"
|
||||||
func check(pkgs ...string) error {
|
}
|
||||||
|
func (*checkCmd) Usage() string {
|
||||||
|
return `check [packages]
|
||||||
|
|
||||||
|
Given one or more packages, check prints any type-checking or Wire errors
|
||||||
|
found with top-level variable provider sets or injector functions.
|
||||||
|
|
||||||
|
If no packages are listed, it defaults to ".".
|
||||||
|
`
|
||||||
|
}
|
||||||
|
func (*checkCmd) SetFlags(_ *flag.FlagSet) {}
|
||||||
|
func (*checkCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Println("failed to get working directory: ", err)
|
||||||
|
return subcommands.ExitFailure
|
||||||
}
|
}
|
||||||
_, errs := wire.Load(context.Background(), wd, os.Environ(), pkgs)
|
_, errs := wire.Load(ctx, wd, os.Environ(), packages(f))
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
logErrors(errs)
|
logErrors(errs)
|
||||||
return errors.New("error loading packages")
|
log.Println("error loading packages")
|
||||||
|
return subcommands.ExitFailure
|
||||||
}
|
}
|
||||||
return nil
|
return subcommands.ExitSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
type outGroup struct {
|
type outGroup struct {
|
||||||
@@ -503,6 +560,6 @@ func formatProviderSetName(importPath, varName string) string {
|
|||||||
|
|
||||||
func logErrors(errs []error) {
|
func logErrors(errs []error) {
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
fmt.Fprintln(os.Stderr, strings.Replace(err.Error(), "\n", "\n\t", -1))
|
log.Println(strings.Replace(err.Error(), "\n", "\n\t", -1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -2,6 +2,7 @@ module github.com/google/wire
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/go-cmp v0.2.0
|
github.com/google/go-cmp v0.2.0
|
||||||
|
github.com/google/subcommands v1.0.1
|
||||||
github.com/pmezard/go-difflib v1.0.0
|
github.com/pmezard/go-difflib v1.0.0
|
||||||
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b
|
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b
|
||||||
)
|
)
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -1,5 +1,7 @@
|
|||||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
|
||||||
|
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
github.com/google/subcommands
|
||||||
github.com/google/wire
|
github.com/google/wire
|
||||||
github.com/pmezard/go-difflib
|
github.com/pmezard/go-difflib
|
||||||
golang.org/x/tools
|
golang.org/x/tools
|
||||||
|
|||||||
Reference in New Issue
Block a user