Source file
src/cmd/dist/util.go
1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "flag"
10 "fmt"
11 "io"
12 "os"
13 "os/exec"
14 "path/filepath"
15 "sort"
16 "strconv"
17 "strings"
18 "sync"
19 "time"
20 )
21
22
23
24 func pathf(format string, args ...interface{}) string {
25 return filepath.Clean(fmt.Sprintf(format, args...))
26 }
27
28
29 func filter(list []string, f func(string) bool) []string {
30 var out []string
31 for _, x := range list {
32 if f(x) {
33 out = append(out, x)
34 }
35 }
36 return out
37 }
38
39
40 func uniq(list []string) []string {
41 out := make([]string, len(list))
42 copy(out, list)
43 sort.Strings(out)
44 keep := out[:0]
45 for _, x := range out {
46 if len(keep) == 0 || keep[len(keep)-1] != x {
47 keep = append(keep, x)
48 }
49 }
50 return keep
51 }
52
53 const (
54 CheckExit = 1 << iota
55 ShowOutput
56 Background
57 )
58
59 var outputLock sync.Mutex
60
61
62 func run(dir string, mode int, cmd ...string) string {
63 return runEnv(dir, mode, nil, cmd...)
64 }
65
66
67
68
69
70
71
72
73 func runEnv(dir string, mode int, env []string, cmd ...string) string {
74 if vflag > 1 {
75 errprintf("run: %s\n", strings.Join(cmd, " "))
76 }
77
78 xcmd := exec.Command(cmd[0], cmd[1:]...)
79 if env != nil {
80 xcmd.Env = append(os.Environ(), env...)
81 }
82 setDir(xcmd, dir)
83 var data []byte
84 var err error
85
86
87
88
89
90
91
92
93 if mode&(Background|ShowOutput) == ShowOutput {
94 xcmd.Stdout = os.Stdout
95 xcmd.Stderr = os.Stderr
96 err = xcmd.Run()
97 } else {
98 data, err = xcmd.CombinedOutput()
99 }
100 if err != nil && mode&CheckExit != 0 {
101 outputLock.Lock()
102 if len(data) > 0 {
103 xprintf("%s\n", data)
104 }
105 outputLock.Unlock()
106 if mode&Background != 0 {
107
108
109 bghelpers.Done()
110 }
111 fatalf("FAILED: %v: %v", strings.Join(cmd, " "), err)
112 }
113 if mode&ShowOutput != 0 {
114 outputLock.Lock()
115 os.Stdout.Write(data)
116 outputLock.Unlock()
117 }
118 if vflag > 2 {
119 errprintf("run: %s DONE\n", strings.Join(cmd, " "))
120 }
121 return string(data)
122 }
123
124 var maxbg = 4
125
126 var (
127 bgwork = make(chan func(), 1e5)
128
129 bghelpers sync.WaitGroup
130
131 dieOnce sync.Once
132 dying = make(chan struct{})
133 )
134
135 func bginit() {
136 bghelpers.Add(maxbg)
137 for i := 0; i < maxbg; i++ {
138 go bghelper()
139 }
140 }
141
142 func bghelper() {
143 defer bghelpers.Done()
144 for {
145 select {
146 case <-dying:
147 return
148 case w := <-bgwork:
149
150 select {
151 case <-dying:
152 return
153 default:
154 w()
155 }
156 }
157 }
158 }
159
160
161
162
163 func bgrun(wg *sync.WaitGroup, dir string, cmd ...string) {
164 wg.Add(1)
165 bgwork <- func() {
166 defer wg.Done()
167 run(dir, CheckExit|ShowOutput|Background, cmd...)
168 }
169 }
170
171
172
173 func bgwait(wg *sync.WaitGroup) {
174 done := make(chan struct{})
175 go func() {
176 wg.Wait()
177 close(done)
178 }()
179 select {
180 case <-done:
181 case <-dying:
182
183
184 select {}
185 }
186 }
187
188
189 func xgetwd() string {
190 wd, err := os.Getwd()
191 if err != nil {
192 fatalf("%s", err)
193 }
194 return wd
195 }
196
197
198
199 func xrealwd(path string) string {
200 old := xgetwd()
201 if err := os.Chdir(path); err != nil {
202 fatalf("chdir %s: %v", path, err)
203 }
204 real := xgetwd()
205 if err := os.Chdir(old); err != nil {
206 fatalf("chdir %s: %v", old, err)
207 }
208 return real
209 }
210
211
212 func isdir(p string) bool {
213 fi, err := os.Stat(p)
214 return err == nil && fi.IsDir()
215 }
216
217
218 func isfile(p string) bool {
219 fi, err := os.Stat(p)
220 return err == nil && fi.Mode().IsRegular()
221 }
222
223
224 func mtime(p string) time.Time {
225 fi, err := os.Stat(p)
226 if err != nil {
227 return time.Time{}
228 }
229 return fi.ModTime()
230 }
231
232
233 func readfile(file string) string {
234 data, err := os.ReadFile(file)
235 if err != nil {
236 fatalf("%v", err)
237 }
238 return string(data)
239 }
240
241 const (
242 writeExec = 1 << iota
243 writeSkipSame
244 )
245
246
247
248
249
250 func writefile(text, file string, flag int) {
251 new := []byte(text)
252 if flag&writeSkipSame != 0 {
253 old, err := os.ReadFile(file)
254 if err == nil && bytes.Equal(old, new) {
255 return
256 }
257 }
258 mode := os.FileMode(0666)
259 if flag&writeExec != 0 {
260 mode = 0777
261 }
262 xremove(file)
263 err := os.WriteFile(file, new, mode)
264 if err != nil {
265 fatalf("%v", err)
266 }
267 }
268
269
270 func xmkdir(p string) {
271 err := os.Mkdir(p, 0777)
272 if err != nil {
273 fatalf("%v", err)
274 }
275 }
276
277
278 func xmkdirall(p string) {
279 err := os.MkdirAll(p, 0777)
280 if err != nil {
281 fatalf("%v", err)
282 }
283 }
284
285
286 func xremove(p string) {
287 if vflag > 2 {
288 errprintf("rm %s\n", p)
289 }
290 os.Remove(p)
291 }
292
293
294 func xremoveall(p string) {
295 if vflag > 2 {
296 errprintf("rm -r %s\n", p)
297 }
298 os.RemoveAll(p)
299 }
300
301
302
303 func xreaddir(dir string) []string {
304 f, err := os.Open(dir)
305 if err != nil {
306 fatalf("%v", err)
307 }
308 defer f.Close()
309 names, err := f.Readdirnames(-1)
310 if err != nil {
311 fatalf("reading %s: %v", dir, err)
312 }
313 return names
314 }
315
316
317
318 func xworkdir() string {
319 name, err := os.MkdirTemp(os.Getenv("GOTMPDIR"), "go-tool-dist-")
320 if err != nil {
321 fatalf("%v", err)
322 }
323 return name
324 }
325
326
327 func fatalf(format string, args ...interface{}) {
328 fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
329
330 dieOnce.Do(func() { close(dying) })
331
332
333
334
335 bghelpers.Wait()
336
337 xexit(2)
338 }
339
340 var atexits []func()
341
342
343 func xexit(n int) {
344 for i := len(atexits) - 1; i >= 0; i-- {
345 atexits[i]()
346 }
347 os.Exit(n)
348 }
349
350
351 func xatexit(f func()) {
352 atexits = append(atexits, f)
353 }
354
355
356 func xprintf(format string, args ...interface{}) {
357 fmt.Printf(format, args...)
358 }
359
360
361 func errprintf(format string, args ...interface{}) {
362 fmt.Fprintf(os.Stderr, format, args...)
363 }
364
365
366 func xsamefile(f1, f2 string) bool {
367 fi1, err1 := os.Stat(f1)
368 fi2, err2 := os.Stat(f2)
369 if err1 != nil || err2 != nil {
370 return f1 == f2
371 }
372 return os.SameFile(fi1, fi2)
373 }
374
375 func xgetgoarm() string {
376
377
378
379
380
381 if gohostarch == "arm" && goarch == "arm" && goos == gohostos && goos != "windows" && goos != "android" {
382
383
384
385 out := run("", 0, os.Args[0], "-check-goarm")
386 v1ok := strings.Contains(out, "VFPv1 OK.")
387 v3ok := strings.Contains(out, "VFPv3 OK.")
388 if v1ok && v3ok {
389 return "7"
390 }
391 if v1ok {
392 return "6"
393 }
394 return "5"
395 }
396
397
398
399
400
401
402
403
404
405 return "7"
406 }
407
408 func min(a, b int) int {
409 if a < b {
410 return a
411 }
412 return b
413 }
414
415
416 func elfIsLittleEndian(fn string) bool {
417
418
419 file, err := os.Open(fn)
420 if err != nil {
421 fatalf("failed to open file to determine endianness: %v", err)
422 }
423 defer file.Close()
424 var hdr [16]byte
425 if _, err := io.ReadFull(file, hdr[:]); err != nil {
426 fatalf("failed to read ELF header to determine endianness: %v", err)
427 }
428
429 switch hdr[5] {
430 default:
431 fatalf("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5])
432 case 1:
433 return true
434 case 2:
435 return false
436 }
437 panic("unreachable")
438 }
439
440
441
442
443 type count int
444
445 func (c *count) String() string {
446 return fmt.Sprint(int(*c))
447 }
448
449 func (c *count) Set(s string) error {
450 switch s {
451 case "true":
452 *c++
453 case "false":
454 *c = 0
455 default:
456 n, err := strconv.Atoi(s)
457 if err != nil {
458 return fmt.Errorf("invalid count %q", s)
459 }
460 *c = count(n)
461 }
462 return nil
463 }
464
465 func (c *count) IsBoolFlag() bool {
466 return true
467 }
468
469 func xflagparse(maxargs int) {
470 flag.Var((*count)(&vflag), "v", "verbosity")
471 flag.Parse()
472 if maxargs >= 0 && flag.NArg() > maxargs {
473 flag.Usage()
474 }
475 }
476
View as plain text