1// Copyright 2018 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// This file enables an external tool to intercept package requests.
6// If the tool is present then its results are used in preference to
7// the go list command.
8
9package packages
10
11import (
12 "bytes"
13 "encoding/json"
14 "fmt"
15 "os/exec"
16 "strings"
17)
18
19// findExternalTool returns the file path of a tool that supplies supplies
20// the build system package structure, or "" if not found."
21// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
22// value, otherwise it searches for a binary named gopackagesdriver on the PATH.
23func findExternalDriver(cfg *Config) driver {
24 const toolPrefix = "GOPACKAGESDRIVER="
25 tool := ""
26 for _, env := range cfg.Env {
27 if val := strings.TrimPrefix(env, toolPrefix); val != env {
28 tool = val
29 }
30 }
31 if tool != "" && tool == "off" {
32 return nil
33 }
34 if tool == "" {
35 var err error
36 tool, err = exec.LookPath("gopackagesdriver")
37 if err != nil {
38 return nil
39 }
40 }
41 return func(cfg *Config, words ...string) (*driverResponse, error) {
42 buf := new(bytes.Buffer)
43 fullargs := []string{
44 "list",
45 fmt.Sprintf("-test=%t", cfg.Tests),
46 fmt.Sprintf("-export=%t", usesExportData(cfg)),
47 fmt.Sprintf("-deps=%t", cfg.Mode >= LoadImports),
48 }
49 for _, f := range cfg.Flags {
50 fullargs = append(fullargs, fmt.Sprintf("-flags=%v", f))
51 }
52 fullargs = append(fullargs, "--")
53 fullargs = append(fullargs, words...)
54 cmd := exec.CommandContext(cfg.Context, tool, fullargs...)
55 cmd.Env = cfg.Env
56 cmd.Dir = cfg.Dir
57 cmd.Stdout = buf
58 cmd.Stderr = new(bytes.Buffer)
59 if err := cmd.Run(); err != nil {
60 return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
61 }
62 var response driverResponse
63 if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
64 return nil, err
65 }
66 return &response, nil
67 }
68}