1// Copyright 2018 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 cache implements the caching layer for gopls.
6package cache
7
8import (
9	"context"
10	"encoding/json"
11	"fmt"
12	"io"
13	"io/ioutil"
14	"os"
15	"os/exec"
16	"path"
17	"path/filepath"
18	"reflect"
19	"strings"
20	"sync"
21	"time"
22
23	"golang.org/x/tools/internal/event"
24	"golang.org/x/tools/internal/event/keys"
25	"golang.org/x/tools/internal/gocommand"
26	"golang.org/x/tools/internal/imports"
27	"golang.org/x/tools/internal/lsp/debug/tag"
28	"golang.org/x/tools/internal/lsp/source"
29	"golang.org/x/tools/internal/memoize"
30	"golang.org/x/tools/internal/span"
31	"golang.org/x/tools/internal/xcontext"
32	errors "golang.org/x/xerrors"
33)
34
35type View struct {
36	session *Session
37	id      string
38
39	optionsMu sync.Mutex
40	options   source.Options
41
42	// mu protects most mutable state of the view.
43	mu sync.Mutex
44
45	// baseCtx is the context handed to NewView. This is the parent of all
46	// background contexts created for this view.
47	baseCtx context.Context
48
49	// backgroundCtx is the current context used by background tasks initiated
50	// by the view.
51	backgroundCtx context.Context
52
53	// cancel is called when all action being performed by the current view
54	// should be stopped.
55	cancel context.CancelFunc
56
57	// name is the user visible name of this view.
58	name string
59
60	// folder is the folder with which this view was constructed.
61	folder span.URI
62
63	// root is the root directory of this view. If we are in GOPATH mode, this
64	// is just the folder. If we are in module mode, this is the module root.
65	root span.URI
66
67	// importsMu guards imports-related state, particularly the ProcessEnv.
68	importsMu sync.Mutex
69
70	// processEnv is the process env for this view.
71	// Some of its fields can be changed dynamically by modifications to
72	// the view's options. These fields are repopulated for every use.
73	// Note: this contains cached module and filesystem state.
74	//
75	// TODO(suzmue): the state cached in the process env is specific to each view,
76	// however, there is state that can be shared between views that is not currently
77	// cached, like the module cache.
78	processEnv              *imports.ProcessEnv
79	cleanupProcessEnv       func()
80	cacheRefreshDuration    time.Duration
81	cacheRefreshTimer       *time.Timer
82	cachedModFileIdentifier string
83	cachedBuildFlags        []string
84
85	// keep track of files by uri and by basename, a single file may be mapped
86	// to multiple uris, and the same basename may map to multiple files
87	filesByURI  map[span.URI]*fileBase
88	filesByBase map[string][]*fileBase
89
90	snapshotMu sync.Mutex
91	snapshot   *snapshot
92
93	// initialized is closed when the view has been fully initialized. On
94	// initialization, the view's workspace packages are loaded. All of the
95	// fields below are set as part of initialization. If we failed to load, we
96	// only retry if the go.mod file changes, to avoid too many go/packages
97	// calls.
98	//
99	// When the view is created, initializeOnce is non-nil, initialized is
100	// open, and initCancelFirstAttempt can be used to terminate
101	// initialization. Once initialization completes, initializedErr may be set
102	// and initializeOnce becomes nil. If initializedErr is non-nil,
103	// initialization may be retried (depending on how files are changed). To
104	// indicate that initialization should be retried, initializeOnce will be
105	// set. The next time a caller requests workspace packages, the
106	// initialization will retry.
107	initialized            chan struct{}
108	initCancelFirstAttempt context.CancelFunc
109
110	// initializationSema is used as a mutex to guard initializeOnce and
111	// initializedErr, which will be updated after each attempt to initialize
112	// the view. We use a channel instead of a mutex to avoid blocking when a
113	// context is canceled.
114	initializationSema chan struct{}
115	initializeOnce     *sync.Once
116	initializedErr     error
117
118	// True if the view is either in GOPATH, a module, or some other
119	// non go command build system.
120	hasValidBuildConfiguration bool
121
122	// The real go.mod and go.sum files that are attributed to a view.
123	modURI, sumURI span.URI
124
125	// True if this view runs go commands using temporary mod files.
126	// Only possible with Go versions 1.14 and above.
127	tmpMod bool
128
129	// goCommand indicates if the user is using the go command or some other
130	// build system.
131	goCommand bool
132
133	// `go env` variables that need to be tracked by gopls.
134	gocache, gomodcache, gopath, goprivate string
135
136	// goEnv is the `go env` output collected when a view is created.
137	// It includes the values of the environment variables above.
138	goEnv map[string]string
139}
140
141type builtinPackageHandle struct {
142	handle *memoize.Handle
143}
144
145type builtinPackageData struct {
146	memoize.NoCopy
147
148	parsed *source.BuiltinPackage
149	err    error
150}
151
152// fileBase holds the common functionality for all files.
153// It is intended to be embedded in the file implementations
154type fileBase struct {
155	uris  []span.URI
156	fname string
157
158	view *View
159}
160
161func (f *fileBase) URI() span.URI {
162	return f.uris[0]
163}
164
165func (f *fileBase) filename() string {
166	return f.fname
167}
168
169func (f *fileBase) addURI(uri span.URI) int {
170	f.uris = append(f.uris, uri)
171	return len(f.uris)
172}
173
174func (v *View) ID() string { return v.id }
175
176func (v *View) ValidBuildConfiguration() bool {
177	return v.hasValidBuildConfiguration
178}
179
180func (v *View) ModFile() span.URI {
181	return v.modURI
182}
183
184// tempModFile creates a temporary go.mod file based on the contents of the
185// given go.mod file. It is the caller's responsibility to clean up the files
186// when they are done using them.
187func tempModFile(modFh, sumFH source.FileHandle) (tmpURI span.URI, cleanup func(), err error) {
188	filenameHash := hashContents([]byte(modFh.URI().Filename()))
189	tmpMod, err := ioutil.TempFile("", fmt.Sprintf("go.%s.*.mod", filenameHash))
190	if err != nil {
191		return "", nil, err
192	}
193	defer tmpMod.Close()
194
195	tmpURI = span.URIFromPath(tmpMod.Name())
196	tmpSumName := sumFilename(tmpURI)
197
198	content, err := modFh.Read()
199	if err != nil {
200		return "", nil, err
201	}
202
203	if _, err := tmpMod.Write(content); err != nil {
204		return "", nil, err
205	}
206
207	cleanup = func() {
208		_ = os.Remove(tmpSumName)
209		_ = os.Remove(tmpURI.Filename())
210	}
211
212	// Be careful to clean up if we return an error from this function.
213	defer func() {
214		if err != nil {
215			cleanup()
216			cleanup = nil
217		}
218	}()
219
220	// Create an analogous go.sum, if one exists.
221	if sumFH != nil {
222		sumContents, err := sumFH.Read()
223		if err != nil {
224			return "", cleanup, err
225		}
226		if err := ioutil.WriteFile(tmpSumName, sumContents, 0655); err != nil {
227			return "", cleanup, err
228		}
229	}
230
231	return tmpURI, cleanup, nil
232}
233
234func (v *View) Session() source.Session {
235	return v.session
236}
237
238// Name returns the user visible name of this view.
239func (v *View) Name() string {
240	return v.name
241}
242
243// Folder returns the root of this view.
244func (v *View) Folder() span.URI {
245	return v.folder
246}
247
248func (v *View) Options() source.Options {
249	v.optionsMu.Lock()
250	defer v.optionsMu.Unlock()
251	return v.options
252}
253
254func minorOptionsChange(a, b source.Options) bool {
255	// Check if any of the settings that modify our understanding of files have been changed
256	if !reflect.DeepEqual(a.Env, b.Env) {
257		return false
258	}
259	if !reflect.DeepEqual(a.BuildFlags, b.BuildFlags) {
260		return false
261	}
262	// the rest of the options are benign
263	return true
264}
265
266func (v *View) SetOptions(ctx context.Context, options source.Options) (source.View, error) {
267	// no need to rebuild the view if the options were not materially changed
268	v.optionsMu.Lock()
269	if minorOptionsChange(v.options, options) {
270		v.options = options
271		v.optionsMu.Unlock()
272		return v, nil
273	}
274	v.optionsMu.Unlock()
275	newView, err := v.session.updateView(ctx, v, options)
276	return newView, err
277}
278
279func (v *View) Rebuild(ctx context.Context) (source.Snapshot, func(), error) {
280	newView, err := v.session.updateView(ctx, v, v.Options())
281	if err != nil {
282		return nil, func() {}, err
283	}
284	snapshot, release := newView.Snapshot()
285	return snapshot, release, nil
286}
287
288func (v *View) WriteEnv(ctx context.Context, w io.Writer) error {
289	v.optionsMu.Lock()
290	env, buildFlags := v.envLocked()
291	v.optionsMu.Unlock()
292
293	fullEnv := make(map[string]string)
294	for k, v := range v.goEnv {
295		fullEnv[k] = v
296	}
297	for _, v := range env {
298		s := strings.SplitN(v, "=", 2)
299		if len(s) != 2 {
300			continue
301		}
302		if _, ok := fullEnv[s[0]]; ok {
303			fullEnv[s[0]] = s[1]
304		}
305
306	}
307	fmt.Fprintf(w, "go env for %v\n(root %s)\n(valid build configuration = %v)\n(build flags: %v)\n",
308		v.folder.Filename(), v.root.Filename(), v.hasValidBuildConfiguration, buildFlags)
309	for k, v := range fullEnv {
310		fmt.Fprintf(w, "%s=%s\n", k, v)
311	}
312	return nil
313}
314
315func (v *View) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error {
316	v.importsMu.Lock()
317	defer v.importsMu.Unlock()
318
319	// Use temporary go.mod files, but always go to disk for the contents.
320	// Rebuilding the cache is expensive, and we don't want to do it for
321	// transient changes.
322	var modFH, sumFH source.FileHandle
323	var modFileIdentifier string
324	var err error
325	if v.modURI != "" {
326		modFH, err = v.session.cache.getFile(ctx, v.modURI)
327		if err != nil {
328			return err
329		}
330		modFileIdentifier = modFH.FileIdentity().Hash
331	}
332	if v.sumURI != "" {
333		sumFH, err = v.session.cache.getFile(ctx, v.sumURI)
334		if err != nil {
335			return err
336		}
337	}
338	// v.goEnv is immutable -- changes make a new view. Options can change.
339	// We can't compare build flags directly because we may add -modfile.
340	v.optionsMu.Lock()
341	localPrefix := v.options.LocalPrefix
342	currentBuildFlags := v.options.BuildFlags
343	changed := !reflect.DeepEqual(currentBuildFlags, v.cachedBuildFlags) ||
344		v.options.VerboseOutput != (v.processEnv.Logf != nil) ||
345		modFileIdentifier != v.cachedModFileIdentifier
346	v.optionsMu.Unlock()
347
348	// If anything relevant to imports has changed, clear caches and
349	// update the processEnv. Clearing caches blocks on any background
350	// scans.
351	if changed {
352		// As a special case, skip cleanup the first time -- we haven't fully
353		// initialized the environment yet and calling GetResolver will do
354		// unnecessary work and potentially mess up the go.mod file.
355		if v.cleanupProcessEnv != nil {
356			if resolver, err := v.processEnv.GetResolver(); err == nil {
357				resolver.(*imports.ModuleResolver).ClearForNewMod()
358			}
359			v.cleanupProcessEnv()
360		}
361		v.cachedModFileIdentifier = modFileIdentifier
362		v.cachedBuildFlags = currentBuildFlags
363		v.cleanupProcessEnv, err = v.populateProcessEnv(ctx, modFH, sumFH)
364		if err != nil {
365			return err
366		}
367	}
368
369	// Run the user function.
370	opts := &imports.Options{
371		// Defaults.
372		AllErrors:   true,
373		Comments:    true,
374		Fragment:    true,
375		FormatOnly:  false,
376		TabIndent:   true,
377		TabWidth:    8,
378		Env:         v.processEnv,
379		LocalPrefix: localPrefix,
380	}
381
382	if err := fn(opts); err != nil {
383		return err
384	}
385
386	if v.cacheRefreshTimer == nil {
387		// Don't refresh more than twice per minute.
388		delay := 30 * time.Second
389		// Don't spend more than a couple percent of the time refreshing.
390		if adaptive := 50 * v.cacheRefreshDuration; adaptive > delay {
391			delay = adaptive
392		}
393		v.cacheRefreshTimer = time.AfterFunc(delay, v.refreshProcessEnv)
394	}
395
396	return nil
397}
398
399func (v *View) refreshProcessEnv() {
400	start := time.Now()
401
402	v.importsMu.Lock()
403	env := v.processEnv
404	if resolver, err := v.processEnv.GetResolver(); err == nil {
405		resolver.ClearForNewScan()
406	}
407	v.importsMu.Unlock()
408
409	// We don't have a context handy to use for logging, so use the stdlib for now.
410	event.Log(v.baseCtx, "background imports cache refresh starting")
411	if err := imports.PrimeCache(context.Background(), env); err == nil {
412		event.Log(v.baseCtx, fmt.Sprintf("background refresh finished after %v", time.Since(start)))
413	} else {
414		event.Log(v.baseCtx, fmt.Sprintf("background refresh finished after %v", time.Since(start)), keys.Err.Of(err))
415	}
416	v.importsMu.Lock()
417	v.cacheRefreshDuration = time.Since(start)
418	v.cacheRefreshTimer = nil
419	v.importsMu.Unlock()
420}
421
422// populateProcessEnv sets the dynamically configurable fields for the view's
423// process environment. Assumes that the caller is holding the s.view.importsMu.
424func (v *View) populateProcessEnv(ctx context.Context, modFH, sumFH source.FileHandle) (cleanup func(), err error) {
425	cleanup = func() {}
426	pe := v.processEnv
427
428	v.optionsMu.Lock()
429	pe.BuildFlags = append([]string(nil), v.options.BuildFlags...)
430	if v.options.VerboseOutput {
431		pe.Logf = func(format string, args ...interface{}) {
432			event.Log(ctx, fmt.Sprintf(format, args...))
433		}
434	} else {
435		pe.Logf = nil
436	}
437	v.optionsMu.Unlock()
438
439	// Add -modfile to the build flags, if we are using it.
440	if v.tmpMod && modFH != nil {
441		var tmpURI span.URI
442		tmpURI, cleanup, err = tempModFile(modFH, sumFH)
443		if err != nil {
444			return nil, err
445		}
446		pe.BuildFlags = append(pe.BuildFlags, fmt.Sprintf("-modfile=%s", tmpURI.Filename()))
447	}
448
449	return cleanup, nil
450}
451
452// envLocked returns the environment and build flags for the current view.
453// It assumes that the caller is holding the view's optionsMu.
454func (v *View) envLocked() ([]string, []string) {
455	env := append([]string{}, v.options.Env...)
456	buildFlags := append([]string{}, v.options.BuildFlags...)
457	return env, buildFlags
458}
459
460func (v *View) contains(uri span.URI) bool {
461	return strings.HasPrefix(string(uri), string(v.root))
462}
463
464func (v *View) mapFile(uri span.URI, f *fileBase) {
465	v.filesByURI[uri] = f
466	if f.addURI(uri) == 1 {
467		basename := basename(f.filename())
468		v.filesByBase[basename] = append(v.filesByBase[basename], f)
469	}
470}
471
472func basename(filename string) string {
473	return strings.ToLower(filepath.Base(filename))
474}
475
476func (v *View) WorkspaceDirectories(ctx context.Context) ([]string, error) {
477	// If the view does not have a go.mod file, only the root directory
478	// is known. In GOPATH mode, we should really watch the entire GOPATH,
479	// but that's probably too expensive.
480	// TODO(rstambler): Figure out a better approach in the future.
481	if v.modURI == "" {
482		return []string{v.root.Filename()}, nil
483	}
484	// Anything inside of the module root is known.
485	dirs := []string{filepath.Dir(v.modURI.Filename())}
486
487	// Keep track of any directories mentioned in replace targets.
488	fh, err := v.session.GetFile(ctx, v.modURI)
489	if err != nil {
490		return nil, err
491	}
492	snapshot, release := v.Snapshot()
493	defer release()
494
495	pm, err := snapshot.ParseMod(ctx, fh)
496	if err != nil {
497		return nil, err
498	}
499	for _, replace := range pm.File.Replace {
500		dirs = append(dirs, replace.New.Path)
501	}
502	return dirs, nil
503}
504
505func (v *View) relevantChange(c source.FileModification) bool {
506	// If the file is known to the view, the change is relevant.
507	known := v.knownFile(c.URI)
508
509	// If the file is not known to the view, and the change is only on-disk,
510	// we should not invalidate the snapshot. This is necessary because Emacs
511	// sends didChangeWatchedFiles events for temp files.
512	if !known && c.OnDisk && (c.Action == source.Change || c.Action == source.Delete) {
513		return false
514	}
515	return v.contains(c.URI) || known
516}
517
518func (v *View) knownFile(uri span.URI) bool {
519	v.mu.Lock()
520	defer v.mu.Unlock()
521
522	f, err := v.findFile(uri)
523	return f != nil && err == nil
524}
525
526// getFile returns a file for the given URI. It will always succeed because it
527// adds the file to the managed set if needed.
528func (v *View) getFile(uri span.URI) (*fileBase, error) {
529	v.mu.Lock()
530	defer v.mu.Unlock()
531
532	f, err := v.findFile(uri)
533	if err != nil {
534		return nil, err
535	} else if f != nil {
536		return f, nil
537	}
538	f = &fileBase{
539		view:  v,
540		fname: uri.Filename(),
541	}
542	v.mapFile(uri, f)
543	return f, nil
544}
545
546// findFile checks the cache for any file matching the given uri.
547//
548// An error is only returned for an irreparable failure, for example, if the
549// filename in question does not exist.
550func (v *View) findFile(uri span.URI) (*fileBase, error) {
551	if f := v.filesByURI[uri]; f != nil {
552		// a perfect match
553		return f, nil
554	}
555	// no exact match stored, time to do some real work
556	// check for any files with the same basename
557	fname := uri.Filename()
558	basename := basename(fname)
559	if candidates := v.filesByBase[basename]; candidates != nil {
560		pathStat, err := os.Stat(fname)
561		if os.IsNotExist(err) {
562			return nil, err
563		}
564		if err != nil {
565			return nil, nil // the file may exist, return without an error
566		}
567		for _, c := range candidates {
568			if cStat, err := os.Stat(c.filename()); err == nil {
569				if os.SameFile(pathStat, cStat) {
570					// same file, map it
571					v.mapFile(uri, c)
572					return c, nil
573				}
574			}
575		}
576	}
577	// no file with a matching name was found, it wasn't in our cache
578	return nil, nil
579}
580
581func (v *View) Shutdown(ctx context.Context) {
582	v.session.removeView(ctx, v)
583}
584
585func (v *View) shutdown(ctx context.Context) {
586	// Cancel the initial workspace load if it is still running.
587	v.initCancelFirstAttempt()
588
589	v.mu.Lock()
590	defer v.mu.Unlock()
591	if v.cancel != nil {
592		v.cancel()
593		v.cancel = nil
594	}
595}
596
597func (v *View) BackgroundContext() context.Context {
598	v.mu.Lock()
599	defer v.mu.Unlock()
600
601	return v.backgroundCtx
602}
603
604func (v *View) IgnoredFile(uri span.URI) bool {
605	filename := uri.Filename()
606	var prefixes []string
607	if v.modURI == "" {
608		for _, entry := range filepath.SplitList(v.gopath) {
609			prefixes = append(prefixes, filepath.Join(entry, "src"))
610		}
611	} else {
612		mainMod := filepath.Dir(v.modURI.Filename())
613		prefixes = []string{mainMod, v.gomodcache}
614	}
615
616	for _, prefix := range prefixes {
617		if strings.HasPrefix(filename, prefix) {
618			return checkIgnored(filename[len(prefix):])
619		}
620	}
621	return false
622}
623
624// checkIgnored implements go list's exclusion rules. go help list:
625// 		Directory and file names that begin with "." or "_" are ignored
626// 		by the go tool, as are directories named "testdata".
627func checkIgnored(suffix string) bool {
628	for _, component := range strings.Split(suffix, string(filepath.Separator)) {
629		if len(component) == 0 {
630			continue
631		}
632		if component[0] == '.' || component[0] == '_' || component == "testdata" {
633			return true
634		}
635	}
636	return false
637}
638
639func (v *View) Snapshot() (source.Snapshot, func()) {
640	s := v.getSnapshot()
641	s.active.Add(1)
642	return s, s.active.Done
643}
644
645func (v *View) getSnapshot() *snapshot {
646	v.snapshotMu.Lock()
647	defer v.snapshotMu.Unlock()
648
649	return v.snapshot
650}
651
652func (v *View) initialize(ctx context.Context, s *snapshot, firstAttempt bool) {
653	select {
654	case <-ctx.Done():
655		return
656	case v.initializationSema <- struct{}{}:
657	}
658
659	defer func() {
660		<-v.initializationSema
661	}()
662
663	if v.initializeOnce == nil {
664		return
665	}
666	v.initializeOnce.Do(func() {
667		defer func() {
668			v.initializeOnce = nil
669			if firstAttempt {
670				close(v.initialized)
671			}
672		}()
673
674		err := s.load(ctx, viewLoadScope("LOAD_VIEW"), packagePath("builtin"))
675		if ctx.Err() != nil {
676			return
677		}
678		if err != nil {
679			event.Error(ctx, "initial workspace load failed", err)
680		}
681		v.initializedErr = err
682	})
683}
684
685func (v *View) awaitInitialized(ctx context.Context) {
686	select {
687	case <-ctx.Done():
688		return
689	case <-v.initialized:
690	}
691	// We typically prefer to run something as intensive as the IWL without
692	// blocking. I'm not sure if there is a way to do that here.
693	v.initialize(ctx, v.getSnapshot(), false)
694}
695
696// invalidateContent invalidates the content of a Go file,
697// including any position and type information that depends on it.
698// It returns true if we were already tracking the given file, false otherwise.
699func (v *View) invalidateContent(ctx context.Context, uris map[span.URI]source.VersionedFileHandle, forceReloadMetadata bool) (source.Snapshot, func()) {
700	// Detach the context so that content invalidation cannot be canceled.
701	ctx = xcontext.Detach(ctx)
702
703	// Cancel all still-running previous requests, since they would be
704	// operating on stale data.
705	v.cancelBackground()
706
707	// Do not clone a snapshot until its view has finished initializing.
708	v.awaitInitialized(ctx)
709
710	// This should be the only time we hold the view's snapshot lock for any period of time.
711	v.snapshotMu.Lock()
712	defer v.snapshotMu.Unlock()
713
714	oldSnapshot := v.snapshot
715	v.snapshot = oldSnapshot.clone(ctx, uris, forceReloadMetadata)
716	// Move the View's reference from the old snapshot to the new one.
717	oldSnapshot.active.Done()
718	v.snapshot.active.Add(1)
719
720	v.snapshot.active.Add(1)
721	return v.snapshot, v.snapshot.active.Done
722}
723
724func (v *View) cancelBackground() {
725	v.mu.Lock()
726	defer v.mu.Unlock()
727	if v.cancel == nil {
728		// this can happen during shutdown
729		return
730	}
731	v.cancel()
732	v.backgroundCtx, v.cancel = context.WithCancel(v.baseCtx)
733}
734
735func (v *View) maybeReinitialize() {
736	v.initializationSema <- struct{}{}
737	defer func() {
738		<-v.initializationSema
739	}()
740
741	if v.initializedErr == nil {
742		return
743	}
744	var once sync.Once
745	v.initializeOnce = &once
746}
747
748func (v *View) setBuildInformation(ctx context.Context, folder span.URI, env []string, modfileFlagEnabled bool) error {
749	if err := checkPathCase(folder.Filename()); err != nil {
750		return fmt.Errorf("invalid workspace configuration: %w", err)
751	}
752	// Make sure to get the `go env` before continuing with initialization.
753	modFile, err := v.setGoEnv(ctx, env)
754	if err != nil {
755		return err
756	}
757	if modFile == os.DevNull {
758		return nil
759	}
760	v.modURI = span.URIFromPath(modFile)
761	// Set the sumURI, if the go.sum exists.
762	sumFilename := filepath.Join(filepath.Dir(modFile), "go.sum")
763	if stat, _ := os.Stat(sumFilename); stat != nil {
764		v.sumURI = span.URIFromPath(sumFilename)
765	}
766
767	v.root = v.folder
768	if v.modURI != "" {
769		v.root = span.URIFromPath(filepath.Dir(v.modURI.Filename()))
770	}
771
772	// Now that we have set all required fields,
773	// check if the view has a valid build configuration.
774	v.setBuildConfiguration()
775
776	// The user has disabled the use of the -modfile flag or has no go.mod file.
777	if !modfileFlagEnabled || v.modURI == "" {
778		return nil
779	}
780	if modfileFlag, err := v.modfileFlagExists(ctx, v.Options().Env); err != nil {
781		return err
782	} else if modfileFlag {
783		v.tmpMod = true
784	}
785	return nil
786}
787
788// OS-specific path case check, for case-insensitive filesystems.
789var checkPathCase = defaultCheckPathCase
790
791func defaultCheckPathCase(path string) error {
792	return nil
793}
794
795func (v *View) setBuildConfiguration() (isValid bool) {
796	defer func() {
797		v.hasValidBuildConfiguration = isValid
798	}()
799	// Since we only really understand the `go` command, if the user is not
800	// using the go command, assume that their configuration is valid.
801	if !v.goCommand {
802		return true
803	}
804	// Check if the user is working within a module.
805	if v.modURI != "" {
806		return true
807	}
808	// The user may have a multiple directories in their GOPATH.
809	// Check if the workspace is within any of them.
810	for _, gp := range filepath.SplitList(v.gopath) {
811		if isSubdirectory(filepath.Join(gp, "src"), v.folder.Filename()) {
812			return true
813		}
814	}
815	return false
816}
817
818func isSubdirectory(root, leaf string) bool {
819	rel, err := filepath.Rel(root, leaf)
820	return err == nil && !strings.HasPrefix(rel, "..")
821}
822
823// setGoEnv sets the view's various GO* values. It also returns the view's
824// GOMOD value, which need not be cached.
825func (v *View) setGoEnv(ctx context.Context, configEnv []string) (string, error) {
826	var gomod string
827	vars := map[string]*string{
828		"GOCACHE":    &v.gocache,
829		"GOPATH":     &v.gopath,
830		"GOPRIVATE":  &v.goprivate,
831		"GOMODCACHE": &v.gomodcache,
832		"GOMOD":      &gomod,
833	}
834	// We can save ~200 ms by requesting only the variables we care about.
835	args := append([]string{"-json"}, imports.RequiredGoEnvVars...)
836	for k := range vars {
837		args = append(args, k)
838	}
839
840	inv := gocommand.Invocation{
841		Verb:       "env",
842		Args:       args,
843		Env:        configEnv,
844		WorkingDir: v.Folder().Filename(),
845	}
846	// Don't go through runGoCommand, as we don't need a temporary -modfile to
847	// run `go env`.
848	stdout, err := v.session.gocmdRunner.Run(ctx, inv)
849	if err != nil {
850		return "", err
851	}
852	if err := json.Unmarshal(stdout.Bytes(), &v.goEnv); err != nil {
853		return "", err
854	}
855
856	for key, ptr := range vars {
857		*ptr = v.goEnv[key]
858	}
859
860	// Old versions of Go don't have GOMODCACHE, so emulate it.
861	if v.gomodcache == "" && v.gopath != "" {
862		v.gomodcache = filepath.Join(filepath.SplitList(v.gopath)[0], "pkg/mod")
863	}
864
865	// The value of GOPACKAGESDRIVER is not returned through the go command.
866	// A user may also have a gopackagesdriver binary on their machine, which
867	// works the same way as setting GOPACKAGESDRIVER.
868	gopackagesdriver := os.Getenv("GOPACKAGESDRIVER")
869	tool, _ := exec.LookPath("gopackagesdriver")
870	v.goCommand = tool == "" && (gopackagesdriver == "" || gopackagesdriver == "off")
871	return gomod, nil
872}
873
874func (v *View) IsGoPrivatePath(target string) bool {
875	return globsMatchPath(v.goprivate, target)
876}
877
878// Copied from
879// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/str/path.go;l=58;drc=2910c5b4a01a573ebc97744890a07c1a3122c67a
880func globsMatchPath(globs, target string) bool {
881	for globs != "" {
882		// Extract next non-empty glob in comma-separated list.
883		var glob string
884		if i := strings.Index(globs, ","); i >= 0 {
885			glob, globs = globs[:i], globs[i+1:]
886		} else {
887			glob, globs = globs, ""
888		}
889		if glob == "" {
890			continue
891		}
892
893		// A glob with N+1 path elements (N slashes) needs to be matched
894		// against the first N+1 path elements of target,
895		// which end just before the N+1'th slash.
896		n := strings.Count(glob, "/")
897		prefix := target
898		// Walk target, counting slashes, truncating at the N+1'th slash.
899		for i := 0; i < len(target); i++ {
900			if target[i] == '/' {
901				if n == 0 {
902					prefix = target[:i]
903					break
904				}
905				n--
906			}
907		}
908		if n > 0 {
909			// Not enough prefix elements.
910			continue
911		}
912		matched, _ := path.Match(glob, prefix)
913		if matched {
914			return true
915		}
916	}
917	return false
918}
919
920// This function will return the main go.mod file for this folder if it exists
921// and whether the -modfile flag exists for this version of go.
922func (v *View) modfileFlagExists(ctx context.Context, env []string) (bool, error) {
923	// Check the go version by running "go list" with modules off.
924	// Borrowed from internal/imports/mod.go:620.
925	const format = `{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}`
926	folder := v.folder.Filename()
927	inv := gocommand.Invocation{
928		Verb:       "list",
929		Args:       []string{"-e", "-f", format},
930		Env:        append(env, "GO111MODULE=off"),
931		WorkingDir: v.root.Filename(),
932	}
933	stdout, err := v.session.gocmdRunner.Run(ctx, inv)
934	if err != nil {
935		return false, err
936	}
937	// If the output is not go1.14 or an empty string, then it could be an error.
938	lines := strings.Split(stdout.String(), "\n")
939	if len(lines) < 2 && stdout.String() != "" {
940		event.Error(ctx, "unexpected stdout when checking for go1.14", errors.Errorf("%q", stdout), tag.Directory.Of(folder))
941		return false, nil
942	}
943	return lines[0] == "go1.14", nil
944}
945