github.com/MangoDowner/go-gm@v0.0.0-20180818020936-8baa2bd4408c/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: "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 tool command -h'.
    31  `,
    32  }
    33  
    34  var toolN bool
    35  
    36  func init() {
    37  	CmdTool.Flag.BoolVar(&toolN, "n", false, "")
    38  }
    39  
    40  func runTool(cmd *base.Command, args []string) {
    41  	if len(args) == 0 {
    42  		listTools()
    43  		return
    44  	}
    45  	toolName := args[0]
    46  	// The tool name must be lower-case letters, numbers or underscores.
    47  	for _, c := range toolName {
    48  		switch {
    49  		case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_':
    50  		default:
    51  			fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName)
    52  			base.SetExitStatus(2)
    53  			return
    54  		}
    55  	}
    56  	toolPath := base.Tool(toolName)
    57  	if toolPath == "" {
    58  		return
    59  	}
    60  	if toolN {
    61  		cmd := toolPath
    62  		if len(args) > 1 {
    63  			cmd += " " + strings.Join(args[1:], " ")
    64  		}
    65  		fmt.Printf("%s\n", cmd)
    66  		return
    67  	}
    68  	args[0] = toolPath // in case the tool wants to re-exec itself, e.g. cmd/dist
    69  	toolCmd := &exec.Cmd{
    70  		Path:   toolPath,
    71  		Args:   args,
    72  		Stdin:  os.Stdin,
    73  		Stdout: os.Stdout,
    74  		Stderr: os.Stderr,
    75  		// Set $GOROOT, mainly for go tool dist.
    76  		Env: base.MergeEnvLists([]string{"GOROOT=" + cfg.GOROOT}, os.Environ()),
    77  	}
    78  	err := toolCmd.Run()
    79  	if err != nil {
    80  		// Only print about the exit status if the command
    81  		// didn't even run (not an ExitError) or it didn't exit cleanly
    82  		// or we're printing command lines too (-x mode).
    83  		// Assume if command exited cleanly (even with non-zero status)
    84  		// it printed any messages it wanted to print.
    85  		if e, ok := err.(*exec.ExitError); !ok || !e.Exited() || cfg.BuildX {
    86  			fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
    87  		}
    88  		base.SetExitStatus(1)
    89  		return
    90  	}
    91  }
    92  
    93  // listTools prints a list of the available tools in the tools directory.
    94  func listTools() {
    95  	f, err := os.Open(base.ToolDir)
    96  	if err != nil {
    97  		fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err)
    98  		base.SetExitStatus(2)
    99  		return
   100  	}
   101  	defer f.Close()
   102  	names, err := f.Readdirnames(-1)
   103  	if err != nil {
   104  		fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err)
   105  		base.SetExitStatus(2)
   106  		return
   107  	}
   108  
   109  	sort.Strings(names)
   110  	for _, name := range names {
   111  		// Unify presentation by going to lower case.
   112  		name = strings.ToLower(name)
   113  		// If it's windows, don't show the .exe suffix.
   114  		if base.ToolIsWindows && strings.HasSuffix(name, base.ToolWindowsExtension) {
   115  			name = name[:len(name)-len(base.ToolWindowsExtension)]
   116  		}
   117  		fmt.Println(name)
   118  	}
   119  }