forked from cerc-io/plugeth
289b30715d
This commit converts the dependency management from Godeps to the vendor folder, also switching the tool from godep to trash. Since the upstream tool lacks a few features proposed via a few PRs, until those PRs are merged in (if), use github.com/karalabe/trash. You can update dependencies via trash --update. All dependencies have been updated to their latest version. Parts of the build system are reworked to drop old notions of Godeps and invocation of the go vet command so that it doesn't run against the vendor folder, as that will just blow up during vetting. The conversion drops OpenCL (and hence GPU mining support) from ethash and our codebase. The short reasoning is that there's noone to maintain and having opencl libs in our deps messes up builds as go install ./... tries to build them, failing with unsatisfied link errors for the C OpenCL deps. golang.org/x/net/context is not vendored in. We expect it to be fetched by the user (i.e. using go get). To keep ci.go builds reproducible the package is "vendored" in build/_vendor.
498 lines
12 KiB
Go
498 lines
12 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
|
|
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
|
|
runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
|
|
|
|
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
|
|
|
|
errNonFuncAction = NewExitError("ERROR invalid Action type. "+
|
|
fmt.Sprintf("Must be a func of type `cli.ActionFunc`. %s", contactSysadmin)+
|
|
fmt.Sprintf("See %s", appActionDeprecationURL), 2)
|
|
errInvalidActionSignature = NewExitError("ERROR invalid Action signature. "+
|
|
fmt.Sprintf("Must be `cli.ActionFunc`. %s", contactSysadmin)+
|
|
fmt.Sprintf("See %s", appActionDeprecationURL), 2)
|
|
)
|
|
|
|
// App is the main structure of a cli application. It is recommended that
|
|
// an app be created with the cli.NewApp() function
|
|
type App struct {
|
|
// The name of the program. Defaults to path.Base(os.Args[0])
|
|
Name string
|
|
// Full name of command for help, defaults to Name
|
|
HelpName string
|
|
// Description of the program.
|
|
Usage string
|
|
// Text to override the USAGE section of help
|
|
UsageText string
|
|
// Description of the program argument format.
|
|
ArgsUsage string
|
|
// Version of the program
|
|
Version string
|
|
// List of commands to execute
|
|
Commands []Command
|
|
// List of flags to parse
|
|
Flags []Flag
|
|
// Boolean to enable bash completion commands
|
|
EnableBashCompletion bool
|
|
// Boolean to hide built-in help command
|
|
HideHelp bool
|
|
// Boolean to hide built-in version flag and the VERSION section of help
|
|
HideVersion bool
|
|
// Populate on app startup, only gettable through method Categories()
|
|
categories CommandCategories
|
|
// An action to execute when the bash-completion flag is set
|
|
BashComplete BashCompleteFunc
|
|
// An action to execute before any subcommands are run, but after the context is ready
|
|
// If a non-nil error is returned, no subcommands are run
|
|
Before BeforeFunc
|
|
// An action to execute after any subcommands are run, but after the subcommand has finished
|
|
// It is run even if Action() panics
|
|
After AfterFunc
|
|
// The action to execute when no subcommands are specified
|
|
// Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
|
|
// *Note*: support for the deprecated `Action` signature will be removed in a future version
|
|
Action interface{}
|
|
|
|
// Execute this function if the proper command cannot be found
|
|
CommandNotFound CommandNotFoundFunc
|
|
// Execute this function if an usage error occurs
|
|
OnUsageError OnUsageErrorFunc
|
|
// Compilation date
|
|
Compiled time.Time
|
|
// List of all authors who contributed
|
|
Authors []Author
|
|
// Copyright of the binary if any
|
|
Copyright string
|
|
// Name of Author (Note: Use App.Authors, this is deprecated)
|
|
Author string
|
|
// Email of Author (Note: Use App.Authors, this is deprecated)
|
|
Email string
|
|
// Writer writer to write output to
|
|
Writer io.Writer
|
|
// ErrWriter writes error output
|
|
ErrWriter io.Writer
|
|
// Other custom info
|
|
Metadata map[string]interface{}
|
|
|
|
didSetup bool
|
|
}
|
|
|
|
// Tries to find out when this binary was compiled.
|
|
// Returns the current time if it fails to find it.
|
|
func compileTime() time.Time {
|
|
info, err := os.Stat(os.Args[0])
|
|
if err != nil {
|
|
return time.Now()
|
|
}
|
|
return info.ModTime()
|
|
}
|
|
|
|
// NewApp creates a new cli Application with some reasonable defaults for Name,
|
|
// Usage, Version and Action.
|
|
func NewApp() *App {
|
|
return &App{
|
|
Name: filepath.Base(os.Args[0]),
|
|
HelpName: filepath.Base(os.Args[0]),
|
|
Usage: "A new cli application",
|
|
UsageText: "",
|
|
Version: "0.0.0",
|
|
BashComplete: DefaultAppComplete,
|
|
Action: helpCommand.Action,
|
|
Compiled: compileTime(),
|
|
Writer: os.Stdout,
|
|
}
|
|
}
|
|
|
|
// Setup runs initialization code to ensure all data structures are ready for
|
|
// `Run` or inspection prior to `Run`. It is internally called by `Run`, but
|
|
// will return early if setup has already happened.
|
|
func (a *App) Setup() {
|
|
if a.didSetup {
|
|
return
|
|
}
|
|
|
|
a.didSetup = true
|
|
|
|
if a.Author != "" || a.Email != "" {
|
|
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
|
|
}
|
|
|
|
newCmds := []Command{}
|
|
for _, c := range a.Commands {
|
|
if c.HelpName == "" {
|
|
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
|
|
}
|
|
newCmds = append(newCmds, c)
|
|
}
|
|
a.Commands = newCmds
|
|
|
|
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
|
a.Commands = append(a.Commands, helpCommand)
|
|
if (HelpFlag != BoolFlag{}) {
|
|
a.appendFlag(HelpFlag)
|
|
}
|
|
}
|
|
|
|
if a.EnableBashCompletion {
|
|
a.appendFlag(BashCompletionFlag)
|
|
}
|
|
|
|
if !a.HideVersion {
|
|
a.appendFlag(VersionFlag)
|
|
}
|
|
|
|
a.categories = CommandCategories{}
|
|
for _, command := range a.Commands {
|
|
a.categories = a.categories.AddCommand(command.Category, command)
|
|
}
|
|
sort.Sort(a.categories)
|
|
}
|
|
|
|
// Run is the entry point to the cli app. Parses the arguments slice and routes
|
|
// to the proper flag/args combination
|
|
func (a *App) Run(arguments []string) (err error) {
|
|
a.Setup()
|
|
|
|
// parse flags
|
|
set := flagSet(a.Name, a.Flags)
|
|
set.SetOutput(ioutil.Discard)
|
|
err = set.Parse(arguments[1:])
|
|
nerr := normalizeFlags(a.Flags, set)
|
|
context := NewContext(a, set, nil)
|
|
if nerr != nil {
|
|
fmt.Fprintln(a.Writer, nerr)
|
|
ShowAppHelp(context)
|
|
return nerr
|
|
}
|
|
|
|
if checkCompletions(context) {
|
|
return nil
|
|
}
|
|
|
|
if err != nil {
|
|
if a.OnUsageError != nil {
|
|
err := a.OnUsageError(context, err, false)
|
|
HandleExitCoder(err)
|
|
return err
|
|
}
|
|
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
|
|
ShowAppHelp(context)
|
|
return err
|
|
}
|
|
|
|
if !a.HideHelp && checkHelp(context) {
|
|
ShowAppHelp(context)
|
|
return nil
|
|
}
|
|
|
|
if !a.HideVersion && checkVersion(context) {
|
|
ShowVersion(context)
|
|
return nil
|
|
}
|
|
|
|
if a.After != nil {
|
|
defer func() {
|
|
if afterErr := a.After(context); afterErr != nil {
|
|
if err != nil {
|
|
err = NewMultiError(err, afterErr)
|
|
} else {
|
|
err = afterErr
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
if a.Before != nil {
|
|
beforeErr := a.Before(context)
|
|
if beforeErr != nil {
|
|
fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
|
|
ShowAppHelp(context)
|
|
HandleExitCoder(beforeErr)
|
|
err = beforeErr
|
|
return err
|
|
}
|
|
}
|
|
|
|
args := context.Args()
|
|
if args.Present() {
|
|
name := args.First()
|
|
c := a.Command(name)
|
|
if c != nil {
|
|
return c.Run(context)
|
|
}
|
|
}
|
|
|
|
// Run default Action
|
|
err = HandleAction(a.Action, context)
|
|
|
|
HandleExitCoder(err)
|
|
return err
|
|
}
|
|
|
|
// RunAndExitOnError calls .Run() and exits non-zero if an error was returned
|
|
//
|
|
// Deprecated: instead you should return an error that fulfills cli.ExitCoder
|
|
// to cli.App.Run. This will cause the application to exit with the given eror
|
|
// code in the cli.ExitCoder
|
|
func (a *App) RunAndExitOnError() {
|
|
if err := a.Run(os.Args); err != nil {
|
|
fmt.Fprintln(a.errWriter(), err)
|
|
OsExiter(1)
|
|
}
|
|
}
|
|
|
|
// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
|
|
// generate command-specific flags
|
|
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|
// append help to commands
|
|
if len(a.Commands) > 0 {
|
|
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
|
a.Commands = append(a.Commands, helpCommand)
|
|
if (HelpFlag != BoolFlag{}) {
|
|
a.appendFlag(HelpFlag)
|
|
}
|
|
}
|
|
}
|
|
|
|
newCmds := []Command{}
|
|
for _, c := range a.Commands {
|
|
if c.HelpName == "" {
|
|
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
|
|
}
|
|
newCmds = append(newCmds, c)
|
|
}
|
|
a.Commands = newCmds
|
|
|
|
// append flags
|
|
if a.EnableBashCompletion {
|
|
a.appendFlag(BashCompletionFlag)
|
|
}
|
|
|
|
// parse flags
|
|
set := flagSet(a.Name, a.Flags)
|
|
set.SetOutput(ioutil.Discard)
|
|
err = set.Parse(ctx.Args().Tail())
|
|
nerr := normalizeFlags(a.Flags, set)
|
|
context := NewContext(a, set, ctx)
|
|
|
|
if nerr != nil {
|
|
fmt.Fprintln(a.Writer, nerr)
|
|
fmt.Fprintln(a.Writer)
|
|
if len(a.Commands) > 0 {
|
|
ShowSubcommandHelp(context)
|
|
} else {
|
|
ShowCommandHelp(ctx, context.Args().First())
|
|
}
|
|
return nerr
|
|
}
|
|
|
|
if checkCompletions(context) {
|
|
return nil
|
|
}
|
|
|
|
if err != nil {
|
|
if a.OnUsageError != nil {
|
|
err = a.OnUsageError(context, err, true)
|
|
HandleExitCoder(err)
|
|
return err
|
|
}
|
|
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
|
|
ShowSubcommandHelp(context)
|
|
return err
|
|
}
|
|
|
|
if len(a.Commands) > 0 {
|
|
if checkSubcommandHelp(context) {
|
|
return nil
|
|
}
|
|
} else {
|
|
if checkCommandHelp(ctx, context.Args().First()) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if a.After != nil {
|
|
defer func() {
|
|
afterErr := a.After(context)
|
|
if afterErr != nil {
|
|
HandleExitCoder(err)
|
|
if err != nil {
|
|
err = NewMultiError(err, afterErr)
|
|
} else {
|
|
err = afterErr
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
if a.Before != nil {
|
|
beforeErr := a.Before(context)
|
|
if beforeErr != nil {
|
|
HandleExitCoder(beforeErr)
|
|
err = beforeErr
|
|
return err
|
|
}
|
|
}
|
|
|
|
args := context.Args()
|
|
if args.Present() {
|
|
name := args.First()
|
|
c := a.Command(name)
|
|
if c != nil {
|
|
return c.Run(context)
|
|
}
|
|
}
|
|
|
|
// Run default Action
|
|
err = HandleAction(a.Action, context)
|
|
|
|
HandleExitCoder(err)
|
|
return err
|
|
}
|
|
|
|
// Command returns the named command on App. Returns nil if the command does not exist
|
|
func (a *App) Command(name string) *Command {
|
|
for _, c := range a.Commands {
|
|
if c.HasName(name) {
|
|
return &c
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Categories returns a slice containing all the categories with the commands they contain
|
|
func (a *App) Categories() CommandCategories {
|
|
return a.categories
|
|
}
|
|
|
|
// VisibleCategories returns a slice of categories and commands that are
|
|
// Hidden=false
|
|
func (a *App) VisibleCategories() []*CommandCategory {
|
|
ret := []*CommandCategory{}
|
|
for _, category := range a.categories {
|
|
if visible := func() *CommandCategory {
|
|
for _, command := range category.Commands {
|
|
if !command.Hidden {
|
|
return category
|
|
}
|
|
}
|
|
return nil
|
|
}(); visible != nil {
|
|
ret = append(ret, visible)
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// VisibleCommands returns a slice of the Commands with Hidden=false
|
|
func (a *App) VisibleCommands() []Command {
|
|
ret := []Command{}
|
|
for _, command := range a.Commands {
|
|
if !command.Hidden {
|
|
ret = append(ret, command)
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// VisibleFlags returns a slice of the Flags with Hidden=false
|
|
func (a *App) VisibleFlags() []Flag {
|
|
return visibleFlags(a.Flags)
|
|
}
|
|
|
|
func (a *App) hasFlag(flag Flag) bool {
|
|
for _, f := range a.Flags {
|
|
if flag == f {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (a *App) errWriter() io.Writer {
|
|
|
|
// When the app ErrWriter is nil use the package level one.
|
|
if a.ErrWriter == nil {
|
|
return ErrWriter
|
|
}
|
|
|
|
return a.ErrWriter
|
|
}
|
|
|
|
func (a *App) appendFlag(flag Flag) {
|
|
if !a.hasFlag(flag) {
|
|
a.Flags = append(a.Flags, flag)
|
|
}
|
|
}
|
|
|
|
// Author represents someone who has contributed to a cli project.
|
|
type Author struct {
|
|
Name string // The Authors name
|
|
Email string // The Authors email
|
|
}
|
|
|
|
// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
|
|
func (a Author) String() string {
|
|
e := ""
|
|
if a.Email != "" {
|
|
e = "<" + a.Email + "> "
|
|
}
|
|
|
|
return fmt.Sprintf("%v %v", a.Name, e)
|
|
}
|
|
|
|
// HandleAction uses ✧✧✧reflection✧✧✧ to figure out if the given Action is an
|
|
// ActionFunc, a func with the legacy signature for Action, or some other
|
|
// invalid thing. If it's an ActionFunc or a func with the legacy signature for
|
|
// Action, the func is run!
|
|
func HandleAction(action interface{}, context *Context) (err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
// Try to detect a known reflection error from *this scope*, rather than
|
|
// swallowing all panics that may happen when calling an Action func.
|
|
s := fmt.Sprintf("%v", r)
|
|
if strings.HasPrefix(s, "reflect: ") && strings.Contains(s, "too many input arguments") {
|
|
err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v. See %s", r, appActionDeprecationURL), 2)
|
|
} else {
|
|
panic(r)
|
|
}
|
|
}
|
|
}()
|
|
|
|
if reflect.TypeOf(action).Kind() != reflect.Func {
|
|
return errNonFuncAction
|
|
}
|
|
|
|
vals := reflect.ValueOf(action).Call([]reflect.Value{reflect.ValueOf(context)})
|
|
|
|
if len(vals) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if len(vals) > 1 {
|
|
return errInvalidActionSignature
|
|
}
|
|
|
|
if retErr, ok := vals[0].Interface().(error); vals[0].IsValid() && ok {
|
|
return retErr
|
|
}
|
|
|
|
return err
|
|
}
|