diff options
| author | Christine Dodrill <me@christine.website> | 2017-12-13 10:43:58 -0800 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2017-12-13 11:42:37 -0800 |
| commit | 3a21ef192628f6952eaa981bcdf718a35a4b43c7 (patch) | |
| tree | 9c88a3ddc57ab5014f436ec2c08c96280872632e /vendor/github.com/magefile/mage/mg | |
| parent | 3b4b6cede9bc30008b0f40989a1564b26e64fd05 (diff) | |
| download | xesite-3a21ef192628f6952eaa981bcdf718a35a4b43c7.tar.xz xesite-3a21ef192628f6952eaa981bcdf718a35a4b43c7.zip | |
convert to go buildpack
Diffstat (limited to 'vendor/github.com/magefile/mage/mg')
| -rw-r--r-- | vendor/github.com/magefile/mage/mg/deps.go | 166 | ||||
| -rw-r--r-- | vendor/github.com/magefile/mage/mg/deps_test.go | 147 | ||||
| -rw-r--r-- | vendor/github.com/magefile/mage/mg/errors.go | 51 | ||||
| -rw-r--r-- | vendor/github.com/magefile/mage/mg/errors_test.go | 19 | ||||
| -rw-r--r-- | vendor/github.com/magefile/mage/mg/runtime.go | 36 |
5 files changed, 419 insertions, 0 deletions
diff --git a/vendor/github.com/magefile/mage/mg/deps.go b/vendor/github.com/magefile/mage/mg/deps.go new file mode 100644 index 0000000..30d6edc --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/deps.go @@ -0,0 +1,166 @@ +package mg + +import ( + "context" + "fmt" + "reflect" + "runtime" + "strings" + "sync" + + "github.com/magefile/mage/types" +) + +type onceMap struct { + mu *sync.Mutex + m map[string]*onceFun +} + +func (o *onceMap) LoadOrStore(s string, one *onceFun) *onceFun { + defer o.mu.Unlock() + o.mu.Lock() + + existing, ok := o.m[s] + if ok { + return existing + } + o.m[s] = one + return one +} + +var onces = &onceMap{ + mu: &sync.Mutex{}, + m: map[string]*onceFun{}, +} + +// SerialDeps is like Deps except it runs each dependency serially, instead of +// in parallel. This can be useful for resource intensive dependencies that +// shouldn't be run at the same time. +func SerialDeps(fns ...interface{}) { + checkFns(fns) + ctx := context.Background() + for _, f := range fns { + runDeps(ctx, f) + } +} + +// SerialCtxDeps is like CtxDeps except it runs each dependency serially, +// instead of in parallel. This can be useful for resource intensive +// dependencies that shouldn't be run at the same time. +func SerialCtxDeps(ctx context.Context, fns ...interface{}) { + checkFns(fns) + for _, f := range fns { + runDeps(ctx, f) + } +} + +// CtxDeps runs the given functions as dependencies of the calling function. +// Dependencies must only be of type: github.com/magefile/mage/types.FuncType. +// The function calling Deps is guaranteed that all dependent functions will be +// run exactly once when Deps returns. Dependent functions may in turn declare +// their own dependencies using Deps. Each dependency is run in their own +// goroutines. Each function is given the context provided if the function +// prototype allows for it. +func CtxDeps(ctx context.Context, fns ...interface{}) { + checkFns(fns) + runDeps(ctx, fns...) +} + +// runDeps assumes you've already called checkFns. +func runDeps(ctx context.Context, fns ...interface{}) { + mu := &sync.Mutex{} + var errs []string + var exit int + wg := &sync.WaitGroup{} + for _, f := range fns { + fn := addDep(ctx, f) + wg.Add(1) + go func() { + defer func() { + if v := recover(); v != nil { + mu.Lock() + if err, ok := v.(error); ok { + exit = changeExit(exit, ExitStatus(err)) + } else { + exit = changeExit(exit, 1) + } + errs = append(errs, fmt.Sprint(v)) + mu.Unlock() + } + wg.Done() + }() + if err := fn.run(); err != nil { + mu.Lock() + errs = append(errs, fmt.Sprint(err)) + exit = changeExit(exit, ExitStatus(err)) + mu.Unlock() + } + }() + } + + wg.Wait() + if len(errs) > 0 { + panic(Fatal(exit, strings.Join(errs, "\n"))) + } +} + +func checkFns(fns []interface{}) { + for _, f := range fns { + if err := types.FuncCheck(f); err != nil { + panic(err) + } + } +} + +// Deps runs the given functions with the default runtime context +func Deps(fns ...interface{}) { + CtxDeps(context.Background(), fns...) +} + +func changeExit(old, new int) int { + if new == 0 { + return old + } + if old == 0 { + return new + } + if old == new { + return old + } + // both different and both non-zero, just set + // exit to 1. Nothing more we can do. + return 1 +} + +func addDep(ctx context.Context, f interface{}) *onceFun { + var fn func(context.Context) error + if fn = types.FuncTypeWrap(f); fn == nil { + // should be impossible, since we already checked this + panic("attempted to add a dep that did not match required type") + } + + n := name(f) + of := onces.LoadOrStore(n, &onceFun{ + fn: fn, + ctx: ctx, + }) + return of +} + +func name(i interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() +} + +type onceFun struct { + once sync.Once + fn func(context.Context) error + ctx context.Context +} + +func (o *onceFun) run() error { + var err error + o.once.Do(func() { + err = o.fn(o.ctx) + }) + return err +} diff --git a/vendor/github.com/magefile/mage/mg/deps_test.go b/vendor/github.com/magefile/mage/mg/deps_test.go new file mode 100644 index 0000000..ea894bb --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/deps_test.go @@ -0,0 +1,147 @@ +package mg_test + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/magefile/mage/mg" +) + +func TestDepsRunOnce(t *testing.T) { + done := make(chan struct{}) + f := func() { + done <- struct{}{} + } + go mg.Deps(f, f) + select { + case <-done: + // cool + case <-time.After(time.Millisecond * 100): + t.Fatal("func not run in a reasonable amount of time.") + } + select { + case <-done: + t.Fatal("func run twice!") + case <-time.After(time.Millisecond * 100): + // cool... this should be plenty of time for the goroutine to have run + } +} + +func TestDepsOfDeps(t *testing.T) { + ch := make(chan string, 3) + // this->f->g->h + h := func() { + ch <- "h" + } + g := func() { + mg.Deps(h) + ch <- "g" + } + f := func() { + mg.Deps(g) + ch <- "f" + } + mg.Deps(f) + + res := <-ch + <-ch + <-ch + + if res != "hgf" { + t.Fatal("expected h then g then f to run, but got " + res) + } +} + +func TestDepError(t *testing.T) { + // TODO: this test is ugly and relies on implementation details. It should + // be recreated as a full-stack test. + + f := func() error { + return errors.New("ouch!") + } + defer func() { + err := recover() + if err == nil { + t.Fatal("expected panic, but didn't get one") + } + actual := fmt.Sprint(err) + if "ouch!" != actual { + t.Fatalf(`expected to get "ouch!" but got "%s"`, actual) + } + }() + mg.Deps(f) +} + +func TestDepFatal(t *testing.T) { + f := func() error { + return mg.Fatal(99, "ouch!") + } + defer func() { + v := recover() + if v == nil { + t.Fatal("expected panic, but didn't get one") + } + actual := fmt.Sprint(v) + if "ouch!" != actual { + t.Fatalf(`expected to get "ouch!" but got "%s"`, actual) + } + err, ok := v.(error) + if !ok { + t.Fatalf("expected recovered val to be error but was %T", v) + } + code := mg.ExitStatus(err) + if code != 99 { + t.Fatalf("Expected exit status 99, but got %v", code) + } + }() + mg.Deps(f) +} + +func TestDepTwoFatal(t *testing.T) { + f := func() error { + return mg.Fatal(99, "ouch!") + } + g := func() error { + return mg.Fatal(11, "bang!") + } + defer func() { + v := recover() + if v == nil { + t.Fatal("expected panic, but didn't get one") + } + actual := fmt.Sprint(v) + // order is non-deterministic, so check for both orders + if "ouch!\nbang!" != actual && "bang!\nouch!" != actual { + t.Fatalf(`expected to get "ouch!" and "bang!" but got "%s"`, actual) + } + err, ok := v.(error) + if !ok { + t.Fatalf("expected recovered val to be error but was %T", v) + } + code := mg.ExitStatus(err) + // two different error codes returns, so we give up and just use error + // code 1. + if code != 1 { + t.Fatalf("Expected exit status 1, but got %v", code) + } + }() + mg.Deps(f, g) +} + +func TestDepWithUnhandledFunc(t *testing.T) { + defer func() { + err := recover() + expected := "Invalid type for dependent function: func(string) string. Dependencies must be func(), func() error, func(context.Context) or func(context.Context) error" + actual, ok := err.(error) + if !ok { + t.Fatalf("Expected type string from panic") + } + if actual.Error() != expected { + t.Fatalf("Expected panic %v but got %v", expected, err) + } + }() + var NotValid func(string) string = func(a string) string { + return a + } + mg.Deps(NotValid) +} diff --git a/vendor/github.com/magefile/mage/mg/errors.go b/vendor/github.com/magefile/mage/mg/errors.go new file mode 100644 index 0000000..06a8690 --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/errors.go @@ -0,0 +1,51 @@ +package mg + +import ( + "errors" + "fmt" +) + +type fatalErr struct { + code int + error +} + +func (f fatalErr) ExitStatus() int { + return f.code +} + +type exitStatus interface { + ExitStatus() int +} + +// Fatal returns an error that will cause mage to print out the +// given args and exit with the given exit code. +func Fatal(code int, args ...interface{}) error { + return fatalErr{ + code: code, + error: errors.New(fmt.Sprint(args...)), + } +} + +// Fatalf returns an error that will cause mage to print out the +// given message and exit with an exit code of 1. +func Fatalf(code int, format string, args ...interface{}) error { + return fatalErr{ + code: code, + error: fmt.Errorf(format, args...), + } +} + +// ExitStatus queries the error for an exit status. If the error is nil, it +// returns 0. If the error does not implement ExitStatus() int, it returns 1. +// Otherwise it retiurns the value from ExitStatus(). +func ExitStatus(err error) int { + if err == nil { + return 0 + } + exit, ok := err.(exitStatus) + if !ok { + return 1 + } + return exit.ExitStatus() +} diff --git a/vendor/github.com/magefile/mage/mg/errors_test.go b/vendor/github.com/magefile/mage/mg/errors_test.go new file mode 100644 index 0000000..ac5e68f --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/errors_test.go @@ -0,0 +1,19 @@ +package mg + +import "testing" + +func TestFatalExit(t *testing.T) { + expected := 99 + code := ExitStatus(Fatal(expected)) + if code != expected { + t.Fatalf("Expected code %v but got %v", expected, code) + } +} + +func TestFatalfExit(t *testing.T) { + expected := 99 + code := ExitStatus(Fatalf(expected, "boo!")) + if code != expected { + t.Fatalf("Expected code %v but got %v", expected, code) + } +} diff --git a/vendor/github.com/magefile/mage/mg/runtime.go b/vendor/github.com/magefile/mage/mg/runtime.go new file mode 100644 index 0000000..8b99613 --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/runtime.go @@ -0,0 +1,36 @@ +package mg + +import ( + "os" + "path/filepath" + "runtime" +) + +// CacheEnv is the environment variable that users may set to change the +// location where mage stores its compiled binaries. +const CacheEnv = "MAGEFILE_CACHE" + +// verboseEnv is the environment variable that indicates the user requested +// verbose mode when running a magefile. +const verboseEnv = "MAGEFILE_VERBOSE" + +// Verbose reports whether a magefile was run with the verbose flag. +func Verbose() bool { + return os.Getenv(verboseEnv) != "" +} + +// CacheDir returns the directory where mage caches compiled binaries. It +// defaults to $HOME/.magefile, but may be overridden by the MAGEFILE_CACHE +// environment variable. +func CacheDir() string { + d := os.Getenv(CacheEnv) + if d != "" { + return d + } + switch runtime.GOOS { + case "windows": + return filepath.Join(os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH"), "magefile") + default: + return filepath.Join(os.Getenv("HOME"), ".magefile") + } +} |
