From cb27fed361a2effcc438ccf218b110d6a5489245 Mon Sep 17 00:00:00 2001 From: Ivan Trubach Date: Sun, 24 May 2020 14:15:26 +0300 Subject: [PATCH] Do not rebuild gotip if the HEAD does not change On successful build a sentinel zero-byte file is written, similar to .unpacked-success in internal/version. If such file exists on next gotip download, compare HEADs before and after FETCH_HEAD checkout, and trigger the build iff they differ. In that case, sentinel file is removed to make git clean happy and indicate that rebuild is needed. --- gotip/main.go | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/gotip/main.go b/gotip/main.go index d1c69cc7..81bf2407 100644 --- a/gotip/main.go +++ b/gotip/main.go @@ -16,8 +16,10 @@ package main import ( + "bytes" "errors" "fmt" + "io/ioutil" "log" "os" "os/exec" @@ -95,6 +97,9 @@ func installTip(root, clNumber string) error { cmd.Dir = root return cmd.Output() } + chomp := func(s []byte) []byte { + return bytes.TrimRight(s, " \t\r\n") + } if _, err := os.Stat(filepath.Join(root, ".git")); err != nil { if err := os.MkdirAll(root, 0755); err != nil { @@ -105,6 +110,16 @@ func installTip(root, clNumber string) error { } } + // Get current HEAD if there is an existing build. + var oldHead []byte + if _, err := os.Stat(filepath.Join(root, builtOkay)); err == nil { + head, err := gitOutput("rev-parse", "--short", "HEAD") + if err != nil { + return fmt.Errorf("failed to parse old HEAD revision: %v", err) + } + oldHead = head + } + if clNumber != "" { fmt.Fprintf(os.Stderr, "This will download and execute code from golang.org/cl/%s, continue? [y/n] ", clNumber) var answer string @@ -152,6 +167,23 @@ func installTip(root, clNumber string) error { if err := git("-c", "advice.detachedHead=false", "checkout", "FETCH_HEAD"); err != nil { return fmt.Errorf("failed to checkout git repository: %v", err) } + + // Compare old and new HEADs to avoid unnecessary rebuilds if there are no changes. + // Notice that oldHead is not nil iff the last build was successful. + if oldHead != nil { + newHead, err := gitOutput("rev-parse", "--short", "HEAD") + if err != nil { + return fmt.Errorf("failed to parse new HEAD revision: %v", err) + } + if bytes.Equal(oldHead, newHead) { + log.Printf("Already built %s in %v", chomp(newHead), root) + return nil + } + if err := os.Remove(filepath.Join(root, builtOkay)); err != nil { + return err + } + } + // It shouldn't be the case, but in practice sometimes binary artifacts // generated by earlier Go versions interfere with the build. // @@ -181,7 +213,9 @@ func installTip(root, clNumber string) error { if err := cmd.Run(); err != nil { return fmt.Errorf("failed to build go: %v", err) } - + if err := ioutil.WriteFile(filepath.Join(root, builtOkay), nil, 0644); err != nil { + return err + } return nil } @@ -198,6 +232,10 @@ func makeScript() string { const caseInsensitiveEnv = runtime.GOOS == "windows" +// builtOkay is a sentinel zero-byte file to indicate that Go +// repository was cloned and built successfully. +const builtOkay = ".built-success" + func exe() string { if runtime.GOOS == "windows" { return ".exe"