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 package packages 6 7 // This file defines the protocol that enables an external "driver" 8 // tool to supply package metadata in place of 'go list'. 9 10 import ( 11 "bytes" 12 "encoding/json" 13 "fmt" 14 "os" 15 "os/exec" 16 "strings" 17 ) 18 19 // DriverRequest defines the schema of a request for package metadata 20 // from an external driver program. The JSON-encoded DriverRequest 21 // message is provided to the driver program's standard input. The 22 // query patterns are provided as command-line arguments. 23 // 24 // See the package documentation for an overview. 25 type DriverRequest struct { 26 Mode LoadMode `json:"mode"` 27 28 // Env specifies the environment the underlying build system should be run in. 29 Env []string `json:"env"` 30 31 // BuildFlags are flags that should be passed to the underlying build system. 32 BuildFlags []string `json:"build_flags"` 33 34 // Tests specifies whether the patterns should also return test packages. 35 Tests bool `json:"tests"` 36 37 // Overlay maps file paths (relative to the driver's working directory) to the byte contents 38 // of overlay files. 39 Overlay map[string][]byte `json:"overlay"` 40 } 41 42 // DriverResponse defines the schema of a response from an external 43 // driver program, providing the results of a query for package 44 // metadata. The driver program must write a JSON-encoded 45 // DriverResponse message to its standard output. 46 // 47 // See the package documentation for an overview. 48 type DriverResponse struct { 49 // NotHandled is returned if the request can't be handled by the current 50 // driver. If an external driver returns a response with NotHandled, the 51 // rest of the DriverResponse is ignored, and go/packages will fallback 52 // to the next driver. If go/packages is extended in the future to support 53 // lists of multiple drivers, go/packages will fall back to the next driver. 54 NotHandled bool 55 56 // Compiler and Arch are the arguments pass of types.SizesFor 57 // to get a types.Sizes to use when type checking. 58 Compiler string 59 Arch string 60 61 // Roots is the set of package IDs that make up the root packages. 62 // We have to encode this separately because when we encode a single package 63 // we cannot know if it is one of the roots as that requires knowledge of the 64 // graph it is part of. 65 Roots []string `json:",omitempty"` 66 67 // Packages is the full set of packages in the graph. 68 // The packages are not connected into a graph. 69 // The Imports if populated will be stubs that only have their ID set. 70 // Imports will be connected and then type and syntax information added in a 71 // later pass (see refine). 72 Packages []*Package 73 74 // GoVersion is the minor version number used by the driver 75 // (e.g. the go command on the PATH) when selecting .go files. 76 // Zero means unknown. 77 GoVersion int 78 } 79 80 // driver is the type for functions that query the build system for the 81 // packages named by the patterns. 82 type driver func(cfg *Config, patterns ...string) (*DriverResponse, error) 83 84 // findExternalDriver returns the file path of a tool that supplies 85 // the build system package structure, or "" if not found." 86 // If GOPACKAGESDRIVER is set in the environment findExternalTool returns its 87 // value, otherwise it searches for a binary named gopackagesdriver on the PATH. 88 func findExternalDriver(cfg *Config) driver { 89 const toolPrefix = "GOPACKAGESDRIVER=" 90 tool := "" 91 for _, env := range cfg.Env { 92 if val := strings.TrimPrefix(env, toolPrefix); val != env { 93 tool = val 94 } 95 } 96 if tool != "" && tool == "off" { 97 return nil 98 } 99 if tool == "" { 100 var err error 101 tool, err = exec.LookPath("gopackagesdriver") 102 if err != nil { 103 return nil 104 } 105 } 106 return func(cfg *Config, words ...string) (*DriverResponse, error) { 107 req, err := json.Marshal(DriverRequest{ 108 Mode: cfg.Mode, 109 Env: cfg.Env, 110 BuildFlags: cfg.BuildFlags, 111 Tests: cfg.Tests, 112 Overlay: cfg.Overlay, 113 }) 114 if err != nil { 115 return nil, fmt.Errorf("failed to encode message to driver tool: %v", err) 116 } 117 118 buf := new(bytes.Buffer) 119 stderr := new(bytes.Buffer) 120 cmd := exec.CommandContext(cfg.Context, tool, words...) 121 cmd.Dir = cfg.Dir 122 cmd.Env = cfg.Env 123 cmd.Stdin = bytes.NewReader(req) 124 cmd.Stdout = buf 125 cmd.Stderr = stderr 126 127 if err := cmd.Run(); err != nil { 128 return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) 129 } 130 if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" { 131 fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr) 132 } 133 134 var response DriverResponse 135 if err := json.Unmarshal(buf.Bytes(), &response); err != nil { 136 return nil, err 137 } 138 return &response, nil 139 } 140 } 141