...

Source file src/golang.org/x/tools/go/packages/golist.go

Documentation: golang.org/x/tools/go/packages

     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 packages
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"go/types"
    13  	"io/ioutil"
    14  	"log"
    15  	"os"
    16  	"path"
    17  	"path/filepath"
    18  	"reflect"
    19  	"sort"
    20  	"strconv"
    21  	"strings"
    22  	"sync"
    23  	"unicode"
    24  
    25  	exec "golang.org/x/sys/execabs"
    26  	"golang.org/x/tools/go/internal/packagesdriver"
    27  	"golang.org/x/tools/internal/gocommand"
    28  	"golang.org/x/tools/internal/packagesinternal"
    29  )
    30  
    31  // debug controls verbose logging.
    32  var debug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG"))
    33  
    34  // A goTooOldError reports that the go command
    35  // found by exec.LookPath is too old to use the new go list behavior.
    36  type goTooOldError struct {
    37  	error
    38  }
    39  
    40  // responseDeduper wraps a driverResponse, deduplicating its contents.
    41  type responseDeduper struct {
    42  	seenRoots    map[string]bool
    43  	seenPackages map[string]*Package
    44  	dr           *driverResponse
    45  }
    46  
    47  func newDeduper() *responseDeduper {
    48  	return &responseDeduper{
    49  		dr:           &driverResponse{},
    50  		seenRoots:    map[string]bool{},
    51  		seenPackages: map[string]*Package{},
    52  	}
    53  }
    54  
    55  // addAll fills in r with a driverResponse.
    56  func (r *responseDeduper) addAll(dr *driverResponse) {
    57  	for _, pkg := range dr.Packages {
    58  		r.addPackage(pkg)
    59  	}
    60  	for _, root := range dr.Roots {
    61  		r.addRoot(root)
    62  	}
    63  	r.dr.GoVersion = dr.GoVersion
    64  }
    65  
    66  func (r *responseDeduper) addPackage(p *Package) {
    67  	if r.seenPackages[p.ID] != nil {
    68  		return
    69  	}
    70  	r.seenPackages[p.ID] = p
    71  	r.dr.Packages = append(r.dr.Packages, p)
    72  }
    73  
    74  func (r *responseDeduper) addRoot(id string) {
    75  	if r.seenRoots[id] {
    76  		return
    77  	}
    78  	r.seenRoots[id] = true
    79  	r.dr.Roots = append(r.dr.Roots, id)
    80  }
    81  
    82  type golistState struct {
    83  	cfg *Config
    84  	ctx context.Context
    85  
    86  	envOnce    sync.Once
    87  	goEnvError error
    88  	goEnv      map[string]string
    89  
    90  	rootsOnce     sync.Once
    91  	rootDirsError error
    92  	rootDirs      map[string]string
    93  
    94  	goVersionOnce  sync.Once
    95  	goVersionError error
    96  	goVersion      int // The X in Go 1.X.
    97  
    98  	// vendorDirs caches the (non)existence of vendor directories.
    99  	vendorDirs map[string]bool
   100  }
   101  
   102  // getEnv returns Go environment variables. Only specific variables are
   103  // populated -- computing all of them is slow.
   104  func (state *golistState) getEnv() (map[string]string, error) {
   105  	state.envOnce.Do(func() {
   106  		var b *bytes.Buffer
   107  		b, state.goEnvError = state.invokeGo("env", "-json", "GOMOD", "GOPATH")
   108  		if state.goEnvError != nil {
   109  			return
   110  		}
   111  
   112  		state.goEnv = make(map[string]string)
   113  		decoder := json.NewDecoder(b)
   114  		if state.goEnvError = decoder.Decode(&state.goEnv); state.goEnvError != nil {
   115  			return
   116  		}
   117  	})
   118  	return state.goEnv, state.goEnvError
   119  }
   120  
   121  // mustGetEnv is a convenience function that can be used if getEnv has already succeeded.
   122  func (state *golistState) mustGetEnv() map[string]string {
   123  	env, err := state.getEnv()
   124  	if err != nil {
   125  		panic(fmt.Sprintf("mustGetEnv: %v", err))
   126  	}
   127  	return env
   128  }
   129  
   130  // goListDriver uses the go list command to interpret the patterns and produce
   131  // the build system package structure.
   132  // See driver for more details.
   133  func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
   134  	// Make sure that any asynchronous go commands are killed when we return.
   135  	parentCtx := cfg.Context
   136  	if parentCtx == nil {
   137  		parentCtx = context.Background()
   138  	}
   139  	ctx, cancel := context.WithCancel(parentCtx)
   140  	defer cancel()
   141  
   142  	response := newDeduper()
   143  
   144  	state := &golistState{
   145  		cfg:        cfg,
   146  		ctx:        ctx,
   147  		vendorDirs: map[string]bool{},
   148  	}
   149  
   150  	// Fill in response.Sizes asynchronously if necessary.
   151  	var sizeserr error
   152  	var sizeswg sync.WaitGroup
   153  	if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
   154  		sizeswg.Add(1)
   155  		go func() {
   156  			var sizes types.Sizes
   157  			sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner)
   158  			// types.SizesFor always returns nil or a *types.StdSizes.
   159  			response.dr.Sizes, _ = sizes.(*types.StdSizes)
   160  			sizeswg.Done()
   161  		}()
   162  	}
   163  
   164  	// Determine files requested in contains patterns
   165  	var containFiles []string
   166  	restPatterns := make([]string, 0, len(patterns))
   167  	// Extract file= and other [querytype]= patterns. Report an error if querytype
   168  	// doesn't exist.
   169  extractQueries:
   170  	for _, pattern := range patterns {
   171  		eqidx := strings.Index(pattern, "=")
   172  		if eqidx < 0 {
   173  			restPatterns = append(restPatterns, pattern)
   174  		} else {
   175  			query, value := pattern[:eqidx], pattern[eqidx+len("="):]
   176  			switch query {
   177  			case "file":
   178  				containFiles = append(containFiles, value)
   179  			case "pattern":
   180  				restPatterns = append(restPatterns, value)
   181  			case "": // not a reserved query
   182  				restPatterns = append(restPatterns, pattern)
   183  			default:
   184  				for _, rune := range query {
   185  					if rune < 'a' || rune > 'z' { // not a reserved query
   186  						restPatterns = append(restPatterns, pattern)
   187  						continue extractQueries
   188  					}
   189  				}
   190  				// Reject all other patterns containing "="
   191  				return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern)
   192  			}
   193  		}
   194  	}
   195  
   196  	// See if we have any patterns to pass through to go list. Zero initial
   197  	// patterns also requires a go list call, since it's the equivalent of
   198  	// ".".
   199  	if len(restPatterns) > 0 || len(patterns) == 0 {
   200  		dr, err := state.createDriverResponse(restPatterns...)
   201  		if err != nil {
   202  			return nil, err
   203  		}
   204  		response.addAll(dr)
   205  	}
   206  
   207  	if len(containFiles) != 0 {
   208  		if err := state.runContainsQueries(response, containFiles); err != nil {
   209  			return nil, err
   210  		}
   211  	}
   212  
   213  	// Only use go/packages' overlay processing if we're using a Go version
   214  	// below 1.16. Otherwise, go list handles it.
   215  	if goVersion, err := state.getGoVersion(); err == nil && goVersion < 16 {
   216  		modifiedPkgs, needPkgs, err := state.processGolistOverlay(response)
   217  		if err != nil {
   218  			return nil, err
   219  		}
   220  
   221  		var containsCandidates []string
   222  		if len(containFiles) > 0 {
   223  			containsCandidates = append(containsCandidates, modifiedPkgs...)
   224  			containsCandidates = append(containsCandidates, needPkgs...)
   225  		}
   226  		if err := state.addNeededOverlayPackages(response, needPkgs); err != nil {
   227  			return nil, err
   228  		}
   229  		// Check candidate packages for containFiles.
   230  		if len(containFiles) > 0 {
   231  			for _, id := range containsCandidates {
   232  				pkg, ok := response.seenPackages[id]
   233  				if !ok {
   234  					response.addPackage(&Package{
   235  						ID: id,
   236  						Errors: []Error{{
   237  							Kind: ListError,
   238  							Msg:  fmt.Sprintf("package %s expected but not seen", id),
   239  						}},
   240  					})
   241  					continue
   242  				}
   243  				for _, f := range containFiles {
   244  					for _, g := range pkg.GoFiles {
   245  						if sameFile(f, g) {
   246  							response.addRoot(id)
   247  						}
   248  					}
   249  				}
   250  			}
   251  		}
   252  		// Add root for any package that matches a pattern. This applies only to
   253  		// packages that are modified by overlays, since they are not added as
   254  		// roots automatically.
   255  		for _, pattern := range restPatterns {
   256  			match := matchPattern(pattern)
   257  			for _, pkgID := range modifiedPkgs {
   258  				pkg, ok := response.seenPackages[pkgID]
   259  				if !ok {
   260  					continue
   261  				}
   262  				if match(pkg.PkgPath) {
   263  					response.addRoot(pkg.ID)
   264  				}
   265  			}
   266  		}
   267  	}
   268  
   269  	sizeswg.Wait()
   270  	if sizeserr != nil {
   271  		return nil, sizeserr
   272  	}
   273  	return response.dr, nil
   274  }
   275  
   276  func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error {
   277  	if len(pkgs) == 0 {
   278  		return nil
   279  	}
   280  	dr, err := state.createDriverResponse(pkgs...)
   281  	if err != nil {
   282  		return err
   283  	}
   284  	for _, pkg := range dr.Packages {
   285  		response.addPackage(pkg)
   286  	}
   287  	_, needPkgs, err := state.processGolistOverlay(response)
   288  	if err != nil {
   289  		return err
   290  	}
   291  	return state.addNeededOverlayPackages(response, needPkgs)
   292  }
   293  
   294  func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error {
   295  	for _, query := range queries {
   296  		// TODO(matloob): Do only one query per directory.
   297  		fdir := filepath.Dir(query)
   298  		// Pass absolute path of directory to go list so that it knows to treat it as a directory,
   299  		// not a package path.
   300  		pattern, err := filepath.Abs(fdir)
   301  		if err != nil {
   302  			return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err)
   303  		}
   304  		dirResponse, err := state.createDriverResponse(pattern)
   305  
   306  		// If there was an error loading the package, or no packages are returned,
   307  		// or the package is returned with errors, try to load the file as an
   308  		// ad-hoc package.
   309  		// Usually the error will appear in a returned package, but may not if we're
   310  		// in module mode and the ad-hoc is located outside a module.
   311  		if err != nil || len(dirResponse.Packages) == 0 || len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].GoFiles) == 0 &&
   312  			len(dirResponse.Packages[0].Errors) == 1 {
   313  			var queryErr error
   314  			if dirResponse, queryErr = state.adhocPackage(pattern, query); queryErr != nil {
   315  				return err // return the original error
   316  			}
   317  		}
   318  		isRoot := make(map[string]bool, len(dirResponse.Roots))
   319  		for _, root := range dirResponse.Roots {
   320  			isRoot[root] = true
   321  		}
   322  		for _, pkg := range dirResponse.Packages {
   323  			// Add any new packages to the main set
   324  			// We don't bother to filter packages that will be dropped by the changes of roots,
   325  			// that will happen anyway during graph construction outside this function.
   326  			// Over-reporting packages is not a problem.
   327  			response.addPackage(pkg)
   328  			// if the package was not a root one, it cannot have the file
   329  			if !isRoot[pkg.ID] {
   330  				continue
   331  			}
   332  			for _, pkgFile := range pkg.GoFiles {
   333  				if filepath.Base(query) == filepath.Base(pkgFile) {
   334  					response.addRoot(pkg.ID)
   335  					break
   336  				}
   337  			}
   338  		}
   339  	}
   340  	return nil
   341  }
   342  
   343  // adhocPackage attempts to load or construct an ad-hoc package for a given
   344  // query, if the original call to the driver produced inadequate results.
   345  func (state *golistState) adhocPackage(pattern, query string) (*driverResponse, error) {
   346  	response, err := state.createDriverResponse(query)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  	// If we get nothing back from `go list`,
   351  	// try to make this file into its own ad-hoc package.
   352  	// TODO(rstambler): Should this check against the original response?
   353  	if len(response.Packages) == 0 {
   354  		response.Packages = append(response.Packages, &Package{
   355  			ID:              "command-line-arguments",
   356  			PkgPath:         query,
   357  			GoFiles:         []string{query},
   358  			CompiledGoFiles: []string{query},
   359  			Imports:         make(map[string]*Package),
   360  		})
   361  		response.Roots = append(response.Roots, "command-line-arguments")
   362  	}
   363  	// Handle special cases.
   364  	if len(response.Packages) == 1 {
   365  		// golang/go#33482: If this is a file= query for ad-hoc packages where
   366  		// the file only exists on an overlay, and exists outside of a module,
   367  		// add the file to the package and remove the errors.
   368  		if response.Packages[0].ID == "command-line-arguments" ||
   369  			filepath.ToSlash(response.Packages[0].PkgPath) == filepath.ToSlash(query) {
   370  			if len(response.Packages[0].GoFiles) == 0 {
   371  				filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath
   372  				// TODO(matloob): check if the file is outside of a root dir?
   373  				for path := range state.cfg.Overlay {
   374  					if path == filename {
   375  						response.Packages[0].Errors = nil
   376  						response.Packages[0].GoFiles = []string{path}
   377  						response.Packages[0].CompiledGoFiles = []string{path}
   378  					}
   379  				}
   380  			}
   381  		}
   382  	}
   383  	return response, nil
   384  }
   385  
   386  // Fields must match go list;
   387  // see $GOROOT/src/cmd/go/internal/load/pkg.go.
   388  type jsonPackage struct {
   389  	ImportPath        string
   390  	Dir               string
   391  	Name              string
   392  	Export            string
   393  	GoFiles           []string
   394  	CompiledGoFiles   []string
   395  	IgnoredGoFiles    []string
   396  	IgnoredOtherFiles []string
   397  	EmbedPatterns     []string
   398  	EmbedFiles        []string
   399  	CFiles            []string
   400  	CgoFiles          []string
   401  	CXXFiles          []string
   402  	MFiles            []string
   403  	HFiles            []string
   404  	FFiles            []string
   405  	SFiles            []string
   406  	SwigFiles         []string
   407  	SwigCXXFiles      []string
   408  	SysoFiles         []string
   409  	Imports           []string
   410  	ImportMap         map[string]string
   411  	Deps              []string
   412  	Module            *Module
   413  	TestGoFiles       []string
   414  	TestImports       []string
   415  	XTestGoFiles      []string
   416  	XTestImports      []string
   417  	ForTest           string // q in a "p [q.test]" package, else ""
   418  	DepOnly           bool
   419  
   420  	Error      *packagesinternal.PackageError
   421  	DepsErrors []*packagesinternal.PackageError
   422  }
   423  
   424  type jsonPackageError struct {
   425  	ImportStack []string
   426  	Pos         string
   427  	Err         string
   428  }
   429  
   430  func otherFiles(p *jsonPackage) [][]string {
   431  	return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
   432  }
   433  
   434  // createDriverResponse uses the "go list" command to expand the pattern
   435  // words and return a response for the specified packages.
   436  func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) {
   437  	// go list uses the following identifiers in ImportPath and Imports:
   438  	//
   439  	// 	"p"			-- importable package or main (command)
   440  	// 	"q.test"		-- q's test executable
   441  	// 	"p [q.test]"		-- variant of p as built for q's test executable
   442  	// 	"q_test [q.test]"	-- q's external test package
   443  	//
   444  	// The packages p that are built differently for a test q.test
   445  	// are q itself, plus any helpers used by the external test q_test,
   446  	// typically including "testing" and all its dependencies.
   447  
   448  	// Run "go list" for complete
   449  	// information on the specified packages.
   450  	goVersion, err := state.getGoVersion()
   451  	if err != nil {
   452  		return nil, err
   453  	}
   454  	buf, err := state.invokeGo("list", golistargs(state.cfg, words, goVersion)...)
   455  	if err != nil {
   456  		return nil, err
   457  	}
   458  
   459  	seen := make(map[string]*jsonPackage)
   460  	pkgs := make(map[string]*Package)
   461  	additionalErrors := make(map[string][]Error)
   462  	// Decode the JSON and convert it to Package form.
   463  	response := &driverResponse{
   464  		GoVersion: goVersion,
   465  	}
   466  	for dec := json.NewDecoder(buf); dec.More(); {
   467  		p := new(jsonPackage)
   468  		if err := dec.Decode(p); err != nil {
   469  			return nil, fmt.Errorf("JSON decoding failed: %v", err)
   470  		}
   471  
   472  		if p.ImportPath == "" {
   473  			// The documentation for go list says that “[e]rroneous packages will have
   474  			// a non-empty ImportPath”. If for some reason it comes back empty, we
   475  			// prefer to error out rather than silently discarding data or handing
   476  			// back a package without any way to refer to it.
   477  			if p.Error != nil {
   478  				return nil, Error{
   479  					Pos: p.Error.Pos,
   480  					Msg: p.Error.Err,
   481  				}
   482  			}
   483  			return nil, fmt.Errorf("package missing import path: %+v", p)
   484  		}
   485  
   486  		// Work around https://golang.org/issue/33157:
   487  		// go list -e, when given an absolute path, will find the package contained at
   488  		// that directory. But when no package exists there, it will return a fake package
   489  		// with an error and the ImportPath set to the absolute path provided to go list.
   490  		// Try to convert that absolute path to what its package path would be if it's
   491  		// contained in a known module or GOPATH entry. This will allow the package to be
   492  		// properly "reclaimed" when overlays are processed.
   493  		if filepath.IsAbs(p.ImportPath) && p.Error != nil {
   494  			pkgPath, ok, err := state.getPkgPath(p.ImportPath)
   495  			if err != nil {
   496  				return nil, err
   497  			}
   498  			if ok {
   499  				p.ImportPath = pkgPath
   500  			}
   501  		}
   502  
   503  		if old, found := seen[p.ImportPath]; found {
   504  			// If one version of the package has an error, and the other doesn't, assume
   505  			// that this is a case where go list is reporting a fake dependency variant
   506  			// of the imported package: When a package tries to invalidly import another
   507  			// package, go list emits a variant of the imported package (with the same
   508  			// import path, but with an error on it, and the package will have a
   509  			// DepError set on it). An example of when this can happen is for imports of
   510  			// main packages: main packages can not be imported, but they may be
   511  			// separately matched and listed by another pattern.
   512  			// See golang.org/issue/36188 for more details.
   513  
   514  			// The plan is that eventually, hopefully in Go 1.15, the error will be
   515  			// reported on the importing package rather than the duplicate "fake"
   516  			// version of the imported package. Once all supported versions of Go
   517  			// have the new behavior this logic can be deleted.
   518  			// TODO(matloob): delete the workaround logic once all supported versions of
   519  			// Go return the errors on the proper package.
   520  
   521  			// There should be exactly one version of a package that doesn't have an
   522  			// error.
   523  			if old.Error == nil && p.Error == nil {
   524  				if !reflect.DeepEqual(p, old) {
   525  					return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
   526  				}
   527  				continue
   528  			}
   529  
   530  			// Determine if this package's error needs to be bubbled up.
   531  			// This is a hack, and we expect for go list to eventually set the error
   532  			// on the package.
   533  			if old.Error != nil {
   534  				var errkind string
   535  				if strings.Contains(old.Error.Err, "not an importable package") {
   536  					errkind = "not an importable package"
   537  				} else if strings.Contains(old.Error.Err, "use of internal package") && strings.Contains(old.Error.Err, "not allowed") {
   538  					errkind = "use of internal package not allowed"
   539  				}
   540  				if errkind != "" {
   541  					if len(old.Error.ImportStack) < 1 {
   542  						return nil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, errkind)
   543  					}
   544  					importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-1]
   545  					if importingPkg == old.ImportPath {
   546  						// Using an older version of Go which put this package itself on top of import
   547  						// stack, instead of the importer. Look for importer in second from top
   548  						// position.
   549  						if len(old.Error.ImportStack) < 2 {
   550  							return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack without importing package`, errkind)
   551  						}
   552  						importingPkg = old.Error.ImportStack[len(old.Error.ImportStack)-2]
   553  					}
   554  					additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{
   555  						Pos:  old.Error.Pos,
   556  						Msg:  old.Error.Err,
   557  						Kind: ListError,
   558  					})
   559  				}
   560  			}
   561  
   562  			// Make sure that if there's a version of the package without an error,
   563  			// that's the one reported to the user.
   564  			if old.Error == nil {
   565  				continue
   566  			}
   567  
   568  			// This package will replace the old one at the end of the loop.
   569  		}
   570  		seen[p.ImportPath] = p
   571  
   572  		pkg := &Package{
   573  			Name:            p.Name,
   574  			ID:              p.ImportPath,
   575  			GoFiles:         absJoin(p.Dir, p.GoFiles, p.CgoFiles),
   576  			CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
   577  			OtherFiles:      absJoin(p.Dir, otherFiles(p)...),
   578  			EmbedFiles:      absJoin(p.Dir, p.EmbedFiles),
   579  			EmbedPatterns:   absJoin(p.Dir, p.EmbedPatterns),
   580  			IgnoredFiles:    absJoin(p.Dir, p.IgnoredGoFiles, p.IgnoredOtherFiles),
   581  			forTest:         p.ForTest,
   582  			depsErrors:      p.DepsErrors,
   583  			Module:          p.Module,
   584  		}
   585  
   586  		if (state.cfg.Mode&typecheckCgo) != 0 && len(p.CgoFiles) != 0 {
   587  			if len(p.CompiledGoFiles) > len(p.GoFiles) {
   588  				// We need the cgo definitions, which are in the first
   589  				// CompiledGoFile after the non-cgo ones. This is a hack but there
   590  				// isn't currently a better way to find it. We also need the pure
   591  				// Go files and unprocessed cgo files, all of which are already
   592  				// in pkg.GoFiles.
   593  				cgoTypes := p.CompiledGoFiles[len(p.GoFiles)]
   594  				pkg.CompiledGoFiles = append([]string{cgoTypes}, pkg.GoFiles...)
   595  			} else {
   596  				// golang/go#38990: go list silently fails to do cgo processing
   597  				pkg.CompiledGoFiles = nil
   598  				pkg.Errors = append(pkg.Errors, Error{
   599  					Msg:  "go list failed to return CompiledGoFiles. This may indicate failure to perform cgo processing; try building at the command line. See https://golang.org/issue/38990.",
   600  					Kind: ListError,
   601  				})
   602  			}
   603  		}
   604  
   605  		// Work around https://golang.org/issue/28749:
   606  		// cmd/go puts assembly, C, and C++ files in CompiledGoFiles.
   607  		// Remove files from CompiledGoFiles that are non-go files
   608  		// (or are not files that look like they are from the cache).
   609  		if len(pkg.CompiledGoFiles) > 0 {
   610  			out := pkg.CompiledGoFiles[:0]
   611  			for _, f := range pkg.CompiledGoFiles {
   612  				if ext := filepath.Ext(f); ext != ".go" && ext != "" { // ext == "" means the file is from the cache, so probably cgo-processed file
   613  					continue
   614  				}
   615  				out = append(out, f)
   616  			}
   617  			pkg.CompiledGoFiles = out
   618  		}
   619  
   620  		// Extract the PkgPath from the package's ID.
   621  		if i := strings.IndexByte(pkg.ID, ' '); i >= 0 {
   622  			pkg.PkgPath = pkg.ID[:i]
   623  		} else {
   624  			pkg.PkgPath = pkg.ID
   625  		}
   626  
   627  		if pkg.PkgPath == "unsafe" {
   628  			pkg.GoFiles = nil // ignore fake unsafe.go file
   629  		}
   630  
   631  		// Assume go list emits only absolute paths for Dir.
   632  		if p.Dir != "" && !filepath.IsAbs(p.Dir) {
   633  			log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir)
   634  		}
   635  
   636  		if p.Export != "" && !filepath.IsAbs(p.Export) {
   637  			pkg.ExportFile = filepath.Join(p.Dir, p.Export)
   638  		} else {
   639  			pkg.ExportFile = p.Export
   640  		}
   641  
   642  		// imports
   643  		//
   644  		// Imports contains the IDs of all imported packages.
   645  		// ImportsMap records (path, ID) only where they differ.
   646  		ids := make(map[string]bool)
   647  		for _, id := range p.Imports {
   648  			ids[id] = true
   649  		}
   650  		pkg.Imports = make(map[string]*Package)
   651  		for path, id := range p.ImportMap {
   652  			pkg.Imports[path] = &Package{ID: id} // non-identity import
   653  			delete(ids, id)
   654  		}
   655  		for id := range ids {
   656  			if id == "C" {
   657  				continue
   658  			}
   659  
   660  			pkg.Imports[id] = &Package{ID: id} // identity import
   661  		}
   662  		if !p.DepOnly {
   663  			response.Roots = append(response.Roots, pkg.ID)
   664  		}
   665  
   666  		// Work around for pre-go.1.11 versions of go list.
   667  		// TODO(matloob): they should be handled by the fallback.
   668  		// Can we delete this?
   669  		if len(pkg.CompiledGoFiles) == 0 {
   670  			pkg.CompiledGoFiles = pkg.GoFiles
   671  		}
   672  
   673  		// Temporary work-around for golang/go#39986. Parse filenames out of
   674  		// error messages. This happens if there are unrecoverable syntax
   675  		// errors in the source, so we can't match on a specific error message.
   676  		if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
   677  			addFilenameFromPos := func(pos string) bool {
   678  				split := strings.Split(pos, ":")
   679  				if len(split) < 1 {
   680  					return false
   681  				}
   682  				filename := strings.TrimSpace(split[0])
   683  				if filename == "" {
   684  					return false
   685  				}
   686  				if !filepath.IsAbs(filename) {
   687  					filename = filepath.Join(state.cfg.Dir, filename)
   688  				}
   689  				info, _ := os.Stat(filename)
   690  				if info == nil {
   691  					return false
   692  				}
   693  				pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename)
   694  				pkg.GoFiles = append(pkg.GoFiles, filename)
   695  				return true
   696  			}
   697  			found := addFilenameFromPos(err.Pos)
   698  			// In some cases, go list only reports the error position in the
   699  			// error text, not the error position. One such case is when the
   700  			// file's package name is a keyword (see golang.org/issue/39763).
   701  			if !found {
   702  				addFilenameFromPos(err.Err)
   703  			}
   704  		}
   705  
   706  		if p.Error != nil {
   707  			msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363.
   708  			// Address golang.org/issue/35964 by appending import stack to error message.
   709  			if msg == "import cycle not allowed" && len(p.Error.ImportStack) != 0 {
   710  				msg += fmt.Sprintf(": import stack: %v", p.Error.ImportStack)
   711  			}
   712  			pkg.Errors = append(pkg.Errors, Error{
   713  				Pos:  p.Error.Pos,
   714  				Msg:  msg,
   715  				Kind: ListError,
   716  			})
   717  		}
   718  
   719  		pkgs[pkg.ID] = pkg
   720  	}
   721  
   722  	for id, errs := range additionalErrors {
   723  		if p, ok := pkgs[id]; ok {
   724  			p.Errors = append(p.Errors, errs...)
   725  		}
   726  	}
   727  	for _, pkg := range pkgs {
   728  		response.Packages = append(response.Packages, pkg)
   729  	}
   730  	sort.Slice(response.Packages, func(i, j int) bool { return response.Packages[i].ID < response.Packages[j].ID })
   731  
   732  	return response, nil
   733  }
   734  
   735  func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool {
   736  	if len(p.GoFiles) > 0 || len(p.CompiledGoFiles) > 0 {
   737  		return false
   738  	}
   739  
   740  	goV, err := state.getGoVersion()
   741  	if err != nil {
   742  		return false
   743  	}
   744  
   745  	// On Go 1.14 and earlier, only add filenames from errors if the import stack is empty.
   746  	// The import stack behaves differently for these versions than newer Go versions.
   747  	if goV < 15 {
   748  		return len(p.Error.ImportStack) == 0
   749  	}
   750  
   751  	// On Go 1.15 and later, only parse filenames out of error if there's no import stack,
   752  	// or the current package is at the top of the import stack. This is not guaranteed
   753  	// to work perfectly, but should avoid some cases where files in errors don't belong to this
   754  	// package.
   755  	return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath
   756  }
   757  
   758  // getGoVersion returns the effective minor version of the go command.
   759  func (state *golistState) getGoVersion() (int, error) {
   760  	state.goVersionOnce.Do(func() {
   761  		state.goVersion, state.goVersionError = gocommand.GoVersion(state.ctx, state.cfgInvocation(), state.cfg.gocmdRunner)
   762  	})
   763  	return state.goVersion, state.goVersionError
   764  }
   765  
   766  // getPkgPath finds the package path of a directory if it's relative to a root
   767  // directory.
   768  func (state *golistState) getPkgPath(dir string) (string, bool, error) {
   769  	absDir, err := filepath.Abs(dir)
   770  	if err != nil {
   771  		return "", false, err
   772  	}
   773  	roots, err := state.determineRootDirs()
   774  	if err != nil {
   775  		return "", false, err
   776  	}
   777  
   778  	for rdir, rpath := range roots {
   779  		// Make sure that the directory is in the module,
   780  		// to avoid creating a path relative to another module.
   781  		if !strings.HasPrefix(absDir, rdir) {
   782  			continue
   783  		}
   784  		// TODO(matloob): This doesn't properly handle symlinks.
   785  		r, err := filepath.Rel(rdir, dir)
   786  		if err != nil {
   787  			continue
   788  		}
   789  		if rpath != "" {
   790  			// We choose only one root even though the directory even it can belong in multiple modules
   791  			// or GOPATH entries. This is okay because we only need to work with absolute dirs when a
   792  			// file is missing from disk, for instance when gopls calls go/packages in an overlay.
   793  			// Once the file is saved, gopls, or the next invocation of the tool will get the correct
   794  			// result straight from golist.
   795  			// TODO(matloob): Implement module tiebreaking?
   796  			return path.Join(rpath, filepath.ToSlash(r)), true, nil
   797  		}
   798  		return filepath.ToSlash(r), true, nil
   799  	}
   800  	return "", false, nil
   801  }
   802  
   803  // absJoin absolutizes and flattens the lists of files.
   804  func absJoin(dir string, fileses ...[]string) (res []string) {
   805  	for _, files := range fileses {
   806  		for _, file := range files {
   807  			if !filepath.IsAbs(file) {
   808  				file = filepath.Join(dir, file)
   809  			}
   810  			res = append(res, file)
   811  		}
   812  	}
   813  	return res
   814  }
   815  
   816  func jsonFlag(cfg *Config, goVersion int) string {
   817  	if goVersion < 19 {
   818  		return "-json"
   819  	}
   820  	var fields []string
   821  	added := make(map[string]bool)
   822  	addFields := func(fs ...string) {
   823  		for _, f := range fs {
   824  			if !added[f] {
   825  				added[f] = true
   826  				fields = append(fields, f)
   827  			}
   828  		}
   829  	}
   830  	addFields("Name", "ImportPath", "Error") // These fields are always needed
   831  	if cfg.Mode&NeedFiles != 0 || cfg.Mode&NeedTypes != 0 {
   832  		addFields("Dir", "GoFiles", "IgnoredGoFiles", "IgnoredOtherFiles", "CFiles",
   833  			"CgoFiles", "CXXFiles", "MFiles", "HFiles", "FFiles", "SFiles",
   834  			"SwigFiles", "SwigCXXFiles", "SysoFiles")
   835  		if cfg.Tests {
   836  			addFields("TestGoFiles", "XTestGoFiles")
   837  		}
   838  	}
   839  	if cfg.Mode&NeedTypes != 0 {
   840  		// CompiledGoFiles seems to be required for the test case TestCgoNoSyntax,
   841  		// even when -compiled isn't passed in.
   842  		// TODO(#52435): Should we make the test ask for -compiled, or automatically
   843  		// request CompiledGoFiles in certain circumstances?
   844  		addFields("Dir", "CompiledGoFiles")
   845  	}
   846  	if cfg.Mode&NeedCompiledGoFiles != 0 {
   847  		addFields("Dir", "CompiledGoFiles", "Export")
   848  	}
   849  	if cfg.Mode&NeedImports != 0 {
   850  		// When imports are requested, DepOnly is used to distinguish between packages
   851  		// explicitly requested and transitive imports of those packages.
   852  		addFields("DepOnly", "Imports", "ImportMap")
   853  		if cfg.Tests {
   854  			addFields("TestImports", "XTestImports")
   855  		}
   856  	}
   857  	if cfg.Mode&NeedDeps != 0 {
   858  		addFields("DepOnly")
   859  	}
   860  	if usesExportData(cfg) {
   861  		// Request Dir in the unlikely case Export is not absolute.
   862  		addFields("Dir", "Export")
   863  	}
   864  	if cfg.Mode&needInternalForTest != 0 {
   865  		addFields("ForTest")
   866  	}
   867  	if cfg.Mode&needInternalDepsErrors != 0 {
   868  		addFields("DepsErrors")
   869  	}
   870  	if cfg.Mode&NeedModule != 0 {
   871  		addFields("Module")
   872  	}
   873  	if cfg.Mode&NeedEmbedFiles != 0 {
   874  		addFields("EmbedFiles")
   875  	}
   876  	if cfg.Mode&NeedEmbedPatterns != 0 {
   877  		addFields("EmbedPatterns")
   878  	}
   879  	return "-json=" + strings.Join(fields, ",")
   880  }
   881  
   882  func golistargs(cfg *Config, words []string, goVersion int) []string {
   883  	const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
   884  	fullargs := []string{
   885  		"-e", jsonFlag(cfg, goVersion),
   886  		fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0),
   887  		fmt.Sprintf("-test=%t", cfg.Tests),
   888  		fmt.Sprintf("-export=%t", usesExportData(cfg)),
   889  		fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0),
   890  		// go list doesn't let you pass -test and -find together,
   891  		// probably because you'd just get the TestMain.
   892  		fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0 && !usesExportData(cfg)),
   893  	}
   894  	fullargs = append(fullargs, cfg.BuildFlags...)
   895  	fullargs = append(fullargs, "--")
   896  	fullargs = append(fullargs, words...)
   897  	return fullargs
   898  }
   899  
   900  // cfgInvocation returns an Invocation that reflects cfg's settings.
   901  func (state *golistState) cfgInvocation() gocommand.Invocation {
   902  	cfg := state.cfg
   903  	return gocommand.Invocation{
   904  		BuildFlags: cfg.BuildFlags,
   905  		ModFile:    cfg.modFile,
   906  		ModFlag:    cfg.modFlag,
   907  		CleanEnv:   cfg.Env != nil,
   908  		Env:        cfg.Env,
   909  		Logf:       cfg.Logf,
   910  		WorkingDir: cfg.Dir,
   911  	}
   912  }
   913  
   914  // invokeGo returns the stdout of a go command invocation.
   915  func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
   916  	cfg := state.cfg
   917  
   918  	inv := state.cfgInvocation()
   919  
   920  	// For Go versions 1.16 and above, `go list` accepts overlays directly via
   921  	// the -overlay flag. Set it, if it's available.
   922  	//
   923  	// The check for "list" is not necessarily required, but we should avoid
   924  	// getting the go version if possible.
   925  	if verb == "list" {
   926  		goVersion, err := state.getGoVersion()
   927  		if err != nil {
   928  			return nil, err
   929  		}
   930  		if goVersion >= 16 {
   931  			filename, cleanup, err := state.writeOverlays()
   932  			if err != nil {
   933  				return nil, err
   934  			}
   935  			defer cleanup()
   936  			inv.Overlay = filename
   937  		}
   938  	}
   939  	inv.Verb = verb
   940  	inv.Args = args
   941  	gocmdRunner := cfg.gocmdRunner
   942  	if gocmdRunner == nil {
   943  		gocmdRunner = &gocommand.Runner{}
   944  	}
   945  	stdout, stderr, friendlyErr, err := gocmdRunner.RunRaw(cfg.Context, inv)
   946  	if err != nil {
   947  		// Check for 'go' executable not being found.
   948  		if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
   949  			return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound)
   950  		}
   951  
   952  		exitErr, ok := err.(*exec.ExitError)
   953  		if !ok {
   954  			// Catastrophic error:
   955  			// - context cancellation
   956  			return nil, fmt.Errorf("couldn't run 'go': %w", err)
   957  		}
   958  
   959  		// Old go version?
   960  		if strings.Contains(stderr.String(), "flag provided but not defined") {
   961  			return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)}
   962  		}
   963  
   964  		// Related to #24854
   965  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "unexpected directory layout") {
   966  			return nil, friendlyErr
   967  		}
   968  
   969  		// Is there an error running the C compiler in cgo? This will be reported in the "Error" field
   970  		// and should be suppressed by go list -e.
   971  		//
   972  		// This condition is not perfect yet because the error message can include other error messages than runtime/cgo.
   973  		isPkgPathRune := func(r rune) bool {
   974  			// From https://golang.org/ref/spec#Import_declarations:
   975  			//    Implementation restriction: A compiler may restrict ImportPaths to non-empty strings
   976  			//    using only characters belonging to Unicode's L, M, N, P, and S general categories
   977  			//    (the Graphic characters without spaces) and may also exclude the
   978  			//    characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character U+FFFD.
   979  			return unicode.IsOneOf([]*unicode.RangeTable{unicode.L, unicode.M, unicode.N, unicode.P, unicode.S}, r) &&
   980  				!strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", r)
   981  		}
   982  		// golang/go#36770: Handle case where cmd/go prints module download messages before the error.
   983  		msg := stderr.String()
   984  		for strings.HasPrefix(msg, "go: downloading") {
   985  			msg = msg[strings.IndexRune(msg, '\n')+1:]
   986  		}
   987  		if len(stderr.String()) > 0 && strings.HasPrefix(stderr.String(), "# ") {
   988  			msg := msg[len("# "):]
   989  			if strings.HasPrefix(strings.TrimLeftFunc(msg, isPkgPathRune), "\n") {
   990  				return stdout, nil
   991  			}
   992  			// Treat pkg-config errors as a special case (golang.org/issue/36770).
   993  			if strings.HasPrefix(msg, "pkg-config") {
   994  				return stdout, nil
   995  			}
   996  		}
   997  
   998  		// This error only appears in stderr. See golang.org/cl/166398 for a fix in go list to show
   999  		// the error in the Err section of stdout in case -e option is provided.
  1000  		// This fix is provided for backwards compatibility.
  1001  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must be .go files") {
  1002  			output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
  1003  				strings.Trim(stderr.String(), "\n"))
  1004  			return bytes.NewBufferString(output), nil
  1005  		}
  1006  
  1007  		// Similar to the previous error, but currently lacks a fix in Go.
  1008  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must all be in one directory") {
  1009  			output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
  1010  				strings.Trim(stderr.String(), "\n"))
  1011  			return bytes.NewBufferString(output), nil
  1012  		}
  1013  
  1014  		// Backwards compatibility for Go 1.11 because 1.12 and 1.13 put the directory in the ImportPath.
  1015  		// If the package doesn't exist, put the absolute path of the directory into the error message,
  1016  		// as Go 1.13 list does.
  1017  		const noSuchDirectory = "no such directory"
  1018  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), noSuchDirectory) {
  1019  			errstr := stderr.String()
  1020  			abspath := strings.TrimSpace(errstr[strings.Index(errstr, noSuchDirectory)+len(noSuchDirectory):])
  1021  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
  1022  				abspath, strings.Trim(stderr.String(), "\n"))
  1023  			return bytes.NewBufferString(output), nil
  1024  		}
  1025  
  1026  		// Workaround for #29280: go list -e has incorrect behavior when an ad-hoc package doesn't exist.
  1027  		// Note that the error message we look for in this case is different that the one looked for above.
  1028  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no such file or directory") {
  1029  			output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
  1030  				strings.Trim(stderr.String(), "\n"))
  1031  			return bytes.NewBufferString(output), nil
  1032  		}
  1033  
  1034  		// Workaround for #34273. go list -e with GO111MODULE=on has incorrect behavior when listing a
  1035  		// directory outside any module.
  1036  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside available modules") {
  1037  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
  1038  				// TODO(matloob): command-line-arguments isn't correct here.
  1039  				"command-line-arguments", strings.Trim(stderr.String(), "\n"))
  1040  			return bytes.NewBufferString(output), nil
  1041  		}
  1042  
  1043  		// Another variation of the previous error
  1044  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside module root") {
  1045  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
  1046  				// TODO(matloob): command-line-arguments isn't correct here.
  1047  				"command-line-arguments", strings.Trim(stderr.String(), "\n"))
  1048  			return bytes.NewBufferString(output), nil
  1049  		}
  1050  
  1051  		// Workaround for an instance of golang.org/issue/26755: go list -e  will return a non-zero exit
  1052  		// status if there's a dependency on a package that doesn't exist. But it should return
  1053  		// a zero exit status and set an error on that package.
  1054  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no Go files in") {
  1055  			// Don't clobber stdout if `go list` actually returned something.
  1056  			if len(stdout.String()) > 0 {
  1057  				return stdout, nil
  1058  			}
  1059  			// try to extract package name from string
  1060  			stderrStr := stderr.String()
  1061  			var importPath string
  1062  			colon := strings.Index(stderrStr, ":")
  1063  			if colon > 0 && strings.HasPrefix(stderrStr, "go build ") {
  1064  				importPath = stderrStr[len("go build "):colon]
  1065  			}
  1066  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
  1067  				importPath, strings.Trim(stderrStr, "\n"))
  1068  			return bytes.NewBufferString(output), nil
  1069  		}
  1070  
  1071  		// Export mode entails a build.
  1072  		// If that build fails, errors appear on stderr
  1073  		// (despite the -e flag) and the Export field is blank.
  1074  		// Do not fail in that case.
  1075  		// The same is true if an ad-hoc package given to go list doesn't exist.
  1076  		// TODO(matloob): Remove these once we can depend on go list to exit with a zero status with -e even when
  1077  		// packages don't exist or a build fails.
  1078  		if !usesExportData(cfg) && !containsGoFile(args) {
  1079  			return nil, friendlyErr
  1080  		}
  1081  	}
  1082  	return stdout, nil
  1083  }
  1084  
  1085  // OverlayJSON is the format overlay files are expected to be in.
  1086  // The Replace map maps from overlaid paths to replacement paths:
  1087  // the Go command will forward all reads trying to open
  1088  // each overlaid path to its replacement path, or consider the overlaid
  1089  // path not to exist if the replacement path is empty.
  1090  //
  1091  // From golang/go#39958.
  1092  type OverlayJSON struct {
  1093  	Replace map[string]string `json:"replace,omitempty"`
  1094  }
  1095  
  1096  // writeOverlays writes out files for go list's -overlay flag, as described
  1097  // above.
  1098  func (state *golistState) writeOverlays() (filename string, cleanup func(), err error) {
  1099  	// Do nothing if there are no overlays in the config.
  1100  	if len(state.cfg.Overlay) == 0 {
  1101  		return "", func() {}, nil
  1102  	}
  1103  	dir, err := ioutil.TempDir("", "gopackages-*")
  1104  	if err != nil {
  1105  		return "", nil, err
  1106  	}
  1107  	// The caller must clean up this directory, unless this function returns an
  1108  	// error.
  1109  	cleanup = func() {
  1110  		os.RemoveAll(dir)
  1111  	}
  1112  	defer func() {
  1113  		if err != nil {
  1114  			cleanup()
  1115  		}
  1116  	}()
  1117  	overlays := map[string]string{}
  1118  	for k, v := range state.cfg.Overlay {
  1119  		// Create a unique filename for the overlaid files, to avoid
  1120  		// creating nested directories.
  1121  		noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "")
  1122  		f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator))
  1123  		if err != nil {
  1124  			return "", func() {}, err
  1125  		}
  1126  		if _, err := f.Write(v); err != nil {
  1127  			return "", func() {}, err
  1128  		}
  1129  		if err := f.Close(); err != nil {
  1130  			return "", func() {}, err
  1131  		}
  1132  		overlays[k] = f.Name()
  1133  	}
  1134  	b, err := json.Marshal(OverlayJSON{Replace: overlays})
  1135  	if err != nil {
  1136  		return "", func() {}, err
  1137  	}
  1138  	// Write out the overlay file that contains the filepath mappings.
  1139  	filename = filepath.Join(dir, "overlay.json")
  1140  	if err := ioutil.WriteFile(filename, b, 0665); err != nil {
  1141  		return "", func() {}, err
  1142  	}
  1143  	return filename, cleanup, nil
  1144  }
  1145  
  1146  func containsGoFile(s []string) bool {
  1147  	for _, f := range s {
  1148  		if strings.HasSuffix(f, ".go") {
  1149  			return true
  1150  		}
  1151  	}
  1152  	return false
  1153  }
  1154  
  1155  func cmdDebugStr(cmd *exec.Cmd) string {
  1156  	env := make(map[string]string)
  1157  	for _, kv := range cmd.Env {
  1158  		split := strings.SplitN(kv, "=", 2)
  1159  		k, v := split[0], split[1]
  1160  		env[k] = v
  1161  	}
  1162  
  1163  	var args []string
  1164  	for _, arg := range cmd.Args {
  1165  		quoted := strconv.Quote(arg)
  1166  		if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") {
  1167  			args = append(args, quoted)
  1168  		} else {
  1169  			args = append(args, arg)
  1170  		}
  1171  	}
  1172  	return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " "))
  1173  }
  1174  

View as plain text