diff --git a/mage/main.go b/mage/main.go index a8adfb29..289cc569 100644 --- a/mage/main.go +++ b/mage/main.go @@ -193,7 +193,7 @@ func Parse(stderr, stdout io.Writer, args []string) (inv Invocation, cmd Command fs.Usage = func() { fmt.Fprint(stdout, ` -mage [options] [target] +mage [options] [target] [] Mage is a make-like command runner. See https://magefile.org for full docs. @@ -207,19 +207,27 @@ Commands: -version show version info for the mage binary Options: - -d + -d run magefiles in the given directory (default ".") -debug turn on debug messages -h show description of a target -f force recreation of compiled magefile -keep keep intermediate mage files around after running -gocmd - use the given go binary to compile the output (default: "go") + use the given go binary to compile the output (default: "go") -goos sets the GOOS for the binary created by -compile (default: current OS) -goarch sets the GOARCH for the binary created by -compile (default: current arch) -t timeout in duration parsable format (e.g. 5m30s) -v show verbose output when running mage targets + +Flags (per target, optional), supported formats: + -- stop processing any further flags + -f -flag --f --flag + boolean flags only + -f=value -flag=value --f=value --flag=value + boolean, integer, string flags + note: file paths passed as values has to be wrapped in quotes `[1:]) } err = fs.Parse(args) diff --git a/mage/template.go b/mage/template.go index dc943fd8..d3e90532 100644 --- a/mage/template.go +++ b/mage/template.go @@ -18,6 +18,7 @@ import ( "sort" "strconv" "strings" + "regexp" "text/tabwriter" "time" {{range .Imports}}{{.UniqueName}} "{{.Path}}" @@ -25,6 +26,7 @@ import ( ) func main() { + // Use local types and functions in order to avoid name conflicts with additional magefiles. type arguments struct { Verbose bool // print out log statements @@ -38,7 +40,7 @@ func main() { val := os.Getenv(env) if val == "" { return false - } + } b, err := strconv.ParseBool(val) if err != nil { log.Printf("warning: environment variable %s is not a valid bool value: %v", env, val) @@ -51,7 +53,7 @@ func main() { val := os.Getenv(env) if val == "" { return 0 - } + } d, err := time.ParseDuration(val) if err != nil { log.Printf("warning: environment variable %s is not a valid duration value: %v", env, val) @@ -92,7 +94,7 @@ Options: fs.Usage() return } - + list := func() error { {{with .Description}}fmt.Println(` + "`{{.}}\n`" + `) {{- end}} @@ -214,11 +216,45 @@ Options: } var unknown []string + var prevTarget string + var filteredTargets = []string{} + var perTargetFlagsMap = make(map[string][]string) + var argLow string + re := regexp.MustCompile(` + "`" + `(^-{1,2}\w+\=\S*)|(^-{1,2}\w+$)` + "`" + `) for _, arg := range args.Args { - if !targets[strings.ToLower(arg)] { - unknown = append(unknown, arg) + // don't process any more arguments if you detect '--' chars + if arg == "--" { + break + } + // convert alias to full target name + switch strings.ToLower(arg) { + {{range $alias, $func := .Aliases}} + case "{{lower $alias}}": + arg = "{{$func.TargetName}}" + {{- end}} + } + argLow = strings.ToLower(arg) + if !targets[argLow] { + //----------------------------------------------------------------------------- + // Add to unknown targets only if arg is not matching above regex pattern e.g: + // -- : stop processing any more arguments + // -f --f -flag --flag : boolean flags only + // -f=value --f=value -flag=value --flag=value : boolean, integer, string flags + //----------------------------------------------------------------------------- + if re.FindString(arg) == "" { + unknown = append(unknown, arg) + } + perTargetFlagsMap[prevTarget] = append(perTargetFlagsMap[prevTarget], arg) + } else { + // target is always first, then args follows + prevTarget = argLow + filteredTargets = append(filteredTargets, argLow) } } + + // all ok, save filtered targets names + args.Args = filteredTargets + if len(unknown) == 1 { logger.Println("Unknown target specified:", unknown[0]) os.Exit(2) diff --git a/mg/runtime.go b/mg/runtime.go index ef4da402..c327d254 100644 --- a/mg/runtime.go +++ b/mg/runtime.go @@ -1,6 +1,7 @@ package mg import ( + "context" "os" "path/filepath" "runtime" @@ -73,3 +74,12 @@ func CacheDir() string { // Namespace allows for the grouping of similar commands type Namespace struct{} + +// GetFlagsFromContext - Helper function +// for extracting flags from context of a target +func GetFlagsFromContext(ctx context.Context) []string { + if ctx != nil && ctx.Value("flags") != nil { + return ctx.Value("flags").([]string) + } + return []string{} +} diff --git a/parse/parse.go b/parse/parse.go index ebe1775f..7b43a152 100644 --- a/parse/parse.go +++ b/parse/parse.go @@ -25,7 +25,7 @@ func EnableDebug() { debug.SetOutput(os.Stderr) } -// PkgInfo contains inforamtion about a package of files according to mage's +// PkgInfo contains information about a package of files according to mage's // parsing rules. type PkgInfo struct { AstPkg *ast.Package @@ -81,29 +81,34 @@ func (f Function) TargetName() string { // runTarget requires. func (f Function) ExecCode() (string, error) { name := f.Name + cliName := name if f.Receiver != "" { name = f.Receiver + "{}." + name + cliName = f.Receiver + ":" + cliName } if f.Package != "" { name = f.Package + "." + name } + cliName = strings.ToLower(cliName) if f.IsContext && f.IsError { out := ` wrapFn := func(ctx context.Context) error { + ctx = context.WithValue(ctx, "flags", perTargetFlagsMap["%s"]) return %s(ctx) } err := runTarget(wrapFn)`[1:] - return fmt.Sprintf(out, name), nil + return fmt.Sprintf(out, cliName, name), nil } if f.IsContext && !f.IsError { out := ` wrapFn := func(ctx context.Context) error { + ctx = context.WithValue(ctx, "flags", perTargetFlagsMap["%s"]) %s(ctx) return nil } err := runTarget(wrapFn)`[1:] - return fmt.Sprintf(out, name), nil + return fmt.Sprintf(out, cliName, name), nil } if !f.IsContext && f.IsError { out := `