invoke.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // Copyright 2020 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. // Package gocommand is a helper for calling the go command.
  5. package gocommand
  6. import (
  7. "bytes"
  8. "context"
  9. "fmt"
  10. exec "golang.org/x/sys/execabs"
  11. "io"
  12. "os"
  13. "regexp"
  14. "strconv"
  15. "strings"
  16. "sync"
  17. "time"
  18. "golang.org/x/tools/internal/event"
  19. )
  20. // An Runner will run go command invocations and serialize
  21. // them if it sees a concurrency error.
  22. type Runner struct {
  23. // once guards the runner initialization.
  24. once sync.Once
  25. // inFlight tracks available workers.
  26. inFlight chan struct{}
  27. // serialized guards the ability to run a go command serially,
  28. // to avoid deadlocks when claiming workers.
  29. serialized chan struct{}
  30. }
  31. const maxInFlight = 10
  32. func (runner *Runner) initialize() {
  33. runner.once.Do(func() {
  34. runner.inFlight = make(chan struct{}, maxInFlight)
  35. runner.serialized = make(chan struct{}, 1)
  36. })
  37. }
  38. // 1.13: go: updates to go.mod needed, but contents have changed
  39. // 1.14: go: updating go.mod: existing contents have changed since last read
  40. var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`)
  41. // Run is a convenience wrapper around RunRaw.
  42. // It returns only stdout and a "friendly" error.
  43. func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) {
  44. stdout, _, friendly, _ := runner.RunRaw(ctx, inv)
  45. return stdout, friendly
  46. }
  47. // RunPiped runs the invocation serially, always waiting for any concurrent
  48. // invocations to complete first.
  49. func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error {
  50. _, err := runner.runPiped(ctx, inv, stdout, stderr)
  51. return err
  52. }
  53. // RunRaw runs the invocation, serializing requests only if they fight over
  54. // go.mod changes.
  55. func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
  56. // Make sure the runner is always initialized.
  57. runner.initialize()
  58. // First, try to run the go command concurrently.
  59. stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv)
  60. // If we encounter a load concurrency error, we need to retry serially.
  61. if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) {
  62. return stdout, stderr, friendlyErr, err
  63. }
  64. event.Error(ctx, "Load concurrency error, will retry serially", err)
  65. // Run serially by calling runPiped.
  66. stdout.Reset()
  67. stderr.Reset()
  68. friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr)
  69. return stdout, stderr, friendlyErr, err
  70. }
  71. func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
  72. // Wait for 1 worker to become available.
  73. select {
  74. case <-ctx.Done():
  75. return nil, nil, nil, ctx.Err()
  76. case runner.inFlight <- struct{}{}:
  77. defer func() { <-runner.inFlight }()
  78. }
  79. stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
  80. friendlyErr, err := inv.runWithFriendlyError(ctx, stdout, stderr)
  81. return stdout, stderr, friendlyErr, err
  82. }
  83. func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) {
  84. // Make sure the runner is always initialized.
  85. runner.initialize()
  86. // Acquire the serialization lock. This avoids deadlocks between two
  87. // runPiped commands.
  88. select {
  89. case <-ctx.Done():
  90. return nil, ctx.Err()
  91. case runner.serialized <- struct{}{}:
  92. defer func() { <-runner.serialized }()
  93. }
  94. // Wait for all in-progress go commands to return before proceeding,
  95. // to avoid load concurrency errors.
  96. for i := 0; i < maxInFlight; i++ {
  97. select {
  98. case <-ctx.Done():
  99. return nil, ctx.Err()
  100. case runner.inFlight <- struct{}{}:
  101. // Make sure we always "return" any workers we took.
  102. defer func() { <-runner.inFlight }()
  103. }
  104. }
  105. return inv.runWithFriendlyError(ctx, stdout, stderr)
  106. }
  107. // An Invocation represents a call to the go command.
  108. type Invocation struct {
  109. Verb string
  110. Args []string
  111. BuildFlags []string
  112. ModFlag string
  113. ModFile string
  114. Overlay string
  115. // If CleanEnv is set, the invocation will run only with the environment
  116. // in Env, not starting with os.Environ.
  117. CleanEnv bool
  118. Env []string
  119. WorkingDir string
  120. Logf func(format string, args ...interface{})
  121. }
  122. func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) {
  123. rawError = i.run(ctx, stdout, stderr)
  124. if rawError != nil {
  125. friendlyError = rawError
  126. // Check for 'go' executable not being found.
  127. if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
  128. friendlyError = fmt.Errorf("go command required, not found: %v", ee)
  129. }
  130. if ctx.Err() != nil {
  131. friendlyError = ctx.Err()
  132. }
  133. friendlyError = fmt.Errorf("err: %v: stderr: %s", friendlyError, stderr)
  134. }
  135. return
  136. }
  137. func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
  138. log := i.Logf
  139. if log == nil {
  140. log = func(string, ...interface{}) {}
  141. }
  142. goArgs := []string{i.Verb}
  143. appendModFile := func() {
  144. if i.ModFile != "" {
  145. goArgs = append(goArgs, "-modfile="+i.ModFile)
  146. }
  147. }
  148. appendModFlag := func() {
  149. if i.ModFlag != "" {
  150. goArgs = append(goArgs, "-mod="+i.ModFlag)
  151. }
  152. }
  153. appendOverlayFlag := func() {
  154. if i.Overlay != "" {
  155. goArgs = append(goArgs, "-overlay="+i.Overlay)
  156. }
  157. }
  158. switch i.Verb {
  159. case "env", "version":
  160. goArgs = append(goArgs, i.Args...)
  161. case "mod":
  162. // mod needs the sub-verb before flags.
  163. goArgs = append(goArgs, i.Args[0])
  164. appendModFile()
  165. goArgs = append(goArgs, i.Args[1:]...)
  166. case "get":
  167. goArgs = append(goArgs, i.BuildFlags...)
  168. appendModFile()
  169. goArgs = append(goArgs, i.Args...)
  170. default: // notably list and build.
  171. goArgs = append(goArgs, i.BuildFlags...)
  172. appendModFile()
  173. appendModFlag()
  174. appendOverlayFlag()
  175. goArgs = append(goArgs, i.Args...)
  176. }
  177. cmd := exec.Command("go", goArgs...)
  178. cmd.Stdout = stdout
  179. cmd.Stderr = stderr
  180. // On darwin the cwd gets resolved to the real path, which breaks anything that
  181. // expects the working directory to keep the original path, including the
  182. // go command when dealing with modules.
  183. // The Go stdlib has a special feature where if the cwd and the PWD are the
  184. // same node then it trusts the PWD, so by setting it in the env for the child
  185. // process we fix up all the paths returned by the go command.
  186. if !i.CleanEnv {
  187. cmd.Env = os.Environ()
  188. }
  189. cmd.Env = append(cmd.Env, i.Env...)
  190. if i.WorkingDir != "" {
  191. cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir)
  192. cmd.Dir = i.WorkingDir
  193. }
  194. defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
  195. return runCmdContext(ctx, cmd)
  196. }
  197. // runCmdContext is like exec.CommandContext except it sends os.Interrupt
  198. // before os.Kill.
  199. func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
  200. if err := cmd.Start(); err != nil {
  201. return err
  202. }
  203. resChan := make(chan error, 1)
  204. go func() {
  205. resChan <- cmd.Wait()
  206. }()
  207. select {
  208. case err := <-resChan:
  209. return err
  210. case <-ctx.Done():
  211. }
  212. // Cancelled. Interrupt and see if it ends voluntarily.
  213. cmd.Process.Signal(os.Interrupt)
  214. select {
  215. case err := <-resChan:
  216. return err
  217. case <-time.After(time.Second):
  218. }
  219. // Didn't shut down in response to interrupt. Kill it hard.
  220. cmd.Process.Kill()
  221. return <-resChan
  222. }
  223. func cmdDebugStr(cmd *exec.Cmd) string {
  224. env := make(map[string]string)
  225. for _, kv := range cmd.Env {
  226. split := strings.SplitN(kv, "=", 2)
  227. k, v := split[0], split[1]
  228. env[k] = v
  229. }
  230. var args []string
  231. for _, arg := range cmd.Args {
  232. quoted := strconv.Quote(arg)
  233. if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") {
  234. args = append(args, quoted)
  235. } else {
  236. args = append(args, arg)
  237. }
  238. }
  239. return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " "))
  240. }