102 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			102 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // Copyright 2018 The Go Authors. All rights reserved. | ||
|  | // Use of this source code is governed by a BSD-style | ||
|  | // license that can be found in the LICENSE file. | ||
|  | 
 | ||
|  | // This file enables an external tool to intercept package requests. | ||
|  | // If the tool is present then its results are used in preference to | ||
|  | // the go list command. | ||
|  | 
 | ||
|  | package packages | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"bytes" | ||
|  | 	"encoding/json" | ||
|  | 	"fmt" | ||
|  | 	"os" | ||
|  | 	"os/exec" | ||
|  | 	"strings" | ||
|  | ) | ||
|  | 
 | ||
|  | // The Driver Protocol | ||
|  | // | ||
|  | // The driver, given the inputs to a call to Load, returns metadata about the packages specified. | ||
|  | // This allows for different build systems to support go/packages by telling go/packages how the | ||
|  | // packages' source is organized. | ||
|  | // The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in | ||
|  | // the path as gopackagesdriver. It's given the inputs to load in its argv. See the package | ||
|  | // documentation in doc.go for the full description of the patterns that need to be supported. | ||
|  | // A driver receives as a JSON-serialized driverRequest struct in standard input and will | ||
|  | // produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output. | ||
|  | 
 | ||
|  | // driverRequest is used to provide the portion of Load's Config that is needed by a driver. | ||
|  | type driverRequest struct { | ||
|  | 	Mode LoadMode `json:"mode"` | ||
|  | 	// Env specifies the environment the underlying build system should be run in. | ||
|  | 	Env []string `json:"env"` | ||
|  | 	// BuildFlags are flags that should be passed to the underlying build system. | ||
|  | 	BuildFlags []string `json:"build_flags"` | ||
|  | 	// Tests specifies whether the patterns should also return test packages. | ||
|  | 	Tests bool `json:"tests"` | ||
|  | 	// Overlay maps file paths (relative to the driver's working directory) to the byte contents | ||
|  | 	// of overlay files. | ||
|  | 	Overlay map[string][]byte `json:"overlay"` | ||
|  | } | ||
|  | 
 | ||
|  | // findExternalDriver returns the file path of a tool that supplies | ||
|  | // the build system package structure, or "" if not found." | ||
|  | // If GOPACKAGESDRIVER is set in the environment findExternalTool returns its | ||
|  | // value, otherwise it searches for a binary named gopackagesdriver on the PATH. | ||
|  | func findExternalDriver(cfg *Config) driver { | ||
|  | 	const toolPrefix = "GOPACKAGESDRIVER=" | ||
|  | 	tool := "" | ||
|  | 	for _, env := range cfg.Env { | ||
|  | 		if val := strings.TrimPrefix(env, toolPrefix); val != env { | ||
|  | 			tool = val | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if tool != "" && tool == "off" { | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 	if tool == "" { | ||
|  | 		var err error | ||
|  | 		tool, err = exec.LookPath("gopackagesdriver") | ||
|  | 		if err != nil { | ||
|  | 			return nil | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return func(cfg *Config, words ...string) (*driverResponse, error) { | ||
|  | 		req, err := json.Marshal(driverRequest{ | ||
|  | 			Mode:       cfg.Mode, | ||
|  | 			Env:        cfg.Env, | ||
|  | 			BuildFlags: cfg.BuildFlags, | ||
|  | 			Tests:      cfg.Tests, | ||
|  | 			Overlay:    cfg.Overlay, | ||
|  | 		}) | ||
|  | 		if err != nil { | ||
|  | 			return nil, fmt.Errorf("failed to encode message to driver tool: %v", err) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		buf := new(bytes.Buffer) | ||
|  | 		stderr := new(bytes.Buffer) | ||
|  | 		cmd := exec.CommandContext(cfg.Context, tool, words...) | ||
|  | 		cmd.Dir = cfg.Dir | ||
|  | 		cmd.Env = cfg.Env | ||
|  | 		cmd.Stdin = bytes.NewReader(req) | ||
|  | 		cmd.Stdout = buf | ||
|  | 		cmd.Stderr = stderr | ||
|  | 
 | ||
|  | 		if err := cmd.Run(); err != nil { | ||
|  | 			return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) | ||
|  | 		} | ||
|  | 		if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" { | ||
|  | 			fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd, words...), stderr) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		var response driverResponse | ||
|  | 		if err := json.Unmarshal(buf.Bytes(), &response); err != nil { | ||
|  | 			return nil, err | ||
|  | 		} | ||
|  | 		return &response, nil | ||
|  | 	} | ||
|  | } |