external.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  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. // This file enables an external tool to intercept package requests.
  5. // If the tool is present then its results are used in preference to
  6. // the go list command.
  7. package packages
  8. import (
  9. "bytes"
  10. "encoding/json"
  11. "fmt"
  12. "os/exec"
  13. "strings"
  14. )
  15. // Driver
  16. type driverRequest struct {
  17. Command string `json:"command"`
  18. Mode LoadMode `json:"mode"`
  19. Env []string `json:"env"`
  20. BuildFlags []string `json:"build_flags"`
  21. Tests bool `json:"tests"`
  22. Overlay map[string][]byte `json:"overlay"`
  23. }
  24. // findExternalDriver returns the file path of a tool that supplies
  25. // the build system package structure, or "" if not found."
  26. // If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
  27. // value, otherwise it searches for a binary named gopackagesdriver on the PATH.
  28. func findExternalDriver(cfg *Config) driver {
  29. const toolPrefix = "GOPACKAGESDRIVER="
  30. tool := ""
  31. for _, env := range cfg.Env {
  32. if val := strings.TrimPrefix(env, toolPrefix); val != env {
  33. tool = val
  34. }
  35. }
  36. if tool != "" && tool == "off" {
  37. return nil
  38. }
  39. if tool == "" {
  40. var err error
  41. tool, err = exec.LookPath("gopackagesdriver")
  42. if err != nil {
  43. return nil
  44. }
  45. }
  46. return func(cfg *Config, words ...string) (*driverResponse, error) {
  47. req, err := json.Marshal(driverRequest{
  48. Mode: cfg.Mode,
  49. Env: cfg.Env,
  50. BuildFlags: cfg.BuildFlags,
  51. Tests: cfg.Tests,
  52. Overlay: cfg.Overlay,
  53. })
  54. if err != nil {
  55. return nil, fmt.Errorf("failed to encode message to driver tool: %v", err)
  56. }
  57. buf := new(bytes.Buffer)
  58. cmd := exec.CommandContext(cfg.Context, tool, words...)
  59. cmd.Dir = cfg.Dir
  60. cmd.Env = cfg.Env
  61. cmd.Stdin = bytes.NewReader(req)
  62. cmd.Stdout = buf
  63. cmd.Stderr = new(bytes.Buffer)
  64. if err := cmd.Run(); err != nil {
  65. return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
  66. }
  67. var response driverResponse
  68. if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
  69. return nil, err
  70. }
  71. return &response, nil
  72. }
  73. }