Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / mvdan.cc / gofumpt@v0.0.0-20200802201014-ab5a8192947d / gofumports / 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         "mvdan.cc/gofumpt/gofumports/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         Env        []string
134         WorkingDir string
135         Logf       func(format string, args ...interface{})
136 }
137
138 func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) {
139         rawError = i.run(ctx, stdout, stderr)
140         if rawError != nil {
141                 friendlyError = rawError
142                 // Check for 'go' executable not being found.
143                 if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
144                         friendlyError = fmt.Errorf("go command required, not found: %v", ee)
145                 }
146                 if ctx.Err() != nil {
147                         friendlyError = ctx.Err()
148                 }
149                 friendlyError = fmt.Errorf("err: %v: stderr: %s", friendlyError, stderr)
150         }
151         return
152 }
153
154 func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
155         log := i.Logf
156         if log == nil {
157                 log = func(string, ...interface{}) {}
158         }
159
160         goArgs := []string{i.Verb}
161         switch i.Verb {
162         case "mod":
163                 // mod needs the sub-verb before build flags.
164                 goArgs = append(goArgs, i.Args[0])
165                 goArgs = append(goArgs, i.BuildFlags...)
166                 goArgs = append(goArgs, i.Args[1:]...)
167         case "env":
168                 // env doesn't take build flags.
169                 goArgs = append(goArgs, i.Args...)
170         default:
171                 goArgs = append(goArgs, i.BuildFlags...)
172                 goArgs = append(goArgs, i.Args...)
173         }
174         cmd := exec.Command("go", goArgs...)
175         cmd.Stdout = stdout
176         cmd.Stderr = stderr
177         // On darwin the cwd gets resolved to the real path, which breaks anything that
178         // expects the working directory to keep the original path, including the
179         // go command when dealing with modules.
180         // The Go stdlib has a special feature where if the cwd and the PWD are the
181         // same node then it trusts the PWD, so by setting it in the env for the child
182         // process we fix up all the paths returned by the go command.
183         cmd.Env = append(os.Environ(), i.Env...)
184         if i.WorkingDir != "" {
185                 cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir)
186                 cmd.Dir = i.WorkingDir
187         }
188         defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
189
190         return runCmdContext(ctx, cmd)
191 }
192
193 // runCmdContext is like exec.CommandContext except it sends os.Interrupt
194 // before os.Kill.
195 func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
196         if err := cmd.Start(); err != nil {
197                 return err
198         }
199         resChan := make(chan error, 1)
200         go func() {
201                 resChan <- cmd.Wait()
202         }()
203
204         select {
205         case err := <-resChan:
206                 return err
207         case <-ctx.Done():
208         }
209         // Cancelled. Interrupt and see if it ends voluntarily.
210         cmd.Process.Signal(os.Interrupt)
211         select {
212         case err := <-resChan:
213                 return err
214         case <-time.After(time.Second):
215         }
216         // Didn't shut down in response to interrupt. Kill it hard.
217         cmd.Process.Kill()
218         return <-resChan
219 }
220
221 func cmdDebugStr(cmd *exec.Cmd) string {
222         env := make(map[string]string)
223         for _, kv := range cmd.Env {
224                 split := strings.Split(kv, "=")
225                 k, v := split[0], split[1]
226                 env[k] = v
227         }
228
229         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)
230 }