package source import ( "bytes" "context" "fmt" "os/exec" errors "golang.org/x/xerrors" ) // InvokeGo returns the output of a go command invocation. // It does not try to recover from errors. func InvokeGo(ctx context.Context, dir string, env []string, args ...string) (*bytes.Buffer, error) { stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) cmd := exec.CommandContext(ctx, "go", args...) // On darwin the cwd gets resolved to the real path, which breaks anything that // expects the working directory to keep the original path, including the // go command when dealing with modules. // The Go stdlib has a special feature where if the cwd and the PWD are the // same node then it trusts the PWD, so by setting it in the env for the child // process we fix up all the paths returned by the go command. cmd.Env = append(append([]string{}, env...), "PWD="+dir) cmd.Dir = dir cmd.Stdout = stdout cmd.Stderr = stderr if err := cmd.Run(); err != nil { // Check for 'go' executable not being found. if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound { return nil, fmt.Errorf("'gopls requires 'go', but %s", exec.ErrNotFound) } if ctx.Err() != nil { return nil, ctx.Err() } return stdout, errors.Errorf("err: %v: stderr: %s", err, stderr) } return stdout, nil }