1
0
mirror of https://github.com/golang/go synced 2024-11-19 04:34:39 -07:00
go/internal/lsp/cache/modfiles.go
Rebecca Stambler 831fdb1e18 internal/lsp: push initialization tasks into one function
This change moves as much view initialization code into the
initialization function, instead of having it happen on view create.
Also, the `go env` variables that are collected at inconsistent times
are all collected on view creation. That is sufficient, since the view
is recreated if the environment changes.

I had originally hoped that the initial call to `go env` and the
-modfile detection could become part of this parallel initialization as
well, but you can't create a *packages.Config until the temporary
modfile has been set up, so it still makes sense to do that on view
create. This is, however, the reasoning behind the refactorings in
the -modfile detection in this CL. The main changes are a few renamings
and a split between snapshot.ModFiles and view.modFiles to maximize the
amount of work done in the view. I changed view.modfiles to moduleInformation
because I thought we might want to store additional information there at some
point. Rohan, please let me know if you disagree with any of the changes I made,
and I can revert them.

Fixes golang/go#36487

Change-Id: I504db5a4f41b79bee99ebd391e32e7b520a19569
Reviewed-on: https://go-review.googlesource.com/c/tools/+/214417
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rohan Challa <rohan@golang.org>
2020-01-15 04:46:56 +00:00

131 lines
4.0 KiB
Go

// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cache
import (
"context"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/lsp/telemetry"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/telemetry/log"
errors "golang.org/x/xerrors"
)
func (v *view) modFiles(ctx context.Context) (span.URI, span.URI, error) {
// Don't return errors if the view is not a module.
if v.mod == nil {
return "", "", nil
}
// Copy the real go.mod file content into the temp go.mod file.
origFile, err := os.Open(v.mod.realMod.Filename())
if err != nil {
return "", "", err
}
defer origFile.Close()
tempFile, err := os.OpenFile(v.mod.tempMod.Filename(), os.O_WRONLY, os.ModePerm)
if err != nil {
return "", "", err
}
defer tempFile.Close()
if _, err := io.Copy(tempFile, origFile); err != nil {
return "", "", err
}
return v.mod.realMod, v.mod.tempMod, nil
}
// This function will return the main go.mod file for this folder if it exists and whether the -modfile
// flag exists for this version of go.
func (v *view) modfileFlagExists(ctx context.Context, env []string) (string, bool, error) {
// Check the go version by running "go list" with modules off.
// Borrowed from internal/imports/mod.go:620.
const format = `{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}`
folder := v.folder.Filename()
stdout, err := source.InvokeGo(ctx, folder, append(env, "GO111MODULE=off"), "list", "-e", "-f", format)
if err != nil {
return "", false, err
}
// If the output is not go1.14 or an empty string, then it could be an error.
lines := strings.Split(stdout.String(), "\n")
if len(lines) < 2 && stdout.String() != "" {
log.Error(ctx, "unexpected stdout when checking for go1.14", errors.Errorf("%q", stdout), telemetry.Directory.Of(folder))
return "", false, nil
}
modfile := strings.TrimSpace(v.gomod)
if modfile == os.DevNull {
return "", false, errors.Errorf("unable to detect a go.mod file in %s", v.folder)
}
return modfile, lines[0] == "go1.14", nil
}
func (v *view) setModuleInformation(ctx context.Context, enabled bool) error {
// The user has disabled the use of the -modfile flag.
if !enabled {
log.Print(ctx, "using the -modfile flag is disabled", telemetry.Directory.Of(v.folder))
return nil
}
modFile, flagExists, err := v.modfileFlagExists(ctx, v.Options().Env)
if err != nil {
return err
}
// The user's version of Go does not support the -modfile flag.
if !flagExists {
return nil
}
if modFile == "" || modFile == os.DevNull {
return errors.Errorf("unable to detect a go.mod file in %s", v.folder)
}
// Copy the current go.mod file into the temporary go.mod file.
// The file's name will be of the format go.1234.mod.
// It's temporary go.sum file should have the corresponding format of go.1234.sum.
tempModFile, err := ioutil.TempFile("", "go.*.mod")
if err != nil {
return err
}
defer tempModFile.Close()
origFile, err := os.Open(modFile)
if err != nil {
return err
}
defer origFile.Close()
if _, err := io.Copy(tempModFile, origFile); err != nil {
return err
}
v.mod = &moduleInformation{
realMod: span.FileURI(modFile),
tempMod: span.FileURI(tempModFile.Name()),
}
// Copy go.sum file as well (if there is one).
sumFile := filepath.Join(filepath.Dir(modFile), "go.sum")
stat, err := os.Stat(sumFile)
if err != nil || !stat.Mode().IsRegular() {
return nil
}
contents, err := ioutil.ReadFile(sumFile)
if err != nil {
return err
}
if err := ioutil.WriteFile(v.mod.tempSumFile(), contents, stat.Mode()); err != nil {
return err
}
return nil
}
// tempSumFile returns the path to the copied temporary go.sum file.
// It simply replaces the extension of the temporary go.mod file with "sum".
func (mod *moduleInformation) tempSumFile() string {
tmp := mod.tempMod.Filename()
return tmp[:len(tmp)-len("mod")] + "sum"
}