go/packages: publish Driver{Request,Response}
and update the documentation.
Fixes golang/go#64608
Change-Id: Icc65d1fb838d9fcb8e56e5f5bfcc175a83a4b706
Reviewed-on: https://go-review.googlesource.com/c/tools/+/557056
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
Auto-Submit: Alan Donovan <adonovan@google.com>
diff --git a/go/packages/doc.go b/go/packages/doc.go
index b2a0b7c..a8d7b06 100644
--- a/go/packages/doc.go
+++ b/go/packages/doc.go
@@ -15,22 +15,10 @@
The default build tool is the go command.
Its supported patterns are described at
https://pkg.go.dev/cmd/go#hdr-Package_lists_and_patterns.
+Other build systems may be supported by providing a "driver";
+see [The driver protocol].
-Load may be used in Go projects that use alternative build systems, by
-installing an appropriate "driver" program for the build system and
-specifying its location in the GOPACKAGESDRIVER environment variable.
-For example,
-https://github.com/bazelbuild/rules_go/wiki/Editor-and-tool-integration
-explains how to use the driver for Bazel.
-The driver program is responsible for interpreting patterns in its
-preferred notation and reporting information about the packages that
-they identify.
-(See driverRequest and driverResponse types for the JSON
-schema used by the protocol.
-Though the protocol is supported, these types are currently unexported;
-see #64608 for a proposal to publish them.)
-
-Regardless of driver, all patterns with the prefix "query=", where query is a
+All patterns with the prefix "query=", where query is a
non-empty string of letters from [a-z], are reserved and may be
interpreted as query operators.
@@ -86,7 +74,29 @@
Most tools should pass their command-line arguments (after any flags)
uninterpreted to [Load], so that it can interpret them
according to the conventions of the underlying build system.
+
See the Example function for typical usage.
+
+# The driver protocol
+
+[Load] may be used to load Go packages even in Go projects that use
+alternative build systems, by installing an appropriate "driver"
+program for the build system and specifying its location in the
+GOPACKAGESDRIVER environment variable.
+For example,
+https://github.com/bazelbuild/rules_go/wiki/Editor-and-tool-integration
+explains how to use the driver for Bazel.
+
+The driver program is responsible for interpreting patterns in its
+preferred notation and reporting information about the packages that
+those patterns identify. Drivers must also support the special "file="
+and "pattern=" patterns described above.
+
+The patterns are provided as positional command-line arguments. A
+JSON-encoded [DriverRequest] message providing additional information
+is written to the driver's standard input. The driver must write a
+JSON-encoded [DriverResponse] message to its standard output. (This
+message differs from the JSON schema produced by 'go list'.)
*/
package packages // import "golang.org/x/tools/go/packages"
diff --git a/go/packages/external.go b/go/packages/external.go
index 7db1d12..4335c1e 100644
--- a/go/packages/external.go
+++ b/go/packages/external.go
@@ -2,12 +2,11 @@
// 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
+// This file defines the protocol that enables an external "driver"
+// tool to supply package metadata in place of 'go list'.
+
import (
"bytes"
"encoding/json"
@@ -17,31 +16,71 @@
"strings"
)
-// The Driver Protocol
+// DriverRequest defines the schema of a request for package metadata
+// from an external driver program. The JSON-encoded DriverRequest
+// message is provided to the driver program's standard input. The
+// query patterns are provided as command-line arguments.
//
-// 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 {
+// See the package documentation for an overview.
+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"`
}
+// DriverResponse defines the schema of a response from an external
+// driver program, providing the results of a query for package
+// metadata. The driver program must write a JSON-encoded
+// DriverResponse message to its standard output.
+//
+// See the package documentation for an overview.
+type DriverResponse struct {
+ // NotHandled is returned if the request can't be handled by the current
+ // driver. If an external driver returns a response with NotHandled, the
+ // rest of the DriverResponse is ignored, and go/packages will fallback
+ // to the next driver. If go/packages is extended in the future to support
+ // lists of multiple drivers, go/packages will fall back to the next driver.
+ NotHandled bool
+
+ // Compiler and Arch are the arguments pass of types.SizesFor
+ // to get a types.Sizes to use when type checking.
+ Compiler string
+ Arch string
+
+ // Roots is the set of package IDs that make up the root packages.
+ // We have to encode this separately because when we encode a single package
+ // we cannot know if it is one of the roots as that requires knowledge of the
+ // graph it is part of.
+ Roots []string `json:",omitempty"`
+
+ // Packages is the full set of packages in the graph.
+ // The packages are not connected into a graph.
+ // The Imports if populated will be stubs that only have their ID set.
+ // Imports will be connected and then type and syntax information added in a
+ // later pass (see refine).
+ Packages []*Package
+
+ // GoVersion is the minor version number used by the driver
+ // (e.g. the go command on the PATH) when selecting .go files.
+ // Zero means unknown.
+ GoVersion int
+}
+
+// driver is the type for functions that query the build system for the
+// packages named by the patterns.
+type driver func(cfg *Config, patterns ...string) (*DriverResponse, error)
+
// 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
@@ -64,8 +103,8 @@
return nil
}
}
- return func(cfg *Config, words ...string) (*driverResponse, error) {
- req, err := json.Marshal(driverRequest{
+ return func(cfg *Config, words ...string) (*DriverResponse, error) {
+ req, err := json.Marshal(DriverRequest{
Mode: cfg.Mode,
Env: cfg.Env,
BuildFlags: cfg.BuildFlags,
@@ -92,7 +131,7 @@
fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr)
}
- var response driverResponse
+ var response DriverResponse
if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
return nil, err
}
diff --git a/go/packages/golist.go b/go/packages/golist.go
index cd375fb..49c2d14 100644
--- a/go/packages/golist.go
+++ b/go/packages/golist.go
@@ -35,23 +35,23 @@
error
}
-// responseDeduper wraps a driverResponse, deduplicating its contents.
+// responseDeduper wraps a DriverResponse, deduplicating its contents.
type responseDeduper struct {
seenRoots map[string]bool
seenPackages map[string]*Package
- dr *driverResponse
+ dr *DriverResponse
}
func newDeduper() *responseDeduper {
return &responseDeduper{
- dr: &driverResponse{},
+ dr: &DriverResponse{},
seenRoots: map[string]bool{},
seenPackages: map[string]*Package{},
}
}
-// addAll fills in r with a driverResponse.
-func (r *responseDeduper) addAll(dr *driverResponse) {
+// addAll fills in r with a DriverResponse.
+func (r *responseDeduper) addAll(dr *DriverResponse) {
for _, pkg := range dr.Packages {
r.addPackage(pkg)
}
@@ -128,7 +128,7 @@
// goListDriver uses the go list command to interpret the patterns and produce
// the build system package structure.
// See driver for more details.
-func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
+func goListDriver(cfg *Config, patterns ...string) (*DriverResponse, error) {
// Make sure that any asynchronous go commands are killed when we return.
parentCtx := cfg.Context
if parentCtx == nil {
@@ -266,7 +266,7 @@
// adhocPackage attempts to load or construct an ad-hoc package for a given
// query, if the original call to the driver produced inadequate results.
-func (state *golistState) adhocPackage(pattern, query string) (*driverResponse, error) {
+func (state *golistState) adhocPackage(pattern, query string) (*DriverResponse, error) {
response, err := state.createDriverResponse(query)
if err != nil {
return nil, err
@@ -357,7 +357,7 @@
// createDriverResponse uses the "go list" command to expand the pattern
// words and return a response for the specified packages.
-func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) {
+func (state *golistState) createDriverResponse(words ...string) (*DriverResponse, error) {
// go list uses the following identifiers in ImportPath and Imports:
//
// "p" -- importable package or main (command)
@@ -384,7 +384,7 @@
pkgs := make(map[string]*Package)
additionalErrors := make(map[string][]Error)
// Decode the JSON and convert it to Package form.
- response := &driverResponse{
+ response := &DriverResponse{
GoVersion: goVersion,
}
for dec := json.NewDecoder(buf); dec.More(); {
diff --git a/go/packages/packages.go b/go/packages/packages.go
index 81e9e6a..fcdcc37 100644
--- a/go/packages/packages.go
+++ b/go/packages/packages.go
@@ -206,43 +206,6 @@
Overlay map[string][]byte
}
-// driver is the type for functions that query the build system for the
-// packages named by the patterns.
-type driver func(cfg *Config, patterns ...string) (*driverResponse, error)
-
-// driverResponse contains the results for a driver query.
-type driverResponse struct {
- // NotHandled is returned if the request can't be handled by the current
- // driver. If an external driver returns a response with NotHandled, the
- // rest of the driverResponse is ignored, and go/packages will fallback
- // to the next driver. If go/packages is extended in the future to support
- // lists of multiple drivers, go/packages will fall back to the next driver.
- NotHandled bool
-
- // Compiler and Arch are the arguments pass of types.SizesFor
- // to get a types.Sizes to use when type checking.
- Compiler string
- Arch string
-
- // Roots is the set of package IDs that make up the root packages.
- // We have to encode this separately because when we encode a single package
- // we cannot know if it is one of the roots as that requires knowledge of the
- // graph it is part of.
- Roots []string `json:",omitempty"`
-
- // Packages is the full set of packages in the graph.
- // The packages are not connected into a graph.
- // The Imports if populated will be stubs that only have their ID set.
- // Imports will be connected and then type and syntax information added in a
- // later pass (see refine).
- Packages []*Package
-
- // GoVersion is the minor version number used by the driver
- // (e.g. the go command on the PATH) when selecting .go files.
- // Zero means unknown.
- GoVersion int
-}
-
// Load loads and returns the Go packages named by the given patterns.
//
// Config specifies loading options;
@@ -291,7 +254,7 @@
// no external driver, or the driver returns a response with NotHandled set,
// defaultDriver will fall back to the go list driver.
// The boolean result indicates that an external driver handled the request.
-func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, bool, error) {
+func defaultDriver(cfg *Config, patterns ...string) (*DriverResponse, bool, error) {
if driver := findExternalDriver(cfg); driver != nil {
response, err := driver(cfg, patterns...)
if err != nil {
@@ -648,7 +611,7 @@
// refine connects the supplied packages into a graph and then adds type
// and syntax information as requested by the LoadMode.
-func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
+func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
roots := response.Roots
rootMap := make(map[string]int, len(roots))
for i, root := range roots {