]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/dist/util.go
[dev.cc] all: merge master (5868ce3) into dev.cc
[gostls13.git] / src / cmd / dist / util.go
1 // Copyright 2012 The Go Authors.  All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package main
6
7 import (
8         "fmt"
9         "io/ioutil"
10         "os"
11         "os/exec"
12         "path/filepath"
13         "runtime"
14         "sort"
15         "strconv"
16         "strings"
17         "sync"
18         "sync/atomic"
19         "time"
20 )
21
22 // pathf is fmt.Sprintf for generating paths
23 // (on windows it turns / into \ after the printf).
24 func pathf(format string, args ...interface{}) string {
25         return filepath.Clean(fmt.Sprintf(format, args...))
26 }
27
28 // filter returns a slice containing the elements x from list for which f(x) == true.
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 // uniq returns a sorted slice containing the unique elements of list.
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 // splitlines returns a slice with the result of splitting
54 // the input p after each \n.
55 func splitlines(p string) []string {
56         return strings.SplitAfter(p, "\n")
57 }
58
59 // splitfields replaces the vector v with the result of splitting
60 // the input p into non-empty fields containing no spaces.
61 func splitfields(p string) []string {
62         return strings.Fields(p)
63 }
64
65 const (
66         CheckExit = 1 << iota
67         ShowOutput
68         Background
69 )
70
71 var outputLock sync.Mutex
72
73 // run runs the command line cmd in dir.
74 // If mode has ShowOutput set, run collects cmd's output and returns it as a string;
75 // otherwise, run prints cmd's output to standard output after the command finishes.
76 // If mode has CheckExit set and the command fails, run calls fatal.
77 // If mode has Background set, this command is being run as a
78 // Background job. Only bgrun should use the Background mode,
79 // not other callers.
80 func run(dir string, mode int, cmd ...string) string {
81         if vflag > 1 {
82                 errprintf("run: %s\n", strings.Join(cmd, " "))
83         }
84
85         xcmd := exec.Command(cmd[0], cmd[1:]...)
86         xcmd.Dir = dir
87         var data []byte
88         var err error
89
90         // If we want to show command output and this is not
91         // a background command, assume it's the only thing
92         // running, so we can just let it write directly stdout/stderr
93         // as it runs without fear of mixing the output with some
94         // other command's output. Not buffering lets the output
95         // appear as it is printed instead of once the command exits.
96         // This is most important for the invocation of 'go1.4 build -v bootstrap/...'.
97         if mode&(Background|ShowOutput) == ShowOutput {
98                 xcmd.Stdout = os.Stdout
99                 xcmd.Stderr = os.Stderr
100                 err = xcmd.Run()
101         } else {
102                 data, err = xcmd.CombinedOutput()
103         }
104         if err != nil && mode&CheckExit != 0 {
105                 outputLock.Lock()
106                 if len(data) > 0 {
107                         xprintf("%s\n", data)
108                 }
109                 outputLock.Unlock()
110                 if mode&Background != 0 {
111                         bgdied.Done()
112                 }
113                 fatal("FAILED: %v", strings.Join(cmd, " "))
114         }
115         if mode&ShowOutput != 0 {
116                 outputLock.Lock()
117                 os.Stdout.Write(data)
118                 outputLock.Unlock()
119         }
120         if vflag > 2 {
121                 errprintf("run: %s DONE\n", strings.Join(cmd, " "))
122         }
123         return string(data)
124 }
125
126 var maxbg = 4 /* maximum number of jobs to run at once */
127
128 var (
129         bgwork = make(chan func(), 1e5)
130         bgdone = make(chan struct{}, 1e5)
131
132         bgdied sync.WaitGroup
133         nwork  int32
134         ndone  int32
135
136         dying  = make(chan bool)
137         nfatal int32
138 )
139
140 func bginit() {
141         bgdied.Add(maxbg)
142         for i := 0; i < maxbg; i++ {
143                 go bghelper()
144         }
145 }
146
147 func bghelper() {
148         for {
149                 w := <-bgwork
150                 w()
151
152                 // Stop if we're dying.
153                 if atomic.LoadInt32(&nfatal) > 0 {
154                         bgdied.Done()
155                         return
156                 }
157         }
158 }
159
160 // bgrun is like run but runs the command in the background.
161 // CheckExit|ShowOutput mode is implied (since output cannot be returned).
162 func bgrun(dir string, cmd ...string) {
163         bgwork <- func() {
164                 run(dir, CheckExit|ShowOutput|Background, cmd...)
165         }
166 }
167
168 // bgwait waits for pending bgruns to finish.
169 // bgwait must be called from only a single goroutine at a time.
170 func bgwait() {
171         var wg sync.WaitGroup
172         wg.Add(maxbg)
173         done := make(chan bool)
174         for i := 0; i < maxbg; i++ {
175                 bgwork <- func() {
176                         wg.Done()
177
178                         // Hold up bg goroutine until either the wait finishes
179                         // or the program starts dying due to a call to fatal.
180                         select {
181                         case <-dying:
182                         case <-done:
183                         }
184                 }
185         }
186         wg.Wait()
187         close(done)
188 }
189
190 // xgetwd returns the current directory.
191 func xgetwd() string {
192         wd, err := os.Getwd()
193         if err != nil {
194                 fatal("%s", err)
195         }
196         return wd
197 }
198
199 // xrealwd returns the 'real' name for the given path.
200 // real is defined as what xgetwd returns in that directory.
201 func xrealwd(path string) string {
202         old := xgetwd()
203         if err := os.Chdir(path); err != nil {
204                 fatal("chdir %s: %v", path, err)
205         }
206         real := xgetwd()
207         if err := os.Chdir(old); err != nil {
208                 fatal("chdir %s: %v", old, err)
209         }
210         return real
211 }
212
213 // isdir reports whether p names an existing directory.
214 func isdir(p string) bool {
215         fi, err := os.Stat(p)
216         return err == nil && fi.IsDir()
217 }
218
219 // isfile reports whether p names an existing file.
220 func isfile(p string) bool {
221         fi, err := os.Stat(p)
222         return err == nil && fi.Mode().IsRegular()
223 }
224
225 // mtime returns the modification time of the file p.
226 func mtime(p string) time.Time {
227         fi, err := os.Stat(p)
228         if err != nil {
229                 return time.Time{}
230         }
231         return fi.ModTime()
232 }
233
234 // isabs reports whether p is an absolute path.
235 func isabs(p string) bool {
236         return filepath.IsAbs(p)
237 }
238
239 // readfile returns the content of the named file.
240 func readfile(file string) string {
241         data, err := ioutil.ReadFile(file)
242         if err != nil {
243                 fatal("%v", err)
244         }
245         return string(data)
246 }
247
248 // writefile writes b to the named file, creating it if needed.  if
249 // exec is non-zero, marks the file as executable.
250 func writefile(b, file string, exec int) {
251         mode := os.FileMode(0666)
252         if exec != 0 {
253                 mode = 0777
254         }
255         err := ioutil.WriteFile(file, []byte(b), mode)
256         if err != nil {
257                 fatal("%v", err)
258         }
259 }
260
261 // xmkdir creates the directory p.
262 func xmkdir(p string) {
263         err := os.Mkdir(p, 0777)
264         if err != nil {
265                 fatal("%v", err)
266         }
267 }
268
269 // xmkdirall creates the directory p and its parents, as needed.
270 func xmkdirall(p string) {
271         err := os.MkdirAll(p, 0777)
272         if err != nil {
273                 fatal("%v", err)
274         }
275 }
276
277 // xremove removes the file p.
278 func xremove(p string) {
279         if vflag > 2 {
280                 errprintf("rm %s\n", p)
281         }
282         os.Remove(p)
283 }
284
285 // xremoveall removes the file or directory tree rooted at p.
286 func xremoveall(p string) {
287         if vflag > 2 {
288                 errprintf("rm -r %s\n", p)
289         }
290         os.RemoveAll(p)
291 }
292
293 // xreaddir replaces dst with a list of the names of the files and subdirectories in dir.
294 // The names are relative to dir; they are not full paths.
295 func xreaddir(dir string) []string {
296         f, err := os.Open(dir)
297         if err != nil {
298                 fatal("%v", err)
299         }
300         defer f.Close()
301         names, err := f.Readdirnames(-1)
302         if err != nil {
303                 fatal("reading %s: %v", dir, err)
304         }
305         return names
306 }
307
308 // xreaddir replaces dst with a list of the names of the files in dir.
309 // The names are relative to dir; they are not full paths.
310 func xreaddirfiles(dir string) []string {
311         f, err := os.Open(dir)
312         if err != nil {
313                 fatal("%v", err)
314         }
315         defer f.Close()
316         infos, err := f.Readdir(-1)
317         if err != nil {
318                 fatal("reading %s: %v", dir, err)
319         }
320         var names []string
321         for _, fi := range infos {
322                 if !fi.IsDir() {
323                         names = append(names, fi.Name())
324                 }
325         }
326         return names
327 }
328
329 // xworkdir creates a new temporary directory to hold object files
330 // and returns the name of that directory.
331 func xworkdir() string {
332         name, err := ioutil.TempDir("", "go-tool-dist-")
333         if err != nil {
334                 fatal("%v", err)
335         }
336         return name
337 }
338
339 // fatal prints an error message to standard error and exits.
340 func fatal(format string, args ...interface{}) {
341         fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
342
343         // Wait for background goroutines to finish,
344         // so that exit handler that removes the work directory
345         // is not fighting with active writes or open files.
346         if atomic.AddInt32(&nfatal, 1) == 1 {
347                 close(dying)
348         }
349         for i := 0; i < maxbg; i++ {
350                 bgwork <- func() {} // wake up workers so they notice nfatal > 0
351         }
352         bgdied.Wait()
353
354         xexit(2)
355 }
356
357 var atexits []func()
358
359 // xexit exits the process with return code n.
360 func xexit(n int) {
361         for i := len(atexits) - 1; i >= 0; i-- {
362                 atexits[i]()
363         }
364         os.Exit(n)
365 }
366
367 // xatexit schedules the exit-handler f to be run when the program exits.
368 func xatexit(f func()) {
369         atexits = append(atexits, f)
370 }
371
372 // xprintf prints a message to standard output.
373 func xprintf(format string, args ...interface{}) {
374         fmt.Printf(format, args...)
375 }
376
377 // errprintf prints a message to standard output.
378 func errprintf(format string, args ...interface{}) {
379         fmt.Fprintf(os.Stderr, format, args...)
380 }
381
382 // main takes care of OS-specific startup and dispatches to xmain.
383 func main() {
384         os.Setenv("TERM", "dumb") // disable escape codes in clang errors
385
386         slash = string(filepath.Separator)
387
388         gohostos = runtime.GOOS
389         switch gohostos {
390         case "darwin":
391                 // Even on 64-bit platform, darwin uname -m prints i386.
392                 if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") {
393                         gohostarch = "amd64"
394                 }
395         case "solaris":
396                 // Even on 64-bit platform, solaris uname -m prints i86pc.
397                 out := run("", CheckExit, "isainfo", "-n")
398                 if strings.Contains(out, "amd64") {
399                         gohostarch = "amd64"
400                 }
401                 if strings.Contains(out, "i386") {
402                         gohostarch = "386"
403                 }
404         case "plan9":
405                 gohostarch = os.Getenv("objtype")
406                 if gohostarch == "" {
407                         fatal("$objtype is unset")
408                 }
409         case "windows":
410                 exe = ".exe"
411         }
412
413         sysinit()
414
415         if gohostarch == "" {
416                 // Default Unix system.
417                 out := run("", CheckExit, "uname", "-m")
418                 switch {
419                 case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"):
420                         gohostarch = "amd64"
421                 case strings.Contains(out, "86"):
422                         gohostarch = "386"
423                 case strings.Contains(out, "arm"):
424                         gohostarch = "arm"
425                 case strings.Contains(out, "ppc64le"):
426                         gohostarch = "ppc64le"
427                 case strings.Contains(out, "ppc64"):
428                         gohostarch = "ppc64"
429                 case gohostos == "darwin":
430                         if strings.Contains(run("", CheckExit, "uname", "-v"), "RELEASE_ARM_") {
431                                 gohostarch = "arm"
432                         }
433                 default:
434                         fatal("unknown architecture: %s", out)
435                 }
436         }
437
438         if gohostarch == "arm" {
439                 maxbg = 1
440         }
441         bginit()
442
443         // The OS X 10.6 linker does not support external linking mode.
444         // See golang.org/issue/5130.
445         //
446         // OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
447         // It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
448         // See golang.org/issue/5822.
449         //
450         // Roughly, OS X 10.N shows up as uname release (N+4),
451         // so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
452         if gohostos == "darwin" {
453                 rel := run("", CheckExit, "uname", "-r")
454                 if i := strings.Index(rel, "."); i >= 0 {
455                         rel = rel[:i]
456                 }
457                 osx, _ := strconv.Atoi(rel)
458                 if osx <= 6+4 {
459                         goextlinkenabled = "0"
460                 }
461                 if osx >= 8+4 {
462                         defaultclang = true
463                 }
464         }
465
466         xinit()
467         xmain()
468         xexit(0)
469 }
470
471 // xsamefile reports whether f1 and f2 are the same file (or dir)
472 func xsamefile(f1, f2 string) bool {
473         fi1, err1 := os.Stat(f1)
474         fi2, err2 := os.Stat(f2)
475         if err1 != nil || err2 != nil {
476                 return f1 == f2
477         }
478         return os.SameFile(fi1, fi2)
479 }
480
481 func cpuid(info *[4]uint32, ax uint32)
482
483 func cansse2() bool {
484         if gohostarch != "386" && gohostarch != "amd64" {
485                 return false
486         }
487
488         var info [4]uint32
489         cpuid(&info, 1)
490         return info[3]&(1<<26) != 0 // SSE2
491 }
492
493 func xgetgoarm() string {
494         if goos == "nacl" {
495                 // NaCl guarantees VFPv3 and is always cross-compiled.
496                 return "7"
497         }
498         if goos == "darwin" {
499                 // Assume all darwin/arm devices are have VFPv3. This
500                 // port is also mostly cross-compiled, so it makes little
501                 // sense to auto-detect the setting.
502                 return "7"
503         }
504         if gohostarch != "arm" || goos != gohostos {
505                 // Conservative default for cross-compilation.
506                 return "5"
507         }
508         if goos == "freebsd" {
509                 // FreeBSD has broken VFP support.
510                 return "5"
511         }
512         if goos != "linux" {
513                 // All other arm platforms that we support
514                 // require ARMv7.
515                 return "7"
516         }
517         cpuinfo := readfile("/proc/cpuinfo")
518         goarm := "5"
519         for _, line := range splitlines(cpuinfo) {
520                 line := strings.SplitN(line, ":", 2)
521                 if len(line) < 2 {
522                         continue
523                 }
524                 if strings.TrimSpace(line[0]) != "Features" {
525                         continue
526                 }
527                 features := splitfields(line[1])
528                 sort.Strings(features) // so vfpv3 sorts after vfp
529
530                 // Infer GOARM value from the vfp features available
531                 // on this host. Values of GOARM detected are:
532                 // 5: no vfp support was found
533                 // 6: vfp (v1) support was detected, but no higher
534                 // 7: vfpv3 support was detected.
535                 // This matches the assertions in runtime.checkarm.
536                 for _, f := range features {
537                         switch f {
538                         case "vfp":
539                                 goarm = "6"
540                         case "vfpv3":
541                                 goarm = "7"
542                         }
543                 }
544         }
545         return goarm
546 }