gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/golang.org/x/tools/go/packages/golist.go (about)

     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  	"encoding/json"
    10  	"fmt"
    11  	"go/types"
    12  	"io/ioutil"
    13  	"log"
    14  	"os"
    15  	"os/exec"
    16  	"path/filepath"
    17  	"reflect"
    18  	"regexp"
    19  	"strconv"
    20  	"strings"
    21  	"sync"
    22  	"time"
    23  
    24  	"golang.org/x/tools/go/internal/packagesdriver"
    25  	"golang.org/x/tools/internal/gopathwalk"
    26  	"golang.org/x/tools/internal/semver"
    27  )
    28  
    29  // debug controls verbose logging.
    30  var debug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG"))
    31  
    32  // A goTooOldError reports that the go command
    33  // found by exec.LookPath is too old to use the new go list behavior.
    34  type goTooOldError struct {
    35  	error
    36  }
    37  
    38  // responseDeduper wraps a driverResponse, deduplicating its contents.
    39  type responseDeduper struct {
    40  	seenRoots    map[string]bool
    41  	seenPackages map[string]*Package
    42  	dr           *driverResponse
    43  }
    44  
    45  // init fills in r with a driverResponse.
    46  func (r *responseDeduper) init(dr *driverResponse) {
    47  	r.dr = dr
    48  	r.seenRoots = map[string]bool{}
    49  	r.seenPackages = map[string]*Package{}
    50  	for _, pkg := range dr.Packages {
    51  		r.seenPackages[pkg.ID] = pkg
    52  	}
    53  	for _, root := range dr.Roots {
    54  		r.seenRoots[root] = true
    55  	}
    56  }
    57  
    58  func (r *responseDeduper) addPackage(p *Package) {
    59  	if r.seenPackages[p.ID] != nil {
    60  		return
    61  	}
    62  	r.seenPackages[p.ID] = p
    63  	r.dr.Packages = append(r.dr.Packages, p)
    64  }
    65  
    66  func (r *responseDeduper) addRoot(id string) {
    67  	if r.seenRoots[id] {
    68  		return
    69  	}
    70  	r.seenRoots[id] = true
    71  	r.dr.Roots = append(r.dr.Roots, id)
    72  }
    73  
    74  // goListDriver uses the go list command to interpret the patterns and produce
    75  // the build system package structure.
    76  // See driver for more details.
    77  func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
    78  	var sizes types.Sizes
    79  	var sizeserr error
    80  	var sizeswg sync.WaitGroup
    81  	if cfg.Mode >= LoadTypes {
    82  		sizeswg.Add(1)
    83  		go func() {
    84  			sizes, sizeserr = getSizes(cfg)
    85  			sizeswg.Done()
    86  		}()
    87  	}
    88  
    89  	// Determine files requested in contains patterns
    90  	var containFiles []string
    91  	var packagesNamed []string
    92  	restPatterns := make([]string, 0, len(patterns))
    93  	// Extract file= and other [querytype]= patterns. Report an error if querytype
    94  	// doesn't exist.
    95  extractQueries:
    96  	for _, pattern := range patterns {
    97  		eqidx := strings.Index(pattern, "=")
    98  		if eqidx < 0 {
    99  			restPatterns = append(restPatterns, pattern)
   100  		} else {
   101  			query, value := pattern[:eqidx], pattern[eqidx+len("="):]
   102  			switch query {
   103  			case "file":
   104  				containFiles = append(containFiles, value)
   105  			case "pattern":
   106  				restPatterns = append(restPatterns, value)
   107  			case "name":
   108  				packagesNamed = append(packagesNamed, value)
   109  			case "": // not a reserved query
   110  				restPatterns = append(restPatterns, pattern)
   111  			default:
   112  				for _, rune := range query {
   113  					if rune < 'a' || rune > 'z' { // not a reserved query
   114  						restPatterns = append(restPatterns, pattern)
   115  						continue extractQueries
   116  					}
   117  				}
   118  				// Reject all other patterns containing "="
   119  				return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern)
   120  			}
   121  		}
   122  	}
   123  
   124  	// TODO(matloob): Remove the definition of listfunc and just use golistPackages once go1.12 is released.
   125  	var listfunc driver
   126  	var isFallback bool
   127  	listfunc = func(cfg *Config, words ...string) (*driverResponse, error) {
   128  		response, err := golistDriverCurrent(cfg, words...)
   129  		if _, ok := err.(goTooOldError); ok {
   130  			isFallback = true
   131  			listfunc = golistDriverFallback
   132  			return listfunc(cfg, words...)
   133  		}
   134  		listfunc = golistDriverCurrent
   135  		return response, err
   136  	}
   137  
   138  	response := &responseDeduper{}
   139  	var err error
   140  
   141  	// See if we have any patterns to pass through to go list. Zero initial
   142  	// patterns also requires a go list call, since it's the equivalent of
   143  	// ".".
   144  	if len(restPatterns) > 0 || len(patterns) == 0 {
   145  		dr, err := listfunc(cfg, restPatterns...)
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  		response.init(dr)
   150  	} else {
   151  		response.init(&driverResponse{})
   152  	}
   153  
   154  	sizeswg.Wait()
   155  	if sizeserr != nil {
   156  		return nil, sizeserr
   157  	}
   158  	// types.SizesFor always returns nil or a *types.StdSizes
   159  	response.dr.Sizes, _ = sizes.(*types.StdSizes)
   160  
   161  	var containsCandidates []string
   162  
   163  	if len(containFiles) != 0 {
   164  		if err := runContainsQueries(cfg, listfunc, isFallback, response, containFiles); err != nil {
   165  			return nil, err
   166  		}
   167  	}
   168  
   169  	if len(packagesNamed) != 0 {
   170  		if err := runNamedQueries(cfg, listfunc, response, packagesNamed); err != nil {
   171  			return nil, err
   172  		}
   173  	}
   174  
   175  	modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response.dr)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	if len(containFiles) > 0 {
   180  		containsCandidates = append(containsCandidates, modifiedPkgs...)
   181  		containsCandidates = append(containsCandidates, needPkgs...)
   182  	}
   183  
   184  	if len(needPkgs) > 0 {
   185  		addNeededOverlayPackages(cfg, listfunc, response, needPkgs)
   186  		if err != nil {
   187  			return nil, err
   188  		}
   189  	}
   190  	// Check candidate packages for containFiles.
   191  	if len(containFiles) > 0 {
   192  		for _, id := range containsCandidates {
   193  			pkg := response.seenPackages[id]
   194  			for _, f := range containFiles {
   195  				for _, g := range pkg.GoFiles {
   196  					if sameFile(f, g) {
   197  						response.addRoot(id)
   198  					}
   199  				}
   200  			}
   201  		}
   202  	}
   203  
   204  	return response.dr, nil
   205  }
   206  
   207  func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string) error {
   208  	dr, err := driver(cfg, pkgs...)
   209  	if err != nil {
   210  		return err
   211  	}
   212  	for _, pkg := range dr.Packages {
   213  		response.addPackage(pkg)
   214  	}
   215  	return nil
   216  }
   217  
   218  func runContainsQueries(cfg *Config, driver driver, isFallback bool, response *responseDeduper, queries []string) error {
   219  	for _, query := range queries {
   220  		// TODO(matloob): Do only one query per directory.
   221  		fdir := filepath.Dir(query)
   222  		// Pass absolute path of directory to go list so that it knows to treat it as a directory,
   223  		// not a package path.
   224  		pattern, err := filepath.Abs(fdir)
   225  		if err != nil {
   226  			return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err)
   227  		}
   228  		if isFallback {
   229  			pattern = "."
   230  			cfg.Dir = fdir
   231  		}
   232  
   233  		dirResponse, err := driver(cfg, pattern)
   234  		if err != nil {
   235  			return err
   236  		}
   237  		isRoot := make(map[string]bool, len(dirResponse.Roots))
   238  		for _, root := range dirResponse.Roots {
   239  			isRoot[root] = true
   240  		}
   241  		for _, pkg := range dirResponse.Packages {
   242  			// Add any new packages to the main set
   243  			// We don't bother to filter packages that will be dropped by the changes of roots,
   244  			// that will happen anyway during graph construction outside this function.
   245  			// Over-reporting packages is not a problem.
   246  			response.addPackage(pkg)
   247  			// if the package was not a root one, it cannot have the file
   248  			if !isRoot[pkg.ID] {
   249  				continue
   250  			}
   251  			for _, pkgFile := range pkg.GoFiles {
   252  				if filepath.Base(query) == filepath.Base(pkgFile) {
   253  					response.addRoot(pkg.ID)
   254  					break
   255  				}
   256  			}
   257  		}
   258  	}
   259  	return nil
   260  }
   261  
   262  // modCacheRegexp splits a path in a module cache into module, module version, and package.
   263  var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
   264  
   265  func runNamedQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error {
   266  	// calling `go env` isn't free; bail out if there's nothing to do.
   267  	if len(queries) == 0 {
   268  		return nil
   269  	}
   270  	// Determine which directories are relevant to scan.
   271  	roots, modRoot, err := roots(cfg)
   272  	if err != nil {
   273  		return err
   274  	}
   275  
   276  	// Scan the selected directories. Simple matches, from GOPATH/GOROOT
   277  	// or the local module, can simply be "go list"ed. Matches from the
   278  	// module cache need special treatment.
   279  	var matchesMu sync.Mutex
   280  	var simpleMatches, modCacheMatches []string
   281  	add := func(root gopathwalk.Root, dir string) {
   282  		// Walk calls this concurrently; protect the result slices.
   283  		matchesMu.Lock()
   284  		defer matchesMu.Unlock()
   285  
   286  		path := dir[len(root.Path)+1:]
   287  		if pathMatchesQueries(path, queries) {
   288  			switch root.Type {
   289  			case gopathwalk.RootModuleCache:
   290  				modCacheMatches = append(modCacheMatches, path)
   291  			case gopathwalk.RootCurrentModule:
   292  				// We'd need to read go.mod to find the full
   293  				// import path. Relative's easier.
   294  				rel, err := filepath.Rel(cfg.Dir, dir)
   295  				if err != nil {
   296  					// This ought to be impossible, since
   297  					// we found dir in the current module.
   298  					panic(err)
   299  				}
   300  				simpleMatches = append(simpleMatches, "./"+rel)
   301  			case gopathwalk.RootGOPATH, gopathwalk.RootGOROOT:
   302  				simpleMatches = append(simpleMatches, path)
   303  			}
   304  		}
   305  	}
   306  
   307  	startWalk := time.Now()
   308  	gopathwalk.Walk(roots, add, gopathwalk.Options{ModulesEnabled: modRoot != "", Debug: debug})
   309  	if debug {
   310  		log.Printf("%v for walk", time.Since(startWalk))
   311  	}
   312  
   313  	// Weird special case: the top-level package in a module will be in
   314  	// whatever directory the user checked the repository out into. It's
   315  	// more reasonable for that to not match the package name. So, if there
   316  	// are any Go files in the mod root, query it just to be safe.
   317  	if modRoot != "" {
   318  		rel, err := filepath.Rel(cfg.Dir, modRoot)
   319  		if err != nil {
   320  			panic(err) // See above.
   321  		}
   322  
   323  		files, err := ioutil.ReadDir(modRoot)
   324  		for _, f := range files {
   325  			if strings.HasSuffix(f.Name(), ".go") {
   326  				simpleMatches = append(simpleMatches, rel)
   327  				break
   328  			}
   329  		}
   330  	}
   331  
   332  	addResponse := func(r *driverResponse) {
   333  		for _, pkg := range r.Packages {
   334  			response.addPackage(pkg)
   335  			for _, name := range queries {
   336  				if pkg.Name == name {
   337  					response.addRoot(pkg.ID)
   338  					break
   339  				}
   340  			}
   341  		}
   342  	}
   343  
   344  	if len(simpleMatches) != 0 {
   345  		resp, err := driver(cfg, simpleMatches...)
   346  		if err != nil {
   347  			return err
   348  		}
   349  		addResponse(resp)
   350  	}
   351  
   352  	// Module cache matches are tricky. We want to avoid downloading new
   353  	// versions of things, so we need to use the ones present in the cache.
   354  	// go list doesn't accept version specifiers, so we have to write out a
   355  	// temporary module, and do the list in that module.
   356  	if len(modCacheMatches) != 0 {
   357  		// Collect all the matches, deduplicating by major version
   358  		// and preferring the newest.
   359  		type modInfo struct {
   360  			mod   string
   361  			major string
   362  		}
   363  		mods := make(map[modInfo]string)
   364  		var imports []string
   365  		for _, modPath := range modCacheMatches {
   366  			matches := modCacheRegexp.FindStringSubmatch(modPath)
   367  			mod, ver := filepath.ToSlash(matches[1]), matches[2]
   368  			importPath := filepath.ToSlash(filepath.Join(matches[1], matches[3]))
   369  
   370  			major := semver.Major(ver)
   371  			if prevVer, ok := mods[modInfo{mod, major}]; !ok || semver.Compare(ver, prevVer) > 0 {
   372  				mods[modInfo{mod, major}] = ver
   373  			}
   374  
   375  			imports = append(imports, importPath)
   376  		}
   377  
   378  		// Build the temporary module.
   379  		var gomod bytes.Buffer
   380  		gomod.WriteString("module modquery\nrequire (\n")
   381  		for mod, version := range mods {
   382  			gomod.WriteString("\t" + mod.mod + " " + version + "\n")
   383  		}
   384  		gomod.WriteString(")\n")
   385  
   386  		tmpCfg := *cfg
   387  
   388  		// We're only trying to look at stuff in the module cache, so
   389  		// disable the network. This should speed things up, and has
   390  		// prevented errors in at least one case, #28518.
   391  		tmpCfg.Env = append(append([]string{"GOPROXY=off"}, cfg.Env...))
   392  
   393  		var err error
   394  		tmpCfg.Dir, err = ioutil.TempDir("", "gopackages-modquery")
   395  		if err != nil {
   396  			return err
   397  		}
   398  		defer os.RemoveAll(tmpCfg.Dir)
   399  
   400  		if err := ioutil.WriteFile(filepath.Join(tmpCfg.Dir, "go.mod"), gomod.Bytes(), 0777); err != nil {
   401  			return fmt.Errorf("writing go.mod for module cache query: %v", err)
   402  		}
   403  
   404  		// Run the query, using the import paths calculated from the matches above.
   405  		resp, err := driver(&tmpCfg, imports...)
   406  		if err != nil {
   407  			return fmt.Errorf("querying module cache matches: %v", err)
   408  		}
   409  		addResponse(resp)
   410  	}
   411  
   412  	return nil
   413  }
   414  
   415  func getSizes(cfg *Config) (types.Sizes, error) {
   416  	return packagesdriver.GetSizesGolist(cfg.Context, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg))
   417  }
   418  
   419  // roots selects the appropriate paths to walk based on the passed-in configuration,
   420  // particularly the environment and the presence of a go.mod in cfg.Dir's parents.
   421  func roots(cfg *Config) ([]gopathwalk.Root, string, error) {
   422  	stdout, err := invokeGo(cfg, "env", "GOROOT", "GOPATH", "GOMOD")
   423  	if err != nil {
   424  		return nil, "", err
   425  	}
   426  
   427  	fields := strings.Split(stdout.String(), "\n")
   428  	if len(fields) != 4 || len(fields[3]) != 0 {
   429  		return nil, "", fmt.Errorf("go env returned unexpected output: %q", stdout.String())
   430  	}
   431  	goroot, gopath, gomod := fields[0], filepath.SplitList(fields[1]), fields[2]
   432  	var modDir string
   433  	if gomod != "" {
   434  		modDir = filepath.Dir(gomod)
   435  	}
   436  
   437  	var roots []gopathwalk.Root
   438  	// Always add GOROOT.
   439  	roots = append(roots, gopathwalk.Root{filepath.Join(goroot, "/src"), gopathwalk.RootGOROOT})
   440  	// If modules are enabled, scan the module dir.
   441  	if modDir != "" {
   442  		roots = append(roots, gopathwalk.Root{modDir, gopathwalk.RootCurrentModule})
   443  	}
   444  	// Add either GOPATH/src or GOPATH/pkg/mod, depending on module mode.
   445  	for _, p := range gopath {
   446  		if modDir != "" {
   447  			roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache})
   448  		} else {
   449  			roots = append(roots, gopathwalk.Root{filepath.Join(p, "/src"), gopathwalk.RootGOPATH})
   450  		}
   451  	}
   452  
   453  	return roots, modDir, nil
   454  }
   455  
   456  // These functions were copied from goimports. See further documentation there.
   457  
   458  // pathMatchesQueries is adapted from pkgIsCandidate.
   459  // TODO: is it reasonable to do Contains here, rather than an exact match on a path component?
   460  func pathMatchesQueries(path string, queries []string) bool {
   461  	lastTwo := lastTwoComponents(path)
   462  	for _, query := range queries {
   463  		if strings.Contains(lastTwo, query) {
   464  			return true
   465  		}
   466  		if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(query) {
   467  			lastTwo = lowerASCIIAndRemoveHyphen(lastTwo)
   468  			if strings.Contains(lastTwo, query) {
   469  				return true
   470  			}
   471  		}
   472  	}
   473  	return false
   474  }
   475  
   476  // lastTwoComponents returns at most the last two path components
   477  // of v, using either / or \ as the path separator.
   478  func lastTwoComponents(v string) string {
   479  	nslash := 0
   480  	for i := len(v) - 1; i >= 0; i-- {
   481  		if v[i] == '/' || v[i] == '\\' {
   482  			nslash++
   483  			if nslash == 2 {
   484  				return v[i:]
   485  			}
   486  		}
   487  	}
   488  	return v
   489  }
   490  
   491  func hasHyphenOrUpperASCII(s string) bool {
   492  	for i := 0; i < len(s); i++ {
   493  		b := s[i]
   494  		if b == '-' || ('A' <= b && b <= 'Z') {
   495  			return true
   496  		}
   497  	}
   498  	return false
   499  }
   500  
   501  func lowerASCIIAndRemoveHyphen(s string) (ret string) {
   502  	buf := make([]byte, 0, len(s))
   503  	for i := 0; i < len(s); i++ {
   504  		b := s[i]
   505  		switch {
   506  		case b == '-':
   507  			continue
   508  		case 'A' <= b && b <= 'Z':
   509  			buf = append(buf, b+('a'-'A'))
   510  		default:
   511  			buf = append(buf, b)
   512  		}
   513  	}
   514  	return string(buf)
   515  }
   516  
   517  // Fields must match go list;
   518  // see $GOROOT/src/cmd/go/internal/load/pkg.go.
   519  type jsonPackage struct {
   520  	ImportPath      string
   521  	Dir             string
   522  	Name            string
   523  	Export          string
   524  	GoFiles         []string
   525  	CompiledGoFiles []string
   526  	CFiles          []string
   527  	CgoFiles        []string
   528  	CXXFiles        []string
   529  	MFiles          []string
   530  	HFiles          []string
   531  	FFiles          []string
   532  	SFiles          []string
   533  	SwigFiles       []string
   534  	SwigCXXFiles    []string
   535  	SysoFiles       []string
   536  	Imports         []string
   537  	ImportMap       map[string]string
   538  	Deps            []string
   539  	TestGoFiles     []string
   540  	TestImports     []string
   541  	XTestGoFiles    []string
   542  	XTestImports    []string
   543  	ForTest         string // q in a "p [q.test]" package, else ""
   544  	DepOnly         bool
   545  
   546  	Error *jsonPackageError
   547  }
   548  
   549  type jsonPackageError struct {
   550  	ImportStack []string
   551  	Pos         string
   552  	Err         string
   553  }
   554  
   555  func otherFiles(p *jsonPackage) [][]string {
   556  	return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
   557  }
   558  
   559  // golistDriverCurrent uses the "go list" command to expand the
   560  // pattern words and return metadata for the specified packages.
   561  // dir may be "" and env may be nil, as per os/exec.Command.
   562  func golistDriverCurrent(cfg *Config, words ...string) (*driverResponse, error) {
   563  	// go list uses the following identifiers in ImportPath and Imports:
   564  	//
   565  	// 	"p"			-- importable package or main (command)
   566  	//      "q.test"		-- q's test executable
   567  	// 	"p [q.test]"		-- variant of p as built for q's test executable
   568  	//	"q_test [q.test]"	-- q's external test package
   569  	//
   570  	// The packages p that are built differently for a test q.test
   571  	// are q itself, plus any helpers used by the external test q_test,
   572  	// typically including "testing" and all its dependencies.
   573  
   574  	// Run "go list" for complete
   575  	// information on the specified packages.
   576  	buf, err := invokeGo(cfg, golistargs(cfg, words)...)
   577  	if err != nil {
   578  		return nil, err
   579  	}
   580  	seen := make(map[string]*jsonPackage)
   581  	// Decode the JSON and convert it to Package form.
   582  	var response driverResponse
   583  	for dec := json.NewDecoder(buf); dec.More(); {
   584  		p := new(jsonPackage)
   585  		if err := dec.Decode(p); err != nil {
   586  			return nil, fmt.Errorf("JSON decoding failed: %v", err)
   587  		}
   588  
   589  		if p.ImportPath == "" {
   590  			// The documentation for go list says that “[e]rroneous packages will have
   591  			// a non-empty ImportPath”. If for some reason it comes back empty, we
   592  			// prefer to error out rather than silently discarding data or handing
   593  			// back a package without any way to refer to it.
   594  			if p.Error != nil {
   595  				return nil, Error{
   596  					Pos: p.Error.Pos,
   597  					Msg: p.Error.Err,
   598  				}
   599  			}
   600  			return nil, fmt.Errorf("package missing import path: %+v", p)
   601  		}
   602  
   603  		if old, found := seen[p.ImportPath]; found {
   604  			if !reflect.DeepEqual(p, old) {
   605  				return nil, fmt.Errorf("go list repeated package %v with different values", p.ImportPath)
   606  			}
   607  			// skip the duplicate
   608  			continue
   609  		}
   610  		seen[p.ImportPath] = p
   611  
   612  		pkg := &Package{
   613  			Name:            p.Name,
   614  			ID:              p.ImportPath,
   615  			GoFiles:         absJoin(p.Dir, p.GoFiles, p.CgoFiles),
   616  			CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
   617  			OtherFiles:      absJoin(p.Dir, otherFiles(p)...),
   618  		}
   619  
   620  		// Workaround for https://golang.org/issue/28749.
   621  		// TODO(adonovan): delete before go1.12 release.
   622  		out := pkg.CompiledGoFiles[:0]
   623  		for _, f := range pkg.CompiledGoFiles {
   624  			if strings.HasSuffix(f, ".s") {
   625  				continue
   626  			}
   627  			out = append(out, f)
   628  		}
   629  		pkg.CompiledGoFiles = out
   630  
   631  		// Extract the PkgPath from the package's ID.
   632  		if i := strings.IndexByte(pkg.ID, ' '); i >= 0 {
   633  			pkg.PkgPath = pkg.ID[:i]
   634  		} else {
   635  			pkg.PkgPath = pkg.ID
   636  		}
   637  
   638  		if pkg.PkgPath == "unsafe" {
   639  			pkg.GoFiles = nil // ignore fake unsafe.go file
   640  		}
   641  
   642  		// Assume go list emits only absolute paths for Dir.
   643  		if p.Dir != "" && !filepath.IsAbs(p.Dir) {
   644  			log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir)
   645  		}
   646  
   647  		if p.Export != "" && !filepath.IsAbs(p.Export) {
   648  			pkg.ExportFile = filepath.Join(p.Dir, p.Export)
   649  		} else {
   650  			pkg.ExportFile = p.Export
   651  		}
   652  
   653  		// imports
   654  		//
   655  		// Imports contains the IDs of all imported packages.
   656  		// ImportsMap records (path, ID) only where they differ.
   657  		ids := make(map[string]bool)
   658  		for _, id := range p.Imports {
   659  			ids[id] = true
   660  		}
   661  		pkg.Imports = make(map[string]*Package)
   662  		for path, id := range p.ImportMap {
   663  			pkg.Imports[path] = &Package{ID: id} // non-identity import
   664  			delete(ids, id)
   665  		}
   666  		for id := range ids {
   667  			if id == "C" {
   668  				continue
   669  			}
   670  
   671  			pkg.Imports[id] = &Package{ID: id} // identity import
   672  		}
   673  		if !p.DepOnly {
   674  			response.Roots = append(response.Roots, pkg.ID)
   675  		}
   676  
   677  		// Work around for pre-go.1.11 versions of go list.
   678  		// TODO(matloob): they should be handled by the fallback.
   679  		// Can we delete this?
   680  		if len(pkg.CompiledGoFiles) == 0 {
   681  			pkg.CompiledGoFiles = pkg.GoFiles
   682  		}
   683  
   684  		if p.Error != nil {
   685  			pkg.Errors = append(pkg.Errors, Error{
   686  				Pos: p.Error.Pos,
   687  				Msg: p.Error.Err,
   688  			})
   689  		}
   690  
   691  		response.Packages = append(response.Packages, pkg)
   692  	}
   693  
   694  	return &response, nil
   695  }
   696  
   697  // absJoin absolutizes and flattens the lists of files.
   698  func absJoin(dir string, fileses ...[]string) (res []string) {
   699  	for _, files := range fileses {
   700  		for _, file := range files {
   701  			if !filepath.IsAbs(file) {
   702  				file = filepath.Join(dir, file)
   703  			}
   704  			res = append(res, file)
   705  		}
   706  	}
   707  	return res
   708  }
   709  
   710  func golistargs(cfg *Config, words []string) []string {
   711  	fullargs := []string{
   712  		"list", "-e", "-json", "-compiled",
   713  		fmt.Sprintf("-test=%t", cfg.Tests),
   714  		fmt.Sprintf("-export=%t", usesExportData(cfg)),
   715  		fmt.Sprintf("-deps=%t", cfg.Mode >= LoadImports),
   716  		// go list doesn't let you pass -test and -find together,
   717  		// probably because you'd just get the TestMain.
   718  		fmt.Sprintf("-find=%t", cfg.Mode < LoadImports && !cfg.Tests),
   719  	}
   720  	fullargs = append(fullargs, cfg.BuildFlags...)
   721  	fullargs = append(fullargs, "--")
   722  	fullargs = append(fullargs, words...)
   723  	return fullargs
   724  }
   725  
   726  // invokeGo returns the stdout of a go command invocation.
   727  func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) {
   728  	if debug {
   729  		defer func(start time.Time) { log.Printf("%s for %v", time.Since(start), cmdDebugStr(cfg, args...)) }(time.Now())
   730  	}
   731  	stdout := new(bytes.Buffer)
   732  	stderr := new(bytes.Buffer)
   733  	cmd := exec.CommandContext(cfg.Context, "go", args...)
   734  	// On darwin the cwd gets resolved to the real path, which breaks anything that
   735  	// expects the working directory to keep the original path, including the
   736  	// go command when dealing with modules.
   737  	// The Go stdlib has a special feature where if the cwd and the PWD are the
   738  	// same node then it trusts the PWD, so by setting it in the env for the child
   739  	// process we fix up all the paths returned by the go command.
   740  	cmd.Env = append(append([]string{}, cfg.Env...), "PWD="+cfg.Dir)
   741  	cmd.Dir = cfg.Dir
   742  	cmd.Stdout = stdout
   743  	cmd.Stderr = stderr
   744  	if err := cmd.Run(); err != nil {
   745  		exitErr, ok := err.(*exec.ExitError)
   746  		if !ok {
   747  			// Catastrophic error:
   748  			// - executable not found
   749  			// - context cancellation
   750  			return nil, fmt.Errorf("couldn't exec 'go %v': %s %T", args, err, err)
   751  		}
   752  
   753  		// Old go version?
   754  		if strings.Contains(stderr.String(), "flag provided but not defined") {
   755  			return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)}
   756  		}
   757  
   758  		// Export mode entails a build.
   759  		// If that build fails, errors appear on stderr
   760  		// (despite the -e flag) and the Export field is blank.
   761  		// Do not fail in that case.
   762  		// The same is true if an ad-hoc package given to go list doesn't exist.
   763  		// TODO(matloob): Remove these once we can depend on go list to exit with a zero status with -e even when
   764  		// packages don't exist or a build fails.
   765  		if !usesExportData(cfg) && !containsGoFile(args) {
   766  			return nil, fmt.Errorf("go %v: %s: %s", args, exitErr, stderr)
   767  		}
   768  	}
   769  
   770  	// As of writing, go list -export prints some non-fatal compilation
   771  	// errors to stderr, even with -e set. We would prefer that it put
   772  	// them in the Package.Error JSON (see https://golang.org/issue/26319).
   773  	// In the meantime, there's nowhere good to put them, but they can
   774  	// be useful for debugging. Print them if $GOPACKAGESPRINTGOLISTERRORS
   775  	// is set.
   776  	if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTGOLISTERRORS") != "" {
   777  		fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cfg, args...), stderr)
   778  	}
   779  
   780  	// debugging
   781  	if false {
   782  		fmt.Fprintf(os.Stderr, "%s stdout: <<%s>>\n", cmdDebugStr(cfg, args...), stdout)
   783  	}
   784  
   785  	return stdout, nil
   786  }
   787  
   788  func containsGoFile(s []string) bool {
   789  	for _, f := range s {
   790  		if strings.HasSuffix(f, ".go") {
   791  			return true
   792  		}
   793  	}
   794  	return false
   795  }
   796  
   797  func cmdDebugStr(cfg *Config, args ...string) string {
   798  	env := make(map[string]string)
   799  	for _, kv := range cfg.Env {
   800  		split := strings.Split(kv, "=")
   801  		k, v := split[0], split[1]
   802  		env[k] = v
   803  	}
   804  
   805  	return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v PWD=%v go %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["PWD"], args)
   806  }