Skip to content

Commit

Permalink
Support namespace functions with aliases and defaults (magefile#183)
Browse files Browse the repository at this point in the history
* support namespace funcs as aliases and defaults

fixes magefile#181
  • Loading branch information
natefinch authored Sep 26, 2018
1 parent eb66d62 commit a7a8e06
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 38 deletions.
5 changes: 1 addition & 4 deletions mage/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ type data struct {
DefaultError bool
Default string
DefaultFunc parse.Function
Aliases map[string]string
Aliases map[string]parse.Function
}

func goVersion(gocmd string) (string, error) {
Expand Down Expand Up @@ -529,13 +529,10 @@ func GenerateMainfile(path, cachedir string, info *parse.PkgInfo) error {
data := data{
Description: info.Description,
Funcs: info.Funcs,
Default: info.DefaultName,
DefaultFunc: info.DefaultFunc,
Aliases: info.Aliases,
}

data.DefaultError = info.DefaultIsError

debug.Println("writing new file at", path)
if err := output.Execute(f, data); err != nil {
return fmt.Errorf("can't execute mainfile template: %v", err)
Expand Down
21 changes: 19 additions & 2 deletions mage/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -740,11 +740,11 @@ func TestInvalidAlias(t *testing.T) {
Args: []string{"co"},
}
code := Invoke(inv)
if code != 1 {
if code != 2 {
t.Errorf("expected to exit with code 1, but got %v", code)
}
actual := stderr.String()
expected := "Unknown target: \"co\"\n"
expected := "Unknown target specified: co\n"
if actual != expected {
t.Fatalf("expected %q, but got %q", expected, actual)
}
Expand Down Expand Up @@ -939,3 +939,20 @@ func TestNamespace(t *testing.T) {
t.Fatalf("expected %q, but got %q", expected, stdout.String())
}
}

func TestNamespaceDefault(t *testing.T) {
stdout := &bytes.Buffer{}
inv := Invocation{
Dir: "./testdata/namespaces",
Stderr: ioutil.Discard,
Stdout: stdout,
}
code := Invoke(inv)
if code != 0 {
t.Fatalf("expected 0, but got %v", code)
}
expected := "hi!\n"
if stdout.String() != expected {
t.Fatalf("expected %q, but got %q", expected, stdout.String())
}
}
17 changes: 9 additions & 8 deletions mage/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ func main() {
// magefiles.
list := func() error {
{{with .Description}}fmt.Println(` + "`{{.}}\n`" + `){{end}}
{{- $default := .Default}}
{{- $default := .DefaultFunc}}
w := tabwriter.NewWriter(os.Stdout, 0, 4, 4, ' ', 0)
fmt.Println("Targets:")
{{- range .Funcs}}
fmt.Fprintln(w, " {{lowerfirst .TemplateName}}{{if eq .Name $default}}*{{end}}\t" + {{printf "%q" .Synopsis}})
fmt.Fprintln(w, " {{lowerfirst .TemplateName}}{{if and (eq .Name $default.Name) (eq .Receiver $default.Receiver)}}*{{end}}\t" + {{printf "%q" .Synopsis}})
{{- end}}
err := w.Flush()
{{- if .Default}}
{{- if .DefaultFunc.Name}}
if err == nil {
fmt.Println("\n* default target")
}
Expand Down Expand Up @@ -107,7 +107,7 @@ func main() {
log.SetOutput(ioutil.Discard)
}
logger := log.New(os.Stderr, "", 0)
if os.Getenv("MAGEFILE_LIST") != "" {
if ok, _ := strconv.ParseBool(os.Getenv("MAGEFILE_LIST")); ok {
if err := list(); err != nil {
log.Println(err)
os.Exit(1)
Expand Down Expand Up @@ -137,7 +137,7 @@ func main() {
os.Exit(2)
}
if os.Getenv("MAGEFILE_HELP") != "" {
if help, _ := strconv.ParseBool(os.Getenv("MAGEFILE_HELP")); help {
if len(os.Args) < 2 {
logger.Println("no target specified")
os.Exit(1)
Expand All @@ -151,8 +151,9 @@ func main() {
{{end}}
var aliases []string
{{- $name := .Name -}}
{{- $recv := .Receiver -}}
{{range $alias, $func := $.Aliases}}
{{if eq $name $func}}aliases = append(aliases, "{{$alias}}"){{end -}}
{{if and (eq $name $func.Name) (eq $recv $func.Receiver)}}aliases = append(aliases, "{{$alias}}"){{end -}}
{{- end}}
if len(aliases) > 0 {
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
Expand All @@ -165,7 +166,7 @@ func main() {
}
}
if len(os.Args) < 2 {
{{- if .Default}}
{{- if .DefaultFunc.Name}}
ignore, _ := strconv.ParseBool(os.Getenv("MAGEFILE_IGNOREDEFAULT"))
if ignore {
if err := list(); err != nil {
Expand All @@ -189,7 +190,7 @@ func main() {
switch strings.ToLower(target) {
{{range $alias, $func := .Aliases}}
case "{{lower $alias}}":
target = "{{$func}}"
target = "{{with $func.Receiver}}{{.}}:{{end}}{{$func.Name}}"
{{- end}}
}
switch strings.ToLower(target) {
Expand Down
2 changes: 2 additions & 0 deletions mage/testdata/namespaces/magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/magefile/mage/mg"
)

var Default = NS.Error

func TestNamespaceDep() {
mg.Deps(NS.Error, NS.Bare, NS.BareCtx, NS.CtxErr)
}
Expand Down
10 changes: 10 additions & 0 deletions magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import (
"github.com/magefile/mage/sh"
)

var Aliases = map[string]interface{}{
"foo": Foo.Build,
}

// Runs "go install" for mage. This generates the version info the binary.
func Install() error {
name := "mage"
Expand Down Expand Up @@ -98,3 +102,9 @@ func hash() string {
hash, _ := sh.Output("git", "rev-parse", "--short", "HEAD")
return hash
}

type Foo mg.Namespace

func (Foo) Build() {

}
54 changes: 33 additions & 21 deletions parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,10 @@ func EnableDebug() {
// PkgInfo contains inforamtion about a package of files according to mage's
// parsing rules.
type PkgInfo struct {
Description string
Funcs []Function
DefaultIsError bool
DefaultIsContext bool
DefaultName string
DefaultFunc Function
Aliases map[string]string
Description string
Funcs []Function
DefaultFunc Function
Aliases map[string]Function
}

// Function represented a job function from a mage file
Expand Down Expand Up @@ -253,15 +250,20 @@ func setDefault(p *doc.Package, pi *PkgInfo) {
if len(spec.Values) != 1 {
log.Println("warning: default declaration has multiple values")
}
id, ok := spec.Values[0].(*ast.Ident)
if !ok {
log.Println("warning: default declaration is not a function name")

var name string
var receiver string
switch v := spec.Values[0].(type) {
case *ast.Ident:
name = v.Name
case *ast.SelectorExpr:
name = v.Sel.Name
receiver = fmt.Sprintf("%s", v.X)
default:
log.Printf("warning: target for Default %s is not a function", spec.Values[0])
}
for _, f := range pi.Funcs {
if f.Name == id.Name {
pi.DefaultName = f.Name
pi.DefaultIsError = f.IsError
pi.DefaultIsContext = f.IsContext
if f.Name == name && f.Receiver == receiver {
pi.DefaultFunc = f
return
}
Expand Down Expand Up @@ -290,7 +292,7 @@ func setAliases(p *doc.Package, pi *PkgInfo) {
log.Println("warning: aliases declaration is not a map")
return
}
pi.Aliases = make(map[string]string)
pi.Aliases = map[string]Function{}
for _, elem := range comp.Elts {
kv, ok := elem.(*ast.KeyValueExpr)
if !ok {
Expand All @@ -302,20 +304,30 @@ func setAliases(p *doc.Package, pi *PkgInfo) {
log.Println("warning: alias is not a string")
return
}
v, ok := kv.Value.(*ast.Ident)
if !ok {
log.Println("warning: alias target is not a function")
return

var name string
var receiver string
switch v := kv.Value.(type) {
case *ast.Ident:
name = v.Name
case *ast.SelectorExpr:
name = v.Sel.Name
receiver = fmt.Sprintf("%s", v.X)
default:
log.Printf("warning: target for alias %s is not a function", k.Value)
}
alias := strings.Trim(k.Value, "\"")
valid := false
for _, f := range pi.Funcs {
valid = valid || f.Name == v.Name
if f.Name == name && f.Receiver == receiver {
pi.Aliases[alias] = f
valid = true
break
}
}
if !valid {
log.Printf("warning: alias declaration (%s) does not reference a mage target", alias)
}
pi.Aliases[alias] = v.Name
}
return
}
Expand Down
18 changes: 15 additions & 3 deletions parse/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,31 @@ func TestParse(t *testing.T) {
}

// DefaultIsError
if info.DefaultIsError != true {
if info.DefaultFunc.IsError != true {
t.Fatalf("expected DefaultIsError to be true")
}

// DefaultName
if info.DefaultName != "ReturnsNilError" {
if info.DefaultFunc.Name != "ReturnsNilError" {
t.Fatalf("expected DefaultName to be ReturnsNilError")
}

if info.Aliases["void"] != "ReturnsVoid" {
if info.Aliases["void"].Name != "ReturnsVoid" {
t.Fatalf("expected alias of void to be ReturnsVoid")
}

f, ok := info.Aliases["baz"]
if !ok {
t.Fatal("missing alias baz")
}
if f.Name != "Baz" || f.Receiver != "Build" {
t.Fatalf("expected alias of void to be Build.Baz")
}

if len(info.Aliases) != 2 {
t.Fatalf("expected to only have two aliases, but have %#v", info.Aliases)
}

for _, fn := range expected {
found := false
for _, infoFn := range info.Funcs {
Expand Down
1 change: 1 addition & 0 deletions parse/testdata/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ package main

var Aliases = map[string]interface{}{
"void": ReturnsVoid,
"baz": Build.Baz,
}

0 comments on commit a7a8e06

Please sign in to comment.