internal/lsp: print view-specific environment

For debugging purposes, we print the output of `go env` on start. Now,
we print a view-specific `go env`, which will helps us when debugging
multi-project workspaces. Additional information included: the folder of
the view, if the view has a valid build configuration, and the build
flags for the view.

Updates golang/go#37978

Change-Id: Iadffd46f8ac5410971558a304b8bbcb2f9bdbcc3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/225137
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
Rebecca Stambler 2020-03-23 22:47:52 -04:00
parent a5e5fedfe7
commit 6fc5d0bc36
7 changed files with 55 additions and 20 deletions

View File

@ -93,7 +93,7 @@ func (s *snapshot) load(ctx context.Context, scopes ...interface{}) error {
return ctx.Err()
}
event.Print(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
event.Print(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
if len(pkgs) == 0 {
return err
}

View File

@ -262,6 +262,24 @@ func (v *view) buildBuiltinPackage(ctx context.Context, goFiles []string) error
return nil
}
func (v *view) WriteEnv(ctx context.Context, w io.Writer) error {
env, buildFlags := v.env()
// TODO(rstambler): We could probably avoid running this by saving the
// output on original create, but I'm not sure if it's worth it.
inv := gocommand.Invocation{
Verb: "env",
Env: env,
WorkingDir: v.Folder().Filename(),
}
stdout, err := inv.Run(ctx)
if err != nil {
return err
}
fmt.Fprintf(w, "go env for %v\n(valid build configuration = %v)\n(build flags: %v)\n", v.folder.Filename(), v.hasValidBuildConfiguration, buildFlags)
fmt.Fprint(w, stdout)
return nil
}
func (v *view) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error {
v.importsMu.Lock()
defer v.importsMu.Unlock()

View File

@ -32,7 +32,7 @@ func (v *version) DetailedHelp(f *flag.FlagSet) {
// Run prints version information to stdout.
func (v *version) Run(ctx context.Context, args ...string) error {
debug.PrintVersionInfo(os.Stdout, v.app.Verbose, debug.PlainText)
debug.PrintVersionInfo(ctx, os.Stdout, v.app.Verbose, debug.PlainText)
return nil
}
@ -69,7 +69,7 @@ A failing unit test is the best.
func (b *bug) Run(ctx context.Context, args ...string) error {
buf := &bytes.Buffer{}
fmt.Fprint(buf, goplsBugHeader)
debug.PrintVersionInfo(buf, true, debug.Markdown)
debug.PrintVersionInfo(ctx, buf, true, debug.Markdown)
body := buf.String()
title := strings.Join(args, " ")
if !strings.HasPrefix(title, goplsBugPrefix) {

View File

@ -6,10 +6,12 @@
package debug
import (
"context"
"fmt"
"io"
"os/exec"
"strings"
"golang.org/x/tools/internal/gocommand"
)
type PrintMode int
@ -24,7 +26,7 @@ const (
var Version = "master"
// PrintServerInfo writes HTML debug info to w for the Instance.
func (i *Instance) PrintServerInfo(w io.Writer) {
func (i *Instance) PrintServerInfo(ctx context.Context, w io.Writer) {
section(w, HTML, "Server Instance", func() {
fmt.Fprintf(w, "Start time: %v\n", i.StartTime)
fmt.Fprintf(w, "LogFile: %s\n", i.Logfile)
@ -32,13 +34,13 @@ func (i *Instance) PrintServerInfo(w io.Writer) {
fmt.Fprintf(w, "Address: %s\n", i.ServerAddress)
fmt.Fprintf(w, "Debug address: %s\n", i.DebugAddress)
})
PrintVersionInfo(w, true, HTML)
PrintVersionInfo(ctx, w, true, HTML)
}
// PrintVersionInfo writes version and environment information to w, using the
// output format specified by mode. verbose controls whether additional
// information is written, including section headers.
func PrintVersionInfo(w io.Writer, verbose bool, mode PrintMode) {
// PrintVersionInfo writes version information to w, using the output format
// specified by mode. verbose controls whether additional information is
// written, including section headers.
func PrintVersionInfo(ctx context.Context, w io.Writer, verbose bool, mode PrintMode) {
if !verbose {
printBuildInfo(w, false, mode)
return
@ -48,13 +50,14 @@ func PrintVersionInfo(w io.Writer, verbose bool, mode PrintMode) {
})
fmt.Fprint(w, "\n")
section(w, mode, "Go info", func() {
cmd := exec.Command("go", "version")
cmd.Stdout = w
cmd.Run()
fmt.Fprint(w, "\n")
cmd = exec.Command("go", "env")
cmd.Stdout = w
cmd.Run()
i := &gocommand.Invocation{
Verb: "version",
}
version, err := i.Run(ctx)
if err != nil {
panic(err)
}
fmt.Fprintln(w, version.String())
})
}

View File

@ -376,7 +376,7 @@ func (i *Instance) getFile(r *http.Request) interface{} {
func (i *Instance) getInfo(r *http.Request) interface{} {
buf := &bytes.Buffer{}
i.PrintServerInfo(buf)
i.PrintServerInfo(r.Context(), buf)
return template.HTML(buf.String())
}

View File

@ -13,6 +13,7 @@ import (
"golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp/debug"
"golang.org/x/tools/internal/lsp/debug/tag"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/span"
@ -156,7 +157,7 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa
}
buf := &bytes.Buffer{}
debug.PrintVersionInfo(buf, true, debug.PlainText)
debug.PrintVersionInfo(ctx, buf, true, debug.PlainText)
event.Print(ctx, buf.String())
s.addFolders(ctx, s.pendingFolders)
@ -171,11 +172,20 @@ func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFol
for _, folder := range folders {
uri := span.URIFromURI(folder.URI)
_, snapshot, err := s.addView(ctx, folder.Name, uri)
view, snapshot, err := s.addView(ctx, folder.Name, uri)
if err != nil {
viewErrors[uri] = err
continue
}
// Print each view's environment.
buf := &bytes.Buffer{}
if err := view.WriteEnv(ctx, buf); err != nil {
event.Error(ctx, "failed to write environment", err, tag.Directory.Of(view.Folder()))
continue
}
event.Print(ctx, buf.String())
// Diagnose the newly created view.
go s.diagnoseDetached(snapshot)
}
if len(viewErrors) > 0 {

View File

@ -10,6 +10,7 @@ import (
"go/ast"
"go/token"
"go/types"
"io"
"golang.org/x/mod/modfile"
"golang.org/x/tools/go/analysis"
@ -125,6 +126,9 @@ type View interface {
// Ignore returns true if this file should be ignored by this view.
Ignore(span.URI) bool
// WriteEnv writes the view-specific environment to the io.Writer.
WriteEnv(ctx context.Context, w io.Writer) error
// RunProcessEnvFunc runs fn with the process env for this snapshot's view.
// Note: the process env contains cached module and filesystem state.
RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error