github.com/yanyiwu/go@v0.0.0-20150106053140-03d6637dbb7f/src/cmd/go/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 main
     6  
     7  import (
     8  	"fmt"
     9  	"go/build"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"runtime"
    14  	"sort"
    15  	"strings"
    16  )
    17  
    18  var cmdTool = &Command{
    19  	Run:       runTool,
    20  	UsageLine: "tool [-n] command [args...]",
    21  	Short:     "run specified go tool",
    22  	Long: `
    23  Tool runs the go tool command identified by the arguments.
    24  With no arguments it prints the list of known tools.
    25  
    26  The -n flag causes tool to print the command that would be
    27  executed but not execute it.
    28  
    29  For more about each tool command, see 'go tool command -h'.
    30  `,
    31  }
    32  
    33  var (
    34  	toolGOOS      = runtime.GOOS
    35  	toolGOARCH    = runtime.GOARCH
    36  	toolIsWindows = toolGOOS == "windows"
    37  	toolDir       = build.ToolDir
    38  
    39  	toolN bool
    40  )
    41  
    42  func init() {
    43  	cmdTool.Flag.BoolVar(&toolN, "n", false, "")
    44  }
    45  
    46  const toolWindowsExtension = ".exe"
    47  
    48  func tool(toolName string) string {
    49  	toolPath := filepath.Join(toolDir, toolName)
    50  	if toolIsWindows {
    51  		toolPath += toolWindowsExtension
    52  	}
    53  	// Give a nice message if there is no tool with that name.
    54  	if _, err := os.Stat(toolPath); err != nil {
    55  		if isInGoToolsRepo(toolName) {
    56  			fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo get golang.org/x/tools/cmd/%s\n", toolName, toolName)
    57  		} else {
    58  			fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
    59  		}
    60  		setExitStatus(3)
    61  		exit()
    62  	}
    63  	return toolPath
    64  }
    65  
    66  func isInGoToolsRepo(toolName string) bool {
    67  	switch toolName {
    68  	case "cover", "vet":
    69  		return true
    70  	}
    71  	return false
    72  }
    73  
    74  func runTool(cmd *Command, args []string) {
    75  	if len(args) == 0 {
    76  		listTools()
    77  		return
    78  	}
    79  	toolName := args[0]
    80  	// The tool name must be lower-case letters, numbers or underscores.
    81  	for _, c := range toolName {
    82  		switch {
    83  		case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_':
    84  		default:
    85  			fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName)
    86  			setExitStatus(2)
    87  			return
    88  		}
    89  	}
    90  	toolPath := tool(toolName)
    91  	if toolPath == "" {
    92  		return
    93  	}
    94  	if toolN {
    95  		fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " "))
    96  		return
    97  	}
    98  	toolCmd := &exec.Cmd{
    99  		Path:   toolPath,
   100  		Args:   args,
   101  		Stdin:  os.Stdin,
   102  		Stdout: os.Stdout,
   103  		Stderr: os.Stderr,
   104  	}
   105  	err := toolCmd.Run()
   106  	if err != nil {
   107  		// Only print about the exit status if the command
   108  		// didn't even run (not an ExitError) or it didn't exit cleanly
   109  		// or we're printing command lines too (-x mode).
   110  		// Assume if command exited cleanly (even with non-zero status)
   111  		// it printed any messages it wanted to print.
   112  		if e, ok := err.(*exec.ExitError); !ok || !e.Exited() || buildX {
   113  			fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
   114  		}
   115  		setExitStatus(1)
   116  		return
   117  	}
   118  }
   119  
   120  // listTools prints a list of the available tools in the tools directory.
   121  func listTools() {
   122  	f, err := os.Open(toolDir)
   123  	if err != nil {
   124  		fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err)
   125  		setExitStatus(2)
   126  		return
   127  	}
   128  	defer f.Close()
   129  	names, err := f.Readdirnames(-1)
   130  	if err != nil {
   131  		fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err)
   132  		setExitStatus(2)
   133  		return
   134  	}
   135  
   136  	sort.Strings(names)
   137  	for _, name := range names {
   138  		// Unify presentation by going to lower case.
   139  		name = strings.ToLower(name)
   140  		// If it's windows, don't show the .exe suffix.
   141  		if toolIsWindows && strings.HasSuffix(name, toolWindowsExtension) {
   142  			name = name[:len(name)-len(toolWindowsExtension)]
   143  		}
   144  		fmt.Println(name)
   145  	}
   146  }