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