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// Driver
20type driverRequest struct {
21 Command string `json:"command"`
22 Mode LoadMode `json:"mode"`
23 Env []string `json:"env"`
24 BuildFlags []string `json:"build_flags"`
25 Tests bool `json:"tests"`
26 Overlay map[string][]byte `json:"overlay"`
27}
28
29// findExternalDriver returns the file path of a tool that supplies
30// the build system package structure, or "" if not found."
31// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
32// value, otherwise it searches for a binary named gopackagesdriver on the PATH.
33func findExternalDriver(cfg *Config) driver {
34 const toolPrefix = "GOPACKAGESDRIVER="
35 tool := ""
36 for _, env := range cfg.Env {
37 if val := strings.TrimPrefix(env, toolPrefix); val != env {
38 tool = val
39 }
40 }
41 if tool != "" && tool == "off" {
42 return nil
43 }
44 if tool == "" {
45 var err error
46 tool, err = exec.LookPath("gopackagesdriver")
47 if err != nil {
48 return nil
49 }
50 }
51 return func(cfg *Config, words ...string) (*driverResponse, error) {
52 req, err := json.Marshal(driverRequest{
53 Mode: cfg.Mode,
54 Env: cfg.Env,
55 BuildFlags: cfg.BuildFlags,
56 Tests: cfg.Tests,
57 Overlay: cfg.Overlay,
58 })
59 if err != nil {
60 return nil, fmt.Errorf("failed to encode message to driver tool: %v", err)
61 }
62
63 buf := new(bytes.Buffer)
64 cmd := exec.CommandContext(cfg.Context, tool, words...)
65 cmd.Dir = cfg.Dir
66 cmd.Env = cfg.Env
67 cmd.Stdin = bytes.NewReader(req)
68 cmd.Stdout = buf
69 cmd.Stderr = new(bytes.Buffer)
70 if err := cmd.Run(); err != nil {
71 return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
72 }
73 var response driverResponse
74 if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
75 return nil, err
76 }
77 return &response, nil
78 }
79}