external.go (3472B)
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 }