Source file 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  	"bytes"
     9  	"flag"
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"strconv"
    16  	"strings"
    17  	"sync"
    18  )
    19  
    20  // pathf is fmt.Sprintf for generating paths
    21  // (on windows it turns / into \ after the printf).
    22  func pathf(format string, args ...any) string {
    23  	return filepath.Clean(fmt.Sprintf(format, args...))
    24  }
    25  
    26  const (
    27  	CheckExit = 1 << iota
    28  	ShowOutput
    29  )
    30  
    31  var outputLock sync.Mutex
    32  
    33  // run is like runEnv with no additional environment.
    34  func run(dir string, mode int, cmd ...string) string {
    35  	return runEnv(dir, mode, nil, cmd...)
    36  }
    37  
    38  // runEnv runs the command line cmd in dir with additional environment env.
    39  // If mode has ShowOutput set, run passes cmd's output to
    40  // stdout/stderr directly. Otherwise, run returns cmd's output as a string.
    41  // If mode has CheckExit set and the command fails, run calls fatalf.
    42  func runEnv(dir string, mode int, env []string, cmd ...string) string {
    43  	if vflag > 1 {
    44  		errprintf("run: %s\n", strings.Join(cmd, " "))
    45  	}
    46  
    47  	xcmd := exec.Command(cmd[0], cmd[1:]...)
    48  	if env != nil {
    49  		xcmd.Env = append(os.Environ(), env...)
    50  	}
    51  	setDir(xcmd, dir)
    52  	var data []byte
    53  	var err error
    54  
    55  	// If we want to show command output and this is not
    56  	// a background command, assume it's the only thing
    57  	// running, so we can just let it write directly stdout/stderr
    58  	// as it runs without fear of mixing the output with some
    59  	// other command's output. Not buffering lets the output
    60  	// appear as it is printed instead of once the command exits.
    61  	// This is most important for the invocation of 'go build -v bootstrap/...'.
    62  	if mode&ShowOutput != 0 {
    63  		xcmd.Stdout = os.Stdout
    64  		xcmd.Stderr = os.Stderr
    65  		err = xcmd.Run()
    66  	} else {
    67  		data, err = xcmd.CombinedOutput()
    68  	}
    69  	if err != nil && mode&CheckExit != 0 {
    70  		outputLock.Lock()
    71  		if len(data) > 0 {
    72  			xprintf("%s\n", data)
    73  		}
    74  		outputLock.Unlock()
    75  		fatalf("FAILED: %v: %v", strings.Join(cmd, " "), err)
    76  	}
    77  	if mode&ShowOutput != 0 {
    78  		outputLock.Lock()
    79  		os.Stdout.Write(data)
    80  		outputLock.Unlock()
    81  	}
    82  	if vflag > 2 {
    83  		errprintf("run: %s DONE\n", strings.Join(cmd, " "))
    84  	}
    85  	return string(data)
    86  }
    87  
    88  var maxbg = 4 /* maximum number of jobs to run at once */
    89  
    90  // isdir reports whether p names an existing directory.
    91  func isdir(p string) bool {
    92  	fi, err := os.Stat(p)
    93  	return err == nil && fi.IsDir()
    94  }
    95  
    96  // isfile reports whether p names an existing file.
    97  func isfile(p string) bool {
    98  	fi, err := os.Stat(p)
    99  	return err == nil && fi.Mode().IsRegular()
   100  }
   101  
   102  // readfile returns the content of the named file.
   103  func readfile(file string) string {
   104  	data, err := os.ReadFile(file)
   105  	if err != nil {
   106  		fatalf("%v", err)
   107  	}
   108  	return string(data)
   109  }
   110  
   111  const (
   112  	writeExec = 1 << iota
   113  	writeSkipSame
   114  )
   115  
   116  // writefile writes text to the named file, creating it if needed.
   117  // if exec is non-zero, marks the file as executable.
   118  // If the file already exists and has the expected content,
   119  // it is not rewritten, to avoid changing the time stamp.
   120  func writefile(text, file string, flag int) {
   121  	new := []byte(text)
   122  	if flag&writeSkipSame != 0 {
   123  		old, err := os.ReadFile(file)
   124  		if err == nil && bytes.Equal(old, new) {
   125  			return
   126  		}
   127  	}
   128  	mode := os.FileMode(0666)
   129  	if flag&writeExec != 0 {
   130  		mode = 0777
   131  	}
   132  	xremove(file) // in case of symlink tricks by misc/reboot test
   133  	err := os.WriteFile(file, new, mode)
   134  	if err != nil {
   135  		fatalf("%v", err)
   136  	}
   137  }
   138  
   139  // xmkdir creates the directory p.
   140  func xmkdir(p string) {
   141  	err := os.Mkdir(p, 0777)
   142  	if err != nil {
   143  		fatalf("%v", err)
   144  	}
   145  }
   146  
   147  // xmkdirall creates the directory p and its parents, as needed.
   148  func xmkdirall(p string) {
   149  	err := os.MkdirAll(p, 0777)
   150  	if err != nil {
   151  		fatalf("%v", err)
   152  	}
   153  }
   154  
   155  // xremove removes the file p.
   156  func xremove(p string) {
   157  	if vflag > 2 {
   158  		errprintf("rm %s\n", p)
   159  	}
   160  	os.Remove(p)
   161  }
   162  
   163  // xremoveall removes the file or directory tree rooted at p.
   164  func xremoveall(p string) {
   165  	if vflag > 2 {
   166  		errprintf("rm -r %s\n", p)
   167  	}
   168  	os.RemoveAll(p)
   169  }
   170  
   171  // xreaddir replaces dst with a list of the names of the files and subdirectories in dir.
   172  // The names are relative to dir; they are not full paths.
   173  func xreaddir(dir string) []string {
   174  	f, err := os.Open(dir)
   175  	if err != nil {
   176  		fatalf("%v", err)
   177  	}
   178  	defer f.Close()
   179  	names, err := f.Readdirnames(-1)
   180  	if err != nil {
   181  		fatalf("reading %s: %v", dir, err)
   182  	}
   183  	return names
   184  }
   185  
   186  // xworkdir creates a new temporary directory to hold object files
   187  // and returns the name of that directory.
   188  func xworkdir() string {
   189  	name, err := os.MkdirTemp(os.Getenv("GOTMPDIR"), "go-tool-dist-")
   190  	if err != nil {
   191  		fatalf("%v", err)
   192  	}
   193  	return name
   194  }
   195  
   196  // fatalf prints an error message to standard error and exits.
   197  func fatalf(format string, args ...any) {
   198  	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
   199  	xexit(2)
   200  }
   201  
   202  var atexits []func()
   203  
   204  // xexit exits the process with return code n.
   205  func xexit(n int) {
   206  	for i := len(atexits) - 1; i >= 0; i-- {
   207  		atexits[i]()
   208  	}
   209  	os.Exit(n)
   210  }
   211  
   212  // xatexit schedules the exit-handler f to be run when the program exits.
   213  func xatexit(f func()) {
   214  	atexits = append(atexits, f)
   215  }
   216  
   217  // xprintf prints a message to standard output.
   218  func xprintf(format string, args ...any) {
   219  	fmt.Printf(format, args...)
   220  }
   221  
   222  // errprintf prints a message to standard output.
   223  func errprintf(format string, args ...any) {
   224  	fmt.Fprintf(os.Stderr, format, args...)
   225  }
   226  
   227  func xgetgoarm() string {
   228  	// If we're building on an actual arm system, and not building
   229  	// a cross-compiling toolchain, try to exec ourselves
   230  	// to detect whether VFP is supported and set the default GOARM.
   231  	// We've always assumed Android is ARMv7.
   232  	if gohostarch == "arm" && goarch == "arm" && goos == gohostos && goos != "android" {
   233  		// Try to exec ourselves in a mode to detect VFP support.
   234  		// Seeing how far it gets determines which instructions failed.
   235  		// The test is OS-agnostic.
   236  		out := run("", 0, os.Args[0], "-check-goarm")
   237  		v1ok := strings.Contains(out, "VFPv1 OK.")
   238  		v3ok := strings.Contains(out, "VFPv3 OK.")
   239  		if v1ok && v3ok {
   240  			return "7"
   241  		}
   242  		if v1ok {
   243  			return "6"
   244  		}
   245  		return "5"
   246  	}
   247  
   248  	// Otherwise, in the absence of local information, assume GOARM=7.
   249  	//
   250  	// We used to assume GOARM=5 in certain contexts but not others,
   251  	// which produced inconsistent results. For example if you cross-compiled
   252  	// for linux/arm from a windows/amd64 machine, you got GOARM=7 binaries,
   253  	// but if you cross-compiled for linux/arm from a linux/amd64 machine,
   254  	// you got GOARM=5 binaries. Now the default is independent of the
   255  	// host operating system, for better reproducibility of builds.
   256  	return "7"
   257  }
   258  
   259  // elfIsLittleEndian detects if the ELF file is little endian.
   260  func elfIsLittleEndian(fn string) bool {
   261  	// read the ELF file header to determine the endianness without using the
   262  	// debug/elf package.
   263  	file, err := os.Open(fn)
   264  	if err != nil {
   265  		fatalf("failed to open file to determine endianness: %v", err)
   266  	}
   267  	defer file.Close()
   268  	var hdr [16]byte
   269  	if _, err := io.ReadFull(file, hdr[:]); err != nil {
   270  		fatalf("failed to read ELF header to determine endianness: %v", err)
   271  	}
   272  	// hdr[5] is EI_DATA byte, 1 is ELFDATA2LSB and 2 is ELFDATA2MSB
   273  	switch hdr[5] {
   274  	default:
   275  		fatalf("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5])
   276  	case 1:
   277  		return true
   278  	case 2:
   279  		return false
   280  	}
   281  	panic("unreachable")
   282  }
   283  
   284  // count is a flag.Value that is like a flag.Bool and a flag.Int.
   285  // If used as -name, it increments the count, but -name=x sets the count.
   286  // Used for verbose flag -v.
   287  type count int
   288  
   289  func (c *count) String() string {
   290  	return fmt.Sprint(int(*c))
   291  }
   292  
   293  func (c *count) Set(s string) error {
   294  	switch s {
   295  	case "true":
   296  		*c++
   297  	case "false":
   298  		*c = 0
   299  	default:
   300  		n, err := strconv.Atoi(s)
   301  		if err != nil {
   302  			return fmt.Errorf("invalid count %q", s)
   303  		}
   304  		*c = count(n)
   305  	}
   306  	return nil
   307  }
   308  
   309  func (c *count) IsBoolFlag() bool {
   310  	return true
   311  }
   312  
   313  func xflagparse(maxargs int) {
   314  	flag.Var((*count)(&vflag), "v", "verbosity")
   315  	flag.Parse()
   316  	if maxargs >= 0 && flag.NArg() > maxargs {
   317  		flag.Usage()
   318  	}
   319  }
   320  

View as plain text