github.com/tiagovtristao/plz@v13.4.0+incompatible/src/tool/tool.go (about)

     1  // Package tool implements running Please's sub-tools (via "plz tool jarcat" etc).
     2  //
     3  // N.B. This is not how they are invoked during the build; that runs them directly.
     4  //      This is only a convenience thing at the command line.
     5  package tool
     6  
     7  import (
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"syscall"
    12  
    13  	"github.com/jessevdk/go-flags"
    14  	"gopkg.in/op/go-logging.v1"
    15  
    16  	"github.com/thought-machine/please/src/core"
    17  	"sort"
    18  )
    19  
    20  var log = logging.MustGetLogger("tool")
    21  
    22  // A Tool is one of Please's tools; this only exists for facilitating tab-completion for flags.
    23  type Tool string
    24  
    25  // Complete suggests completions for a partial tool name.
    26  func (tool Tool) Complete(match string) []flags.Completion {
    27  	ret := []flags.Completion{}
    28  	for k := range matchingTools(core.DefaultConfiguration(), match) {
    29  		ret = append(ret, flags.Completion{Item: k})
    30  	}
    31  	return ret
    32  }
    33  
    34  // Run runs one of the sub-tools.
    35  func Run(config *core.Configuration, tool Tool, args []string) {
    36  	tools := matchingTools(config, string(tool))
    37  	if len(tools) != 1 {
    38  		log.Fatalf("Unknown tool: %s. Must be one of [%s]", tool, strings.Join(allToolNames(config, ""), ", "))
    39  	}
    40  	target := core.ExpandHomePath(tools[allToolNames(config, string(tool))[0]])
    41  	if !core.LooksLikeABuildLabel(target) {
    42  		if !filepath.IsAbs(target) {
    43  			t, err := core.LookBuildPath(target, config)
    44  			if err != nil {
    45  				log.Fatalf("%s", err)
    46  			}
    47  			target = t
    48  		}
    49  		// Hopefully we have an absolute path now, so let's run it.
    50  		err := syscall.Exec(target, append([]string{target}, args...), os.Environ())
    51  		log.Fatalf("Failed to exec %s: %s", target, err) // Always a failure, exec never returns.
    52  	}
    53  	// The tool is allowed to be an in-repo target. In that case it's essentially equivalent to "plz run".
    54  	// We have to re-exec ourselves in such a case since we don't know enough about it to run it now.
    55  	plz, _ := os.Executable()
    56  	args = append([]string{os.Args[0], "run", target, "--"}, args...)
    57  	err := syscall.Exec(plz, args, os.Environ())
    58  	log.Fatalf("Failed to exec %s run %s: %s", plz, target, err) // Always a failure, exec never returns.
    59  }
    60  
    61  // matchingTools returns a set of matching tools for a string prefix.
    62  func matchingTools(config *core.Configuration, prefix string) map[string]string {
    63  	knownTools := map[string]string{
    64  		"gotest":      config.Go.TestTool,
    65  		"jarcat":      config.Java.JarCatTool,
    66  		"javacworker": config.Java.JavacWorker,
    67  		"junitrunner": config.Java.JUnitRunner,
    68  		"langserver":  "build_langserver",
    69  		"lps":         "build_langserver",
    70  		"maven":       config.Java.PleaseMavenTool,
    71  		"pex":         config.Python.PexTool,
    72  		"sandbox":     "please_sandbox",
    73  	}
    74  	ret := map[string]string{}
    75  	for k, v := range knownTools {
    76  		if strings.HasPrefix(k, prefix) {
    77  			ret[k] = v
    78  		}
    79  	}
    80  	return ret
    81  }
    82  
    83  // allToolNames returns the names of all available tools.
    84  func allToolNames(config *core.Configuration, prefix string) []string {
    85  	ret := []string{}
    86  	for k := range matchingTools(config, prefix) {
    87  		ret = append(ret, k)
    88  	}
    89  	sort.Strings(ret)
    90  	return ret
    91  }