external.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  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. exec "golang.org/x/sys/execabs"
  13. "os"
  14. "strings"
  15. )
  16. // The Driver Protocol
  17. //
  18. // The driver, given the inputs to a call to Load, returns metadata about the packages specified.
  19. // This allows for different build systems to support go/packages by telling go/packages how the
  20. // packages' source is organized.
  21. // The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in
  22. // the path as gopackagesdriver. It's given the inputs to load in its argv. See the package
  23. // documentation in doc.go for the full description of the patterns that need to be supported.
  24. // A driver receives as a JSON-serialized driverRequest struct in standard input and will
  25. // produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output.
  26. // driverRequest is used to provide the portion of Load's Config that is needed by a driver.
  27. type driverRequest struct {
  28. Mode LoadMode `json:"mode"`
  29. // Env specifies the environment the underlying build system should be run in.
  30. Env []string `json:"env"`
  31. // BuildFlags are flags that should be passed to the underlying build system.
  32. BuildFlags []string `json:"build_flags"`
  33. // Tests specifies whether the patterns should also return test packages.
  34. Tests bool `json:"tests"`
  35. // Overlay maps file paths (relative to the driver's working directory) to the byte contents
  36. // of overlay files.
  37. Overlay map[string][]byte `json:"overlay"`
  38. }
  39. // findExternalDriver returns the file path of a tool that supplies
  40. // the build system package structure, or "" if not found."
  41. // If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
  42. // value, otherwise it searches for a binary named gopackagesdriver on the PATH.
  43. func findExternalDriver(cfg *Config) driver {
  44. const toolPrefix = "GOPACKAGESDRIVER="
  45. tool := ""
  46. for _, env := range cfg.Env {
  47. if val := strings.TrimPrefix(env, toolPrefix); val != env {
  48. tool = val
  49. }
  50. }
  51. if tool != "" && tool == "off" {
  52. return nil
  53. }
  54. if tool == "" {
  55. var err error
  56. tool, err = exec.LookPath("gopackagesdriver")
  57. if err != nil {
  58. return nil
  59. }
  60. }
  61. return func(cfg *Config, words ...string) (*driverResponse, error) {
  62. req, err := json.Marshal(driverRequest{
  63. Mode: cfg.Mode,
  64. Env: cfg.Env,
  65. BuildFlags: cfg.BuildFlags,
  66. Tests: cfg.Tests,
  67. Overlay: cfg.Overlay,
  68. })
  69. if err != nil {
  70. return nil, fmt.Errorf("failed to encode message to driver tool: %v", err)
  71. }
  72. buf := new(bytes.Buffer)
  73. stderr := new(bytes.Buffer)
  74. cmd := exec.CommandContext(cfg.Context, tool, words...)
  75. cmd.Dir = cfg.Dir
  76. cmd.Env = cfg.Env
  77. cmd.Stdin = bytes.NewReader(req)
  78. cmd.Stdout = buf
  79. cmd.Stderr = stderr
  80. if err := cmd.Run(); err != nil {
  81. return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
  82. }
  83. if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" {
  84. fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr)
  85. }
  86. var response driverResponse
  87. if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
  88. return nil, err
  89. }
  90. return &response, nil
  91. }
  92. }