golang.org/x/tools@v0.21.0/go/packages/external.go (about)

     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  }