Skip to content

add MAGEFILE_HASHFAST to avoid rerunning go build #258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions mage/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ type Invocation struct {
Args []string // args to pass to the compiled binary
GoCmd string // the go binary command to run
CacheDir string // the directory where we should store compiled binaries
HashFast bool // don't rely on GOCACHE, just hash the magefiles
}

// ParseAndRun parses the command line, and then compiles and runs the mage
Expand Down Expand Up @@ -282,7 +283,7 @@ Options:
if len(inv.Args) > 0 && cmd != None {
return inv, cmd, fmt.Errorf("unexpected arguments to command: %q", inv.Args)
}

inv.HashFast = mg.HashFast()
return inv, cmd, err
}

Expand Down Expand Up @@ -321,12 +322,16 @@ func Invoke(inv Invocation) int {
debug.Println("output exe is ", exePath)

useCache := false
if s, err := internal.OutputDebug(inv.GoCmd, "env", "GOCACHE"); err == nil {
// if GOCACHE exists, always rebuild, so we catch transitive
// dependencies that have changed.
if s != "" {
debug.Println("build cache exists, will ignore any compiled binary")
useCache = true
if inv.HashFast {
debug.Println("user has set MAGEFILE_HASHFAST, so we'll ignore GOCACHE")
} else {
if s, err := internal.OutputDebug(inv.GoCmd, "env", "GOCACHE"); err == nil {
// if GOCACHE exists, always rebuild, so we catch transitive
// dependencies that have changed.
if s != "" {
debug.Println("go build cache exists, will ignore any compiled binary")
useCache = true
}
}
}

Expand Down
57 changes: 56 additions & 1 deletion mage/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func testmain(m *testing.M) int {
}

func TestTransitiveDepCache(t *testing.T) {
cache, err := internal.OutputDebug("go", "env", "gocache")
cache, err := internal.OutputDebug("go", "env", "GOCACHE")
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -108,6 +108,61 @@ func TestTransitiveDepCache(t *testing.T) {
}
}

func TestTransitiveHashFast(t *testing.T) {
cache, err := internal.OutputDebug("go", "env", "GOCACHE")
if err != nil {
t.Fatal(err)
}
if cache == "" {
t.Skip("skipping hashfast tests on go version without cache")
}

// Test that if we change a transitive dep, that we don't recompile.
// We intentionally run the first time without hashfast to ensure that
// we recompile the binary with the current code.
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
inv := Invocation{
Stderr: stderr,
Stdout: stdout,
Dir: "testdata/transitiveDeps",
Args: []string{"Run"},
}
code := Invoke(inv)
if code != 0 {
t.Fatalf("got code %v, err: %s", code, stderr)
}
expected := "woof\n"
if actual := stdout.String(); actual != expected {
t.Fatalf("expected %q but got %q", expected, actual)
}

// ok, so baseline, the generated and cached binary should do "woof"
// now change out the transitive dependency that does the output
// so that it produces different output.
if err := os.Rename("testdata/transitiveDeps/dep/dog.go", "testdata/transitiveDeps/dep/dog.notgo"); err != nil {
t.Fatal(err)
}
defer os.Rename("testdata/transitiveDeps/dep/dog.notgo", "testdata/transitiveDeps/dep/dog.go")
if err := os.Rename("testdata/transitiveDeps/dep/cat.notgo", "testdata/transitiveDeps/dep/cat.go"); err != nil {
t.Fatal(err)
}
defer os.Rename("testdata/transitiveDeps/dep/cat.go", "testdata/transitiveDeps/dep/cat.notgo")
stderr.Reset()
stdout.Reset()
inv.HashFast = true
code = Invoke(inv)
if code != 0 {
t.Fatalf("got code %v, err: %s", code, stderr)
}
// we should still get woof, even though the dependency was changed to
// return "meow", because we're only hashing the top level magefiles, not
// dependencies.
if actual := stdout.String(); actual != expected {
t.Fatalf("expected %q but got %q", expected, actual)
}
}

func TestListMagefilesMain(t *testing.T) {
buf := &bytes.Buffer{}
files, err := Magefiles("testdata/mixed_main_files", "", "", "go", buf, false)
Expand Down
2 changes: 1 addition & 1 deletion mage/testdata/transitiveDeps/magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

package main

import "./dep"
import "github.com/magefile/mage/mage/testdata/transitiveDeps/dep"

func Run() {
dep.Speak()
Expand Down
14 changes: 14 additions & 0 deletions mg/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ const GoCmdEnv = "MAGEFILE_GOCMD"
// to ignore the default target specified in the magefile.
const IgnoreDefaultEnv = "MAGEFILE_IGNOREDEFAULT"

// HashFastEnv is the environment variable that indicates the user requested to
// use a quick hash of magefiles to determine whether or not the magefile binary
// needs to be rebuilt. This results in faster runtimes, but means that mage
// will fail to rebuild if a dependency has changed. To force a rebuild, run
// mage with the -f flag.
const HashFastEnv = "MAGEFILE_HASHFAST"

// Verbose reports whether a magefile was run with the verbose flag.
func Verbose() bool {
b, _ := strconv.ParseBool(os.Getenv(VerboseEnv))
Expand All @@ -48,6 +55,13 @@ func GoCmd() string {
return "go"
}

// HashFast reports whether the user has requested to use the fast hashing
// mechanism rather than rely on go's rebuilding mechanism.
func HashFast() bool {
b, _ := strconv.ParseBool(os.Getenv(HashFastEnv))
return b
}

// IgnoreDefault reports whether the user has requested to ignore the default target
// in the magefile.
func IgnoreDefault() bool {
Expand Down
12 changes: 10 additions & 2 deletions site/content/environment/_index.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,13 @@ Sets the binary that mage will use to compile with (default is "go").

## MAGEFILE_IGNOREDEFAULT

If set to 1 or true, will tell the compiled magefile to ignore the default
target and print the list of targets when you run `mage`.
If set to "1" or "true", tells the compiled magefile to ignore the default
target and print the list of targets when you run `mage`.

## MAGEFILE_HASHFAST

If set to "1" or "true", tells mage to use a quick hash of magefiles to
determine whether or not the magefile binary needs to be rebuilt. This results
in faster run times (especially on Windows), but means that mage will fail to
rebuild if a dependency has changed. To force a rebuild when you know or suspect
a dependency has changed, run mage with the -f flag.