Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
util.go458 linesDownload Raw Back to dist
1// Copyright 2012 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4 5package main6 7import (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// pathf is fmt.Sprintf for generating paths23// (on windows it turns / into \ after the printf).24func pathf(format string, args ...any) 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.29func filter(list []string, f func(string) bool) []string {30	var out []string31	for _, x := range list {32		if f(x) {33			out = append(out, x)34		}35	}36	return out37}38 39// uniq returns a sorted slice containing the unique elements of list.40func 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 keep51}52 53const (54	CheckExit = 1 << iota55	ShowOutput56	Background57)58 59var outputLock sync.Mutex60 61// run is like runEnv with no additional environment.62func run(dir string, mode int, cmd ...string) string {63	return runEnv(dir, mode, nil, cmd...)64}65 66// runEnv runs the command line cmd in dir with additional environment env.67// If mode has ShowOutput set and Background unset, run passes cmd's output to68// stdout/stderr directly. Otherwise, run returns cmd's output as a string.69// If mode has CheckExit set and the command fails, run calls fatalf.70// If mode has Background set, this command is being run as a71// Background job. Only bgrun should use the Background mode,72// not other callers.73func 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 []byte84	var err error85 86	// If we want to show command output and this is not87	// a background command, assume it's the only thing88	// running, so we can just let it write directly stdout/stderr89	// as it runs without fear of mixing the output with some90	// other command's output. Not buffering lets the output91	// appear as it is printed instead of once the command exits.92	// This is most important for the invocation of 'go build -v bootstrap/...'.93	if mode&(Background|ShowOutput) == ShowOutput {94		xcmd.Stdout = os.Stdout95		xcmd.Stderr = os.Stderr96		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			// Prevent fatalf from waiting on our own goroutine's108			// bghelper to exit: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 124var maxbg = 4 /* maximum number of jobs to run at once */125 126var (127	bgwork = make(chan func(), 1e5)128 129	bghelpers sync.WaitGroup130 131	dieOnce sync.Once // guards close of dying132	dying   = make(chan struct{})133)134 135func bginit() {136	bghelpers.Add(maxbg)137	for i := 0; i < maxbg; i++ {138		go bghelper()139	}140}141 142func bghelper() {143	defer bghelpers.Done()144	for {145		select {146		case <-dying:147			return148		case w := <-bgwork:149			// Dying takes precedence over doing more work.150			select {151			case <-dying:152				return153			default:154				w()155			}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// bgrun adds 1 to wg immediately, and calls Done when the work completes.163func 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// bgwait waits for pending bgruns to finish.172// bgwait must be called from only a single goroutine at a time.173func 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		// Don't return to the caller, to avoid reporting additional errors183		// to the user.184		select {}185	}186}187 188// xgetwd returns the current directory.189func xgetwd() string {190	wd, err := os.Getwd()191	if err != nil {192		fatalf("%s", err)193	}194	return wd195}196 197// xrealwd returns the 'real' name for the given path.198// real is defined as what xgetwd returns in that directory.199func 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 real209}210 211// isdir reports whether p names an existing directory.212func isdir(p string) bool {213	fi, err := os.Stat(p)214	return err == nil && fi.IsDir()215}216 217// isfile reports whether p names an existing file.218func isfile(p string) bool {219	fi, err := os.Stat(p)220	return err == nil && fi.Mode().IsRegular()221}222 223// mtime returns the modification time of the file p.224func 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// readfile returns the content of the named file.233func 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 241const (242	writeExec = 1 << iota243	writeSkipSame244)245 246// writefile writes text to the named file, creating it if needed.247// if exec is non-zero, marks the file as executable.248// If the file already exists and has the expected content,249// it is not rewritten, to avoid changing the time stamp.250func 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			return256		}257	}258	mode := os.FileMode(0666)259	if flag&writeExec != 0 {260		mode = 0777261	}262	xremove(file) // in case of symlink tricks by misc/reboot test263	err := os.WriteFile(file, new, mode)264	if err != nil {265		fatalf("%v", err)266	}267}268 269// xmkdir creates the directory p.270func xmkdir(p string) {271	err := os.Mkdir(p, 0777)272	if err != nil {273		fatalf("%v", err)274	}275}276 277// xmkdirall creates the directory p and its parents, as needed.278func xmkdirall(p string) {279	err := os.MkdirAll(p, 0777)280	if err != nil {281		fatalf("%v", err)282	}283}284 285// xremove removes the file p.286func xremove(p string) {287	if vflag > 2 {288		errprintf("rm %s\n", p)289	}290	os.Remove(p)291}292 293// xremoveall removes the file or directory tree rooted at p.294func xremoveall(p string) {295	if vflag > 2 {296		errprintf("rm -r %s\n", p)297	}298	os.RemoveAll(p)299}300 301// xreaddir replaces dst with a list of the names of the files and subdirectories in dir.302// The names are relative to dir; they are not full paths.303func 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 names314}315 316// xworkdir creates a new temporary directory to hold object files317// and returns the name of that directory.318func xworkdir() string {319	name, err := os.MkdirTemp(os.Getenv("GOTMPDIR"), "go-tool-dist-")320	if err != nil {321		fatalf("%v", err)322	}323	return name324}325 326// fatalf prints an error message to standard error and exits.327func fatalf(format string, args ...any) {328	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))329 330	dieOnce.Do(func() { close(dying) })331 332	// Wait for background goroutines to finish,333	// so that exit handler that removes the work directory334	// is not fighting with active writes or open files.335	bghelpers.Wait()336 337	xexit(2)338}339 340var atexits []func()341 342// xexit exits the process with return code n.343func xexit(n int) {344	for i := len(atexits) - 1; i >= 0; i-- {345		atexits[i]()346	}347	os.Exit(n)348}349 350// xatexit schedules the exit-handler f to be run when the program exits.351func xatexit(f func()) {352	atexits = append(atexits, f)353}354 355// xprintf prints a message to standard output.356func xprintf(format string, args ...any) {357	fmt.Printf(format, args...)358}359 360// errprintf prints a message to standard output.361func errprintf(format string, args ...any) {362	fmt.Fprintf(os.Stderr, format, args...)363}364 365func xgetgoarm() string {366	// If we're building on an actual arm system, and not building367	// a cross-compiling toolchain, try to exec ourselves368	// to detect whether VFP is supported and set the default GOARM.369	// We've always assumed Android is ARMv7.370	if gohostarch == "arm" && goarch == "arm" && goos == gohostos && goos != "android" {371		// Try to exec ourselves in a mode to detect VFP support.372		// Seeing how far it gets determines which instructions failed.373		// The test is OS-agnostic.374		out := run("", 0, os.Args[0], "-check-goarm")375		v1ok := strings.Contains(out, "VFPv1 OK.")376		v3ok := strings.Contains(out, "VFPv3 OK.")377		if v1ok && v3ok {378			return "7"379		}380		if v1ok {381			return "6"382		}383		return "5"384	}385 386	// Otherwise, in the absence of local information, assume GOARM=7.387	//388	// We used to assume GOARM=5 in certain contexts but not others,389	// which produced inconsistent results. For example if you cross-compiled390	// for linux/arm from a windows/amd64 machine, you got GOARM=7 binaries,391	// but if you cross-compiled for linux/arm from a linux/amd64 machine,392	// you got GOARM=5 binaries. Now the default is independent of the393	// host operating system, for better reproducibility of builds.394	return "7"395}396 397// elfIsLittleEndian detects if the ELF file is little endian.398func elfIsLittleEndian(fn string) bool {399	// read the ELF file header to determine the endianness without using the400	// debug/elf package.401	file, err := os.Open(fn)402	if err != nil {403		fatalf("failed to open file to determine endianness: %v", err)404	}405	defer file.Close()406	var hdr [16]byte407	if _, err := io.ReadFull(file, hdr[:]); err != nil {408		fatalf("failed to read ELF header to determine endianness: %v", err)409	}410	// hdr[5] is EI_DATA byte, 1 is ELFDATA2LSB and 2 is ELFDATA2MSB411	switch hdr[5] {412	default:413		fatalf("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5])414	case 1:415		return true416	case 2:417		return false418	}419	panic("unreachable")420}421 422// count is a flag.Value that is like a flag.Bool and a flag.Int.423// If used as -name, it increments the count, but -name=x sets the count.424// Used for verbose flag -v.425type count int426 427func (c *count) String() string {428	return fmt.Sprint(int(*c))429}430 431func (c *count) Set(s string) error {432	switch s {433	case "true":434		*c++435	case "false":436		*c = 0437	default:438		n, err := strconv.Atoi(s)439		if err != nil {440			return fmt.Errorf("invalid count %q", s)441		}442		*c = count(n)443	}444	return nil445}446 447func (c *count) IsBoolFlag() bool {448	return true449}450 451func xflagparse(maxargs int) {452	flag.Var((*count)(&vflag), "v", "verbosity")453	flag.Parse()454	if maxargs >= 0 && flag.NArg() > maxargs {455		flag.Usage()456	}457}458 
codekingpro/portable-devtools · Team Ai