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