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 | |
9 | package packages |
10 | |
11 | import ( |
12 | "bytes" |
13 | "encoding/json" |
14 | "fmt" |
15 | exec "golang.org/x/sys/execabs" |
16 | "os" |
17 | "strings" |
18 | ) |
19 | |
20 | // The Driver Protocol |
21 | // |
22 | // The driver, given the inputs to a call to Load, returns metadata about the packages specified. |
23 | // This allows for different build systems to support go/packages by telling go/packages how the |
24 | // packages' source is organized. |
25 | // The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in |
26 | // the path as gopackagesdriver. It's given the inputs to load in its argv. See the package |
27 | // documentation in doc.go for the full description of the patterns that need to be supported. |
28 | // A driver receives as a JSON-serialized driverRequest struct in standard input and will |
29 | // produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output. |
30 | |
31 | // driverRequest is used to provide the portion of Load's Config that is needed by a driver. |
32 | type driverRequest struct { |
33 | Mode LoadMode `json:"mode"` |
34 | // Env specifies the environment the underlying build system should be run in. |
35 | Env []string `json:"env"` |
36 | // BuildFlags are flags that should be passed to the underlying build system. |
37 | BuildFlags []string `json:"build_flags"` |
38 | // Tests specifies whether the patterns should also return test packages. |
39 | Tests bool `json:"tests"` |
40 | // Overlay maps file paths (relative to the driver's working directory) to the byte contents |
41 | // of overlay files. |
42 | Overlay map[string][]byte `json:"overlay"` |
43 | } |
44 | |
45 | // findExternalDriver returns the file path of a tool that supplies |
46 | // the build system package structure, or "" if not found." |
47 | // If GOPACKAGESDRIVER is set in the environment findExternalTool returns its |
48 | // value, otherwise it searches for a binary named gopackagesdriver on the PATH. |
49 | func findExternalDriver(cfg *Config) driver { |
50 | const toolPrefix = "GOPACKAGESDRIVER=" |
51 | tool := "" |
52 | for _, env := range cfg.Env { |
53 | if val := strings.TrimPrefix(env, toolPrefix); val != env { |
54 | tool = val |
55 | } |
56 | } |
57 | if tool != "" && tool == "off" { |
58 | return nil |
59 | } |
60 | if tool == "" { |
61 | var err error |
62 | tool, err = exec.LookPath("gopackagesdriver") |
63 | if err != nil { |
64 | return nil |
65 | } |
66 | } |
67 | return func(cfg *Config, words ...string) (*driverResponse, error) { |
68 | req, err := json.Marshal(driverRequest{ |
69 | Mode: cfg.Mode, |
70 | Env: cfg.Env, |
71 | BuildFlags: cfg.BuildFlags, |
72 | Tests: cfg.Tests, |
73 | Overlay: cfg.Overlay, |
74 | }) |
75 | if err != nil { |
76 | return nil, fmt.Errorf("failed to encode message to driver tool: %v", err) |
77 | } |
78 | |
79 | buf := new(bytes.Buffer) |
80 | stderr := new(bytes.Buffer) |
81 | cmd := exec.CommandContext(cfg.Context, tool, words...) |
82 | cmd.Dir = cfg.Dir |
83 | cmd.Env = cfg.Env |
84 | cmd.Stdin = bytes.NewReader(req) |
85 | cmd.Stdout = buf |
86 | cmd.Stderr = stderr |
87 | |
88 | if err := cmd.Run(); err != nil { |
89 | return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) |
90 | } |
91 | if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" { |
92 | fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr) |
93 | } |
94 | |
95 | var response driverResponse |
96 | if err := json.Unmarshal(buf.Bytes(), &response); err != nil { |
97 | return nil, err |
98 | } |
99 | return &response, nil |
100 | } |
101 | } |
102 |
Members