go/packages: change driver communication mechanism

Pass in parameters to the driver in stdin rather than through argv.
This allows us to more easily route parameters to the driver and add
more parameters without breaking users. The driver doesn't need to
have the same interface as go list, because it's unlikely the driver interface
will converge with the go list interface.

We still pass in the query "words" to the driver in the argv, because
that's as good a place as any, but there's no "list" command.

Question: should we add a "command" option to the driverRequest struct?

Change-Id: Ifdbb3f84b6bfd04259f5ab63e756341d7f69de9b
Reviewed-on: https://go-review.googlesource.com/c/150337
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/go/packages/external.go b/go/packages/external.go
index 53cc080..9d369a3 100644
--- a/go/packages/external.go
+++ b/go/packages/external.go
@@ -16,6 +16,17 @@
 	"strings"
 )
 
+// Driver
+type driverRequest struct {
+	// TODO(matloob): Add a "command" option, so the "list" argument
+	// to the command is instead supplied in the driverRequest?
+	Mode       LoadMode          `json:"mode"`
+	Env        []string          `json:"env"`
+	BuildFlags []string          `json:"build_flags"`
+	Tests      bool              `json:"tests"`
+	Overlay    map[string][]byte `json:"overlay"`
+}
+
 // findExternalTool 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
@@ -39,21 +50,22 @@
 		}
 	}
 	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)
-		fullargs := []string{
-			"list",
-			fmt.Sprintf("-test=%t", cfg.Tests),
-			fmt.Sprintf("-export=%t", usesExportData(cfg)),
-			fmt.Sprintf("-deps=%t", cfg.Mode >= LoadImports),
-		}
-		for _, f := range cfg.BuildFlags {
-			fullargs = append(fullargs, fmt.Sprintf("-buildflag=%v", f))
-		}
-		fullargs = append(fullargs, "--")
-		fullargs = append(fullargs, words...)
-		cmd := exec.CommandContext(cfg.Context, tool, fullargs...)
-		cmd.Env = cfg.Env
+		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 = new(bytes.Buffer)
 		if err := cmd.Run(); err != nil {