github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/cmd/go/internal/tool/tool.go (about)

     1  // Copyright 2011 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 tool implements the ``go tool'' command.
     6  package tool
     7  
     8  import (
     9  	"fmt"
    10  	"os"
    11  	"os/exec"
    12  	"sort"
    13  	"strings"
    14  
    15  	"cmd/go/internal/base"
    16  	"cmd/go/internal/cfg"
    17  )
    18  
    19  var CmdTool = &base.Command{
    20  	Run:       runTool,
    21  	UsageLine: "go tool [-n] command [args...]",
    22  	Short:     "run specified go tool",
    23  	Long: `
    24  Tool runs the go tool command identified by the arguments.
    25  With no arguments it prints the list of known tools.
    26  
    27  The -n flag causes tool to print the command that would be
    28  executed but not execute it.
    29  
    30  For more about each tool command, see 'go doc cmd/<command>'.
    31  `,
    32  }
    33  
    34  var toolN bool
    35  
    36  // Return whether tool can be expected in the gccgo tool directory.
    37  // Other binaries could be in the same directory so don't
    38  // show those with the 'go tool' command.
    39  func isGccgoTool(tool string) bool {
    40  	switch tool {
    41  	case "cgo", "fix", "cover", "godoc", "vet":
    42  		return true
    43  	}
    44  	return false
    45  }
    46  
    47  func init() {
    48  	CmdTool.Flag.BoolVar(&toolN, "n", false, "")
    49  }
    50  
    51  func runTool(cmd *base.Command, args []string) {
    52  	if len(args) == 0 {
    53  		listTools()
    54  		return
    55  	}
    56  	toolName := args[0]
    57  	// The tool name must be lower-case letters, numbers or underscores.
    58  	for _, c := range toolName {
    59  		switch {
    60  		case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_':
    61  		default:
    62  			fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName)
    63  			base.SetExitStatus(2)
    64  			return
    65  		}
    66  	}
    67  	toolPath := base.Tool(toolName)
    68  	if toolPath == "" {
    69  		return
    70  	}
    71  	if toolN {
    72  		cmd := toolPath
    73  		if len(args) > 1 {
    74  			cmd += " " + strings.Join(args[1:], " ")
    75  		}
    76  		fmt.Printf("%s\n", cmd)
    77  		return
    78  	}
    79  	args[0] = toolPath // in case the tool wants to re-exec itself, e.g. cmd/dist
    80  	toolCmd := &exec.Cmd{
    81  		Path:   toolPath,
    82  		Args:   args,
    83  		Stdin:  os.Stdin,
    84  		Stdout: os.Stdout,
    85  		Stderr: os.Stderr,
    86  		// Set $GOROOT, mainly for go tool dist.
    87  		Env: base.MergeEnvLists([]string{"GOROOT=" + cfg.GOROOT}, os.Environ()),
    88  	}
    89  	err := toolCmd.Run()
    90  	if err != nil {
    91  		// Only print about the exit status if the command
    92  		// didn't even run (not an ExitError) or it didn't exit cleanly
    93  		// or we're printing command lines too (-x mode).
    94  		// Assume if command exited cleanly (even with non-zero status)
    95  		// it printed any messages it wanted to print.
    96  		if e, ok := err.(*exec.ExitError); !ok || !e.Exited() || cfg.BuildX {
    97  			fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
    98  		}
    99  		base.SetExitStatus(1)
   100  		return
   101  	}
   102  }
   103  
   104  // listTools prints a list of the available tools in the tools directory.
   105  func listTools() {
   106  	f, err := os.Open(base.ToolDir)
   107  	if err != nil {
   108  		fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err)
   109  		base.SetExitStatus(2)
   110  		return
   111  	}
   112  	defer f.Close()
   113  	names, err := f.Readdirnames(-1)
   114  	if err != nil {
   115  		fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err)
   116  		base.SetExitStatus(2)
   117  		return
   118  	}
   119  
   120  	sort.Strings(names)
   121  	for _, name := range names {
   122  		// Unify presentation by going to lower case.
   123  		name = strings.ToLower(name)
   124  		// If it's windows, don't show the .exe suffix.
   125  		if base.ToolIsWindows && strings.HasSuffix(name, base.ToolWindowsExtension) {
   126  			name = name[:len(name)-len(base.ToolWindowsExtension)]
   127  		}
   128  		// The tool directory used by gccgo will have other binaries
   129  		// in addition to go tools. Only display go tools here.
   130  		if cfg.BuildToolchainName == "gccgo" && !isGccgoTool(name) {
   131  			continue
   132  		}
   133  		fmt.Println(name)
   134  	}
   135  }