| 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