16
16
package main
17
17
18
18
import (
19
+ "bytes"
19
20
"errors"
20
21
"fmt"
22
+ "io/ioutil"
21
23
"log"
22
24
"os"
23
25
"os/exec"
@@ -95,6 +97,9 @@ func installTip(root, clNumber string) error {
95
97
cmd .Dir = root
96
98
return cmd .Output ()
97
99
}
100
+ chomp := func (s []byte ) []byte {
101
+ return bytes .TrimRight (s , " \t \r \n " )
102
+ }
98
103
99
104
if _ , err := os .Stat (filepath .Join (root , ".git" )); err != nil {
100
105
if err := os .MkdirAll (root , 0755 ); err != nil {
@@ -105,6 +110,16 @@ func installTip(root, clNumber string) error {
105
110
}
106
111
}
107
112
113
+ // Get current HEAD if there is an existing build.
114
+ var oldHead []byte
115
+ if _ , err := os .Stat (filepath .Join (root , builtOkay )); err == nil {
116
+ head , err := gitOutput ("rev-parse" , "--short" , "HEAD" )
117
+ if err != nil {
118
+ return fmt .Errorf ("failed to parse old HEAD revision: %v" , err )
119
+ }
120
+ oldHead = head
121
+ }
122
+
108
123
if clNumber != "" {
109
124
fmt .Fprintf (os .Stderr , "This will download and execute code from golang.org/cl/%s, continue? [y/n] " , clNumber )
110
125
var answer string
@@ -152,6 +167,23 @@ func installTip(root, clNumber string) error {
152
167
if err := git ("-c" , "advice.detachedHead=false" , "checkout" , "FETCH_HEAD" ); err != nil {
153
168
return fmt .Errorf ("failed to checkout git repository: %v" , err )
154
169
}
170
+
171
+ // Compare old and new HEADs to avoid unnecessary rebuilds if there are no changes.
172
+ // Notice that oldHead is not nil iff the last build was successful.
173
+ if oldHead != nil {
174
+ newHead , err := gitOutput ("rev-parse" , "--short" , "HEAD" )
175
+ if err != nil {
176
+ return fmt .Errorf ("failed to parse new HEAD revision: %v" , err )
177
+ }
178
+ if bytes .Equal (oldHead , newHead ) {
179
+ log .Printf ("Already built %s in %v" , chomp (newHead ), root )
180
+ return nil
181
+ }
182
+ if err := os .Remove (filepath .Join (root , builtOkay )); err != nil {
183
+ return err
184
+ }
185
+ }
186
+
155
187
// It shouldn't be the case, but in practice sometimes binary artifacts
156
188
// generated by earlier Go versions interfere with the build.
157
189
//
@@ -181,7 +213,9 @@ func installTip(root, clNumber string) error {
181
213
if err := cmd .Run (); err != nil {
182
214
return fmt .Errorf ("failed to build go: %v" , err )
183
215
}
184
-
216
+ if err := ioutil .WriteFile (filepath .Join (root , builtOkay ), nil , 0644 ); err != nil {
217
+ return err
218
+ }
185
219
return nil
186
220
}
187
221
@@ -198,6 +232,10 @@ func makeScript() string {
198
232
199
233
const caseInsensitiveEnv = runtime .GOOS == "windows"
200
234
235
+ // builtOkay is a sentinel zero-byte file to indicate that Go
236
+ // repository was cloned and built successfully.
237
+ const builtOkay = ".built-success"
238
+
201
239
func exe () string {
202
240
if runtime .GOOS == "windows" {
203
241
return ".exe"
0 commit comments