mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
improve command feature for package gcmd
This commit is contained in:
@ -1,88 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
c := &gcmd.Command{
|
||||
Name: "gf",
|
||||
Description: `GoFrame Command Line Interface, which is your helpmate for building GoFrame application with convenience.`,
|
||||
Additional: `
|
||||
Use 'gf help COMMAND' or 'gf COMMAND -h' for detail about a command, which has '...' in the tail of their comments.`,
|
||||
}
|
||||
// env
|
||||
commandEnv := gcmd.Command{
|
||||
Name: "env",
|
||||
Brief: "show current Golang environment variables",
|
||||
Description: "show current Golang environment variables",
|
||||
Func: func(parser *gcmd.Parser) {
|
||||
|
||||
},
|
||||
}
|
||||
if err = c.AddCommand(commandEnv); err != nil {
|
||||
g.Log().Fatal(err)
|
||||
}
|
||||
// get
|
||||
commandGet := gcmd.Command{
|
||||
Name: "get",
|
||||
Brief: "install or update GF to system in default...",
|
||||
Description: "show current Golang environment variables",
|
||||
|
||||
Examples: `
|
||||
gf get github.com/gogf/gf
|
||||
gf get github.com/gogf/gf@latest
|
||||
gf get github.com/gogf/gf@master
|
||||
gf get golang.org/x/sys
|
||||
`,
|
||||
Func: func(parser *gcmd.Parser) {
|
||||
|
||||
},
|
||||
}
|
||||
if err = c.AddCommand(commandGet); err != nil {
|
||||
g.Log().Fatal(err)
|
||||
}
|
||||
// build
|
||||
//-n, --name output binary name
|
||||
//-v, --version output binary version
|
||||
//-a, --arch output binary architecture, multiple arch separated with ','
|
||||
//-s, --system output binary system, multiple os separated with ','
|
||||
//-o, --output output binary path, used when building single binary file
|
||||
//-p, --path output binary directory path, default is './bin'
|
||||
//-e, --extra extra custom "go build" options
|
||||
//-m, --mod like "-mod" option of "go build", use "-m none" to disable go module
|
||||
//-c, --cgo enable or disable cgo feature, it's disabled in default
|
||||
|
||||
commandBuild := gcmd.Command{
|
||||
Name: "build",
|
||||
Usage: "gf build FILE [OPTION]",
|
||||
Brief: "cross-building go project for lots of platforms...",
|
||||
Description: `
|
||||
The "build" command is most commonly used command, which is designed as a powerful wrapper for
|
||||
"go build" command for convenience cross-compiling usage.
|
||||
It provides much more features for building binary:
|
||||
1. Cross-Compiling for many platforms and architectures.
|
||||
2. Configuration file support for compiling.
|
||||
3. Build-In Variables.
|
||||
`,
|
||||
Examples: `
|
||||
gf build main.go
|
||||
gf build main.go --swagger
|
||||
gf build main.go --pack public,template
|
||||
gf build main.go --cgo
|
||||
gf build main.go -m none
|
||||
gf build main.go -n my-app -a all -s all
|
||||
gf build main.go -n my-app -a amd64,386 -s linux -p .
|
||||
gf build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./docker/bin
|
||||
`,
|
||||
Func: func(parser *gcmd.Parser) {
|
||||
|
||||
},
|
||||
}
|
||||
if err = c.AddCommand(commandBuild); err != nil {
|
||||
g.Log().Fatal(err)
|
||||
}
|
||||
c.Run()
|
||||
}
|
||||
@ -19,11 +19,7 @@ import (
|
||||
const (
|
||||
helpOptionName = "help"
|
||||
helpOptionNameShort = "h"
|
||||
maxLineChars = 100
|
||||
)
|
||||
|
||||
var (
|
||||
defaultCommandFuncMap = make(map[string]func())
|
||||
maxLineChars = 120
|
||||
)
|
||||
|
||||
// Init does custom initialization.
|
||||
|
||||
@ -8,122 +8,51 @@
|
||||
package gcmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/command"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// Command holds the info about an argument that can handle custom logic.
|
||||
type Command struct {
|
||||
parent *Command
|
||||
commands []Command
|
||||
options []Option
|
||||
level int
|
||||
Name string
|
||||
Usage string
|
||||
Short string
|
||||
Brief string
|
||||
Description string
|
||||
Func func(parser *Parser)
|
||||
HelpFunc func(parser *Parser)
|
||||
Examples string
|
||||
Additional string
|
||||
Name string // Command name(case-sensitive).
|
||||
Usage string // A brief line description about its usage, eg: gf build main.go [OPTION]
|
||||
Brief string // A brief info that describes what this command will do.
|
||||
Description string // A detailed description.
|
||||
Options []Option // Option array, configuring how this command act.
|
||||
Func Function // Custom function.
|
||||
HelpFunc Function // Custom help function
|
||||
Examples string // Usage examples.
|
||||
Additional string // Additional custom info about this command.
|
||||
parent *Command // Parent command for internal usage.
|
||||
commands []Command // Sub commands of this command.
|
||||
}
|
||||
|
||||
// Function is a custom command callback function that is bound to a certain argument.
|
||||
type Function func(ctx context.Context, parser *Parser) (err error)
|
||||
|
||||
// Option is the command value that is specified by a name or shor name.
|
||||
// An Option can have or have no value bound to it.
|
||||
type Option struct {
|
||||
Name string
|
||||
Short string
|
||||
Brief string
|
||||
Description string
|
||||
NeedValue bool
|
||||
Name string // Option name.
|
||||
Short string // Option short.
|
||||
Brief string // Brief info about this Option, which is used in help info.
|
||||
NeedValue bool // Whether this Option having or having no value bound to it.
|
||||
}
|
||||
|
||||
func (c *Command) Print() {
|
||||
prefix := gstr.Repeat(" ", 4)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
// Usage.
|
||||
if c.Usage != "" || c.Name != "" {
|
||||
buffer.WriteString("USAGE\n")
|
||||
buffer.WriteString(prefix)
|
||||
if c.Usage != "" {
|
||||
buffer.WriteString(c.Usage)
|
||||
} else {
|
||||
var (
|
||||
p = c
|
||||
name = c.Name
|
||||
)
|
||||
for p.parent != nil {
|
||||
name = p.parent.Name + " " + name
|
||||
p = p.parent
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`%s ARGUMENT [OPTION]`, name))
|
||||
}
|
||||
buffer.WriteString("\n\n")
|
||||
}
|
||||
// Command.
|
||||
if len(c.commands) > 0 {
|
||||
buffer.WriteString("COMMAND\n")
|
||||
maxSpaceLength := 0
|
||||
for _, cmd := range c.commands {
|
||||
nameStr := cmd.Name + "/" + cmd.Short
|
||||
if len(nameStr) > maxSpaceLength {
|
||||
maxSpaceLength = len(nameStr)
|
||||
}
|
||||
}
|
||||
for _, cmd := range c.commands {
|
||||
nameStr := cmd.Name
|
||||
if cmd.Short != "" {
|
||||
nameStr += "/" + cmd.Short
|
||||
}
|
||||
var (
|
||||
spaceLength = maxSpaceLength - len(nameStr)
|
||||
lineStr = fmt.Sprintf(
|
||||
"%s%s%s %s\n",
|
||||
prefix, nameStr, gstr.Repeat(" ", spaceLength), cmd.Brief,
|
||||
)
|
||||
)
|
||||
lineStr = gstr.WordWrap(lineStr, maxLineChars, "\n")
|
||||
buffer.WriteString(lineStr)
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
var (
|
||||
// defaultHelpOption is the default help option that will be automatically added to each command.
|
||||
defaultHelpOption = Option{
|
||||
Name: `help`,
|
||||
Short: `h`,
|
||||
Brief: `more information about this command`,
|
||||
NeedValue: false,
|
||||
}
|
||||
)
|
||||
|
||||
// Examples.
|
||||
if c.Examples != "" {
|
||||
buffer.WriteString("EXAMPLES\n")
|
||||
lineStr := gstr.WordWrap(gstr.Trim(c.Examples), maxLineChars, "\n")
|
||||
for _, line := range gstr.SplitAndTrim(lineStr, "\n") {
|
||||
buffer.WriteString(prefix)
|
||||
buffer.WriteString(line)
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
// Description.
|
||||
if c.Description != "" {
|
||||
buffer.WriteString("DESCRIPTION\n")
|
||||
lineStr := gstr.WordWrap(gstr.Trim(c.Description), maxLineChars, "\n")
|
||||
for _, line := range gstr.SplitAndTrim(lineStr, "\n") {
|
||||
buffer.WriteString(prefix)
|
||||
buffer.WriteString(line)
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
// Additional.
|
||||
if c.Additional != "" {
|
||||
lineStr := gstr.WordWrap(gstr.Trim(c.Additional), maxLineChars, "\n")
|
||||
buffer.WriteString(lineStr)
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
fmt.Println(buffer.String())
|
||||
}
|
||||
|
||||
func (c *Command) AddCommand(command ...Command) error {
|
||||
for _, cmd := range command {
|
||||
func (c *Command) Add(commands ...Command) error {
|
||||
for _, cmd := range commands {
|
||||
cmd.Name = gstr.Trim(cmd.Name)
|
||||
if cmd.Name == "" {
|
||||
return gerror.New("command name should not be empty")
|
||||
@ -132,72 +61,7 @@ func (c *Command) AddCommand(command ...Command) error {
|
||||
return gerror.New("command function should not be empty")
|
||||
}
|
||||
cmd.parent = c
|
||||
cmd.level = c.level + 1
|
||||
c.commands = append(c.commands, cmd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Command) AddOption(option ...Option) error {
|
||||
for _, opt := range option {
|
||||
opt.Name = gstr.Trim(opt.Name)
|
||||
if opt.Name == "" {
|
||||
return gerror.New("option name should not be empty")
|
||||
}
|
||||
}
|
||||
c.options = append(c.options, option...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Command) Run() {
|
||||
// Find the matched command and run it.
|
||||
argument := GetArg(c.level + 1)
|
||||
if !argument.IsEmpty() {
|
||||
if len(c.commands) > 0 {
|
||||
for _, cmd := range c.commands {
|
||||
if gstr.Equal(cmd.Name, argument.String()) {
|
||||
cmd.Run()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Run current command function.
|
||||
var (
|
||||
err error
|
||||
parser *Parser
|
||||
)
|
||||
if len(c.options) > 0 {
|
||||
optionParsingMap := make(map[string]bool, 0)
|
||||
// Add custom options to parser.
|
||||
for _, option := range c.options {
|
||||
optionParsingKey := option.Name
|
||||
if option.Short != "" {
|
||||
optionParsingKey += "," + option.Short
|
||||
}
|
||||
optionParsingMap[optionParsingKey] = option.NeedValue
|
||||
}
|
||||
// Add help option to parser.
|
||||
optionParsingMap[helpOptionName+","+helpOptionNameShort] = false
|
||||
parser, err = Parse(optionParsingMap)
|
||||
} else {
|
||||
parsedArgs, parsedOptions := command.ParseUsingDefaultAlgorithm(os.Args...)
|
||||
parser = &Parser{
|
||||
strict: false,
|
||||
parsedArgs: parsedArgs,
|
||||
parsedOptions: parsedOptions,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
}
|
||||
if parser.ContainsOpt(helpOptionName) || parser.ContainsOpt(helpOptionNameShort) {
|
||||
if c.HelpFunc != nil {
|
||||
c.HelpFunc(parser)
|
||||
} else {
|
||||
c.Print()
|
||||
}
|
||||
return
|
||||
}
|
||||
c.Func(parser)
|
||||
}
|
||||
|
||||
130
os/gcmd/gcmd_command_help.go
Normal file
130
os/gcmd/gcmd_command_help.go
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gcmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
func (c *Command) Print() {
|
||||
prefix := gstr.Repeat(" ", 4)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
// Usage.
|
||||
if c.Usage != "" || c.Name != "" {
|
||||
buffer.WriteString("USAGE\n")
|
||||
buffer.WriteString(prefix)
|
||||
if c.Usage != "" {
|
||||
buffer.WriteString(c.Usage)
|
||||
} else {
|
||||
var (
|
||||
p = c
|
||||
name = c.Name
|
||||
)
|
||||
for p.parent != nil {
|
||||
name = p.parent.Name + " " + name
|
||||
p = p.parent
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`%s ARGUMENT [OPTION]`, name))
|
||||
}
|
||||
buffer.WriteString("\n\n")
|
||||
}
|
||||
// Command.
|
||||
if len(c.commands) > 0 {
|
||||
buffer.WriteString("COMMAND\n")
|
||||
var (
|
||||
maxSpaceLength = 0
|
||||
)
|
||||
for _, cmd := range c.commands {
|
||||
if len(cmd.Name) > maxSpaceLength {
|
||||
maxSpaceLength = len(cmd.Name)
|
||||
}
|
||||
}
|
||||
for _, cmd := range c.commands {
|
||||
// Add "..." to brief for those commands that also have sub-commands.
|
||||
if len(cmd.commands) > 0 {
|
||||
cmd.Brief = gstr.TrimRight(cmd.Brief, ".") + "..."
|
||||
}
|
||||
var (
|
||||
spaceLength = maxSpaceLength - len(cmd.Name)
|
||||
lineStr = fmt.Sprintf("%s%s%s%s\n", prefix, cmd.Name, gstr.Repeat(" ", spaceLength+4), cmd.Brief)
|
||||
wordwrapPrefix = gstr.Repeat(" ", len(prefix+cmd.Name)+spaceLength+4)
|
||||
)
|
||||
lineStr = gstr.WordWrap(lineStr, maxLineChars, "\n"+wordwrapPrefix)
|
||||
buffer.WriteString(lineStr)
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
|
||||
// Option.
|
||||
if len(c.Options) > 0 {
|
||||
buffer.WriteString("OPTION\n")
|
||||
var (
|
||||
nameStr string
|
||||
maxSpaceLength = 0
|
||||
)
|
||||
for _, option := range c.Options {
|
||||
if option.Short != "" {
|
||||
nameStr = fmt.Sprintf("-%s,\t--%s", option.Short, option.Name)
|
||||
} else {
|
||||
nameStr = fmt.Sprintf("-/--%s", option.Name)
|
||||
}
|
||||
if len(nameStr) > maxSpaceLength {
|
||||
maxSpaceLength = len(nameStr)
|
||||
}
|
||||
}
|
||||
for _, option := range c.Options {
|
||||
if option.Short != "" {
|
||||
nameStr = fmt.Sprintf("-%s,\t--%s", option.Short, option.Name)
|
||||
} else {
|
||||
nameStr = fmt.Sprintf("-/--%s", option.Name)
|
||||
}
|
||||
var (
|
||||
spaceLength = maxSpaceLength - len(nameStr)
|
||||
lineStr = fmt.Sprintf("%s%s%s%s\n", prefix, nameStr, gstr.Repeat(" ", spaceLength+4), option.Brief)
|
||||
wordwrapPrefix = gstr.Repeat(" ", len(prefix+nameStr)+spaceLength+4)
|
||||
)
|
||||
lineStr = gstr.WordWrap(lineStr, maxLineChars, "\n"+wordwrapPrefix)
|
||||
buffer.WriteString(lineStr)
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
|
||||
// Example.
|
||||
if c.Examples != "" {
|
||||
buffer.WriteString("EXAMPLE\n")
|
||||
buffer.WriteString(prefix)
|
||||
buffer.WriteString(gstr.WordWrap(gstr.Trim(c.Examples), maxLineChars, "\n"+prefix))
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
|
||||
// Description.
|
||||
if c.Description != "" {
|
||||
buffer.WriteString("DESCRIPTION\n")
|
||||
buffer.WriteString(prefix)
|
||||
buffer.WriteString(gstr.WordWrap(gstr.Trim(c.Description), maxLineChars, "\n"+prefix))
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
|
||||
// Additional.
|
||||
if c.Additional != "" {
|
||||
lineStr := gstr.WordWrap(gstr.Trim(c.Additional), maxLineChars, "\n")
|
||||
buffer.WriteString(lineStr)
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
fmt.Println(buffer.String())
|
||||
}
|
||||
|
||||
func (c *Command) defaultHelpFunc(ctx context.Context, parser *Parser) error {
|
||||
c.Print()
|
||||
return nil
|
||||
}
|
||||
99
os/gcmd/gcmd_command_run.go
Normal file
99
os/gcmd/gcmd_command_run.go
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gcmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
func (c *Command) Run(ctx context.Context) error {
|
||||
// Parse command arguments and options using default algorithm.
|
||||
parser, err := Parse(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args := parser.GetArgAll()
|
||||
if len(args) == 1 {
|
||||
if c.HelpFunc != nil {
|
||||
return c.HelpFunc(ctx, parser)
|
||||
}
|
||||
return c.defaultHelpFunc(ctx, parser)
|
||||
}
|
||||
|
||||
// Exclude the root binary name.
|
||||
args = args[1:]
|
||||
|
||||
// Find the matched command and run it.
|
||||
if subCommand := c.searchCommand(args); subCommand != nil {
|
||||
return subCommand.doRun(ctx, parser)
|
||||
}
|
||||
|
||||
// Print error and help command if no command found.
|
||||
fmt.Printf("Error: command not found for \"%s\"\n\n", gstr.Join(args, " "))
|
||||
c.Print()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Command) doRun(ctx context.Context, parser *Parser) (err error) {
|
||||
// Add built-in help option, just for info only.
|
||||
c.Options = append(c.Options, defaultHelpOption)
|
||||
// Check built-in help command.
|
||||
if parser.ContainsOpt(helpOptionName) || parser.ContainsOpt(helpOptionNameShort) {
|
||||
if c.HelpFunc != nil {
|
||||
return c.HelpFunc(ctx, parser)
|
||||
}
|
||||
return c.defaultHelpFunc(ctx, parser)
|
||||
}
|
||||
// Reparse the arguments for current command configuration.
|
||||
parser, err = c.reParse(ctx, parser)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Registered command function calling.
|
||||
return c.Func(ctx, parser)
|
||||
}
|
||||
|
||||
func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error) {
|
||||
// It seems just has built-in help option, it so does nothing.
|
||||
if len(c.Options) == 1 {
|
||||
return parser, nil
|
||||
}
|
||||
var (
|
||||
optionKey string
|
||||
supportedOptions = make(map[string]bool)
|
||||
)
|
||||
for _, option := range c.Options {
|
||||
if option.Short != "" {
|
||||
optionKey = fmt.Sprintf(`%s.%s`, option.Name, option.Short)
|
||||
} else {
|
||||
optionKey = option.Name
|
||||
}
|
||||
supportedOptions[optionKey] = option.NeedValue
|
||||
}
|
||||
return Parse(supportedOptions)
|
||||
}
|
||||
|
||||
func (c *Command) searchCommand(args []string) *Command {
|
||||
if len(args) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, cmd := range c.commands {
|
||||
if cmd.Name == args[0] {
|
||||
leftArgs := args[1:]
|
||||
if len(leftArgs) == 0 {
|
||||
return &cmd
|
||||
}
|
||||
return cmd.searchCommand(leftArgs)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gcmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
// BindHandle registers callback function `f` with `cmd`.
|
||||
func BindHandle(cmd string, f func()) error {
|
||||
if _, ok := defaultCommandFuncMap[cmd]; ok {
|
||||
return gerror.NewCode(gcode.CodeInvalidOperation, "duplicated handle for command:"+cmd)
|
||||
} else {
|
||||
defaultCommandFuncMap[cmd] = f
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BindHandleMap registers callback function with map `m`.
|
||||
func BindHandleMap(m map[string]func()) error {
|
||||
var err error
|
||||
for k, v := range m {
|
||||
if err = BindHandle(k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// RunHandle executes the callback function registered by `cmd`.
|
||||
func RunHandle(cmd string) error {
|
||||
if handle, ok := defaultCommandFuncMap[cmd]; ok {
|
||||
handle()
|
||||
} else {
|
||||
return gerror.NewCode(gcode.CodeMissingConfiguration, "no handle found for command:"+cmd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AutoRun automatically recognizes and executes the callback function
|
||||
// by value of index 0 (the first console parameter).
|
||||
func AutoRun() error {
|
||||
if cmd := GetArg(1); !cmd.IsEmpty() {
|
||||
if handle, ok := defaultCommandFuncMap[cmd.String()]; ok {
|
||||
handle()
|
||||
} else {
|
||||
return gerror.NewCode(gcode.CodeMissingConfiguration, "no handle found for command:"+cmd.String())
|
||||
}
|
||||
} else {
|
||||
return gerror.NewCode(gcode.CodeMissingParameter, "no command found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/command"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
@ -25,7 +26,7 @@ type Parser struct {
|
||||
parsedArgs []string // As name described.
|
||||
parsedOptions map[string]string // As name described.
|
||||
passedOptions map[string]bool // User passed supported options.
|
||||
supportedOptions map[string]bool // Option [option name : need argument].
|
||||
supportedOptions map[string]bool // Option [OptionName:WhetherNeedArgument].
|
||||
commandFuncMap map[string]func() // Command function map for function handler.
|
||||
}
|
||||
|
||||
@ -36,6 +37,13 @@ type Parser struct {
|
||||
//
|
||||
// The optional parameter `strict` specifies whether stops parsing and returns error if invalid option passed.
|
||||
func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
|
||||
if supportedOptions == nil {
|
||||
command.Init(os.Args...)
|
||||
return &Parser{
|
||||
parsedArgs: GetArgAll(),
|
||||
parsedOptions: GetOptAll(),
|
||||
}, nil
|
||||
}
|
||||
return ParseWithArgs(os.Args, supportedOptions, strict...)
|
||||
}
|
||||
|
||||
@ -46,6 +54,13 @@ func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
|
||||
//
|
||||
// The optional parameter `strict` specifies whether stops parsing and returns error if invalid option passed.
|
||||
func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
|
||||
if supportedOptions == nil {
|
||||
command.Init(args...)
|
||||
return &Parser{
|
||||
parsedArgs: GetArgAll(),
|
||||
parsedOptions: GetOptAll(),
|
||||
}, nil
|
||||
}
|
||||
strictParsing := false
|
||||
if len(strict) > 0 {
|
||||
strictParsing = strict[0]
|
||||
@ -86,7 +101,7 @@ func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bo
|
||||
}
|
||||
} else {
|
||||
// Multiple options?
|
||||
if array := parser.parseMultiOption(option); len(array) > 0 {
|
||||
if array = parser.parseMultiOption(option); len(array) > 0 {
|
||||
for _, v := range array {
|
||||
parser.setOptionValue(v, "")
|
||||
}
|
||||
|
||||
@ -9,10 +9,13 @@
|
||||
package gcmd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
@ -67,3 +70,151 @@ func Test_GetWithEnv(t *testing.T) {
|
||||
t.Assert(gcmd.GetOptWithEnv("test"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Command(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
err error
|
||||
)
|
||||
commandRoot := &gcmd.Command{
|
||||
Name: "gf",
|
||||
}
|
||||
// env
|
||||
commandEnv := gcmd.Command{
|
||||
Name: "env",
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) error {
|
||||
fmt.Println("env")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
// test
|
||||
commandTest := gcmd.Command{
|
||||
Name: "test",
|
||||
Brief: "test brief",
|
||||
Description: "test description current Golang environment variables",
|
||||
Examples: `
|
||||
gf get github.com/gogf/gf
|
||||
gf get github.com/gogf/gf@latest
|
||||
gf get github.com/gogf/gf@master
|
||||
gf get golang.org/x/sys
|
||||
`,
|
||||
Options: []gcmd.Option{
|
||||
{
|
||||
Name: "my-option",
|
||||
Short: "o",
|
||||
Brief: "It's my custom option",
|
||||
NeedValue: false,
|
||||
},
|
||||
{
|
||||
Name: "another",
|
||||
Short: "a",
|
||||
Brief: "It's my another custom option",
|
||||
NeedValue: false,
|
||||
},
|
||||
},
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) error {
|
||||
fmt.Println("test")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
err = commandRoot.Add(
|
||||
commandEnv,
|
||||
commandTest,
|
||||
)
|
||||
if err != nil {
|
||||
g.Log().Fatal(ctx, err)
|
||||
}
|
||||
|
||||
if err = commandRoot.Run(ctx); err != nil {
|
||||
g.Log().Fatal(ctx, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Command_Print(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
err error
|
||||
)
|
||||
c := &gcmd.Command{
|
||||
Name: "gf",
|
||||
Description: `GoFrame Command Line Interface, which is your helpmate for building GoFrame application with convenience.`,
|
||||
Additional: `
|
||||
Use 'gf help COMMAND' or 'gf COMMAND -h' for detail about a command, which has '...' in the tail of their comments.`,
|
||||
}
|
||||
// env
|
||||
commandEnv := gcmd.Command{
|
||||
Name: "env",
|
||||
Brief: "show current Golang environment variables, long brief.long brief.long brief.long brief.long brief.long brief.long brief.long brief.",
|
||||
Description: "show current Golang environment variables",
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
if err = c.Add(commandEnv); err != nil {
|
||||
g.Log().Fatal(ctx, err)
|
||||
}
|
||||
// get
|
||||
commandGet := gcmd.Command{
|
||||
Name: "get",
|
||||
Brief: "install or update GF to system in default...",
|
||||
Description: "show current Golang environment variables",
|
||||
|
||||
Examples: `
|
||||
gf get github.com/gogf/gf
|
||||
gf get github.com/gogf/gf@latest
|
||||
gf get github.com/gogf/gf@master
|
||||
gf get golang.org/x/sys
|
||||
`,
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
if err = c.Add(commandGet); err != nil {
|
||||
g.Log().Fatal(ctx, err)
|
||||
}
|
||||
// build
|
||||
//-n, --name output binary name
|
||||
//-v, --version output binary version
|
||||
//-a, --arch output binary architecture, multiple arch separated with ','
|
||||
//-s, --system output binary system, multiple os separated with ','
|
||||
//-o, --output output binary path, used when building single binary file
|
||||
//-p, --path output binary directory path, default is './bin'
|
||||
//-e, --extra extra custom "go build" options
|
||||
//-m, --mod like "-mod" option of "go build", use "-m none" to disable go module
|
||||
//-c, --cgo enable or disable cgo feature, it's disabled in default
|
||||
|
||||
commandBuild := gcmd.Command{
|
||||
Name: "build",
|
||||
Usage: "gf build FILE [OPTION]",
|
||||
Brief: "cross-building go project for lots of platforms...",
|
||||
Description: `
|
||||
The "build" command is most commonly used command, which is designed as a powerful wrapper for
|
||||
"go build" command for convenience cross-compiling usage.
|
||||
It provides much more features for building binary:
|
||||
1. Cross-Compiling for many platforms and architectures.
|
||||
2. Configuration file support for compiling.
|
||||
3. Build-In Variables.
|
||||
`,
|
||||
Examples: `
|
||||
gf build main.go
|
||||
gf build main.go --swagger
|
||||
gf build main.go --pack public,template
|
||||
gf build main.go --cgo
|
||||
gf build main.go -m none
|
||||
gf build main.go -n my-app -a all -s all
|
||||
gf build main.go -n my-app -a amd64,386 -s linux -p .
|
||||
gf build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./docker/bin
|
||||
`,
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
if err = c.Add(commandBuild); err != nil {
|
||||
g.Log().Fatal(ctx, err)
|
||||
}
|
||||
c.Run(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user