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.
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 "golang.org/x/xerrors"
32 // debug controls verbose logging.
33 var debug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG"))
35 // A goTooOldError reports that the go command
36 // found by exec.LookPath is too old to use the new go list behavior.
37 type goTooOldError struct {
41 // responseDeduper wraps a driverResponse, deduplicating its contents.
42 type responseDeduper struct {
43 seenRoots map[string]bool
44 seenPackages map[string]*Package
48 func newDeduper() *responseDeduper {
49 return &responseDeduper{
50 dr: &driverResponse{},
51 seenRoots: map[string]bool{},
52 seenPackages: map[string]*Package{},
56 // addAll fills in r with a driverResponse.
57 func (r *responseDeduper) addAll(dr *driverResponse) {
58 for _, pkg := range dr.Packages {
61 for _, root := range dr.Roots {
66 func (r *responseDeduper) addPackage(p *Package) {
67 if r.seenPackages[p.ID] != nil {
70 r.seenPackages[p.ID] = p
71 r.dr.Packages = append(r.dr.Packages, p)
74 func (r *responseDeduper) addRoot(id string) {
78 r.seenRoots[id] = true
79 r.dr.Roots = append(r.dr.Roots, id)
82 type golistState struct {
88 goEnv map[string]string
92 rootDirs map[string]string
94 goVersionOnce sync.Once
96 goVersion int // The X in Go 1.X.
98 // vendorDirs caches the (non)existence of vendor directories.
99 vendorDirs map[string]bool
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() {
107 b, state.goEnvError = state.invokeGo("env", "-json", "GOMOD", "GOPATH")
108 if state.goEnvError != nil {
112 state.goEnv = make(map[string]string)
113 decoder := json.NewDecoder(b)
114 if state.goEnvError = decoder.Decode(&state.goEnv); state.goEnvError != nil {
118 return state.goEnv, state.goEnvError
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()
125 panic(fmt.Sprintf("mustGetEnv: %v", err))
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()
139 ctx, cancel := context.WithCancel(parentCtx)
142 response := newDeduper()
144 state := &golistState{
147 vendorDirs: map[string]bool{},
150 // Fill in response.Sizes asynchronously if necessary.
152 var sizeswg sync.WaitGroup
153 if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
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)
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
170 for _, pattern := range patterns {
171 eqidx := strings.Index(pattern, "=")
173 restPatterns = append(restPatterns, pattern)
175 query, value := pattern[:eqidx], pattern[eqidx+len("="):]
178 containFiles = append(containFiles, value)
180 restPatterns = append(restPatterns, value)
181 case "": // not a reserved query
182 restPatterns = append(restPatterns, pattern)
184 for _, rune := range query {
185 if rune < 'a' || rune > 'z' { // not a reserved query
186 restPatterns = append(restPatterns, pattern)
187 continue extractQueries
190 // Reject all other patterns containing "="
191 return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern)
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
199 if len(restPatterns) > 0 || len(patterns) == 0 {
200 dr, err := state.createDriverResponse(restPatterns...)
207 if len(containFiles) != 0 {
208 if err := state.runContainsQueries(response, containFiles); err != nil {
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)
221 var containsCandidates []string
222 if len(containFiles) > 0 {
223 containsCandidates = append(containsCandidates, modifiedPkgs...)
224 containsCandidates = append(containsCandidates, needPkgs...)
226 if err := state.addNeededOverlayPackages(response, needPkgs); err != nil {
229 // Check candidate packages for containFiles.
230 if len(containFiles) > 0 {
231 for _, id := range containsCandidates {
232 pkg, ok := response.seenPackages[id]
234 response.addPackage(&Package{
238 Msg: fmt.Sprintf("package %s expected but not seen", id),
243 for _, f := range containFiles {
244 for _, g := range pkg.GoFiles {
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]
262 if match(pkg.PkgPath) {
263 response.addRoot(pkg.ID)
273 return response.dr, nil
276 func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error {
280 dr, err := state.createDriverResponse(pkgs...)
284 for _, pkg := range dr.Packages {
285 response.addPackage(pkg)
287 _, needPkgs, err := state.processGolistOverlay(response)
291 return state.addNeededOverlayPackages(response, needPkgs)
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)
302 return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err)
304 dirResponse, err := state.createDriverResponse(pattern)
306 // If there was an error loading the package, or the package is returned
307 // with errors, try to load the file as an ad-hoc package.
308 // Usually the error will appear in a returned package, but may not if we're
309 // in module mode and the ad-hoc is located outside a module.
310 if err != nil || len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].GoFiles) == 0 &&
311 len(dirResponse.Packages[0].Errors) == 1 {
313 if dirResponse, queryErr = state.adhocPackage(pattern, query); queryErr != nil {
314 return err // return the original error
317 isRoot := make(map[string]bool, len(dirResponse.Roots))
318 for _, root := range dirResponse.Roots {
321 for _, pkg := range dirResponse.Packages {
322 // Add any new packages to the main set
323 // We don't bother to filter packages that will be dropped by the changes of roots,
324 // that will happen anyway during graph construction outside this function.
325 // Over-reporting packages is not a problem.
326 response.addPackage(pkg)
327 // if the package was not a root one, it cannot have the file
331 for _, pkgFile := range pkg.GoFiles {
332 if filepath.Base(query) == filepath.Base(pkgFile) {
333 response.addRoot(pkg.ID)
342 // adhocPackage attempts to load or construct an ad-hoc package for a given
343 // query, if the original call to the driver produced inadequate results.
344 func (state *golistState) adhocPackage(pattern, query string) (*driverResponse, error) {
345 response, err := state.createDriverResponse(query)
349 // If we get nothing back from `go list`,
350 // try to make this file into its own ad-hoc package.
351 // TODO(rstambler): Should this check against the original response?
352 if len(response.Packages) == 0 {
353 response.Packages = append(response.Packages, &Package{
354 ID: "command-line-arguments",
356 GoFiles: []string{query},
357 CompiledGoFiles: []string{query},
358 Imports: make(map[string]*Package),
360 response.Roots = append(response.Roots, "command-line-arguments")
362 // Handle special cases.
363 if len(response.Packages) == 1 {
364 // golang/go#33482: If this is a file= query for ad-hoc packages where
365 // the file only exists on an overlay, and exists outside of a module,
366 // add the file to the package and remove the errors.
367 if response.Packages[0].ID == "command-line-arguments" ||
368 filepath.ToSlash(response.Packages[0].PkgPath) == filepath.ToSlash(query) {
369 if len(response.Packages[0].GoFiles) == 0 {
370 filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath
371 // TODO(matloob): check if the file is outside of a root dir?
372 for path := range state.cfg.Overlay {
373 if path == filename {
374 response.Packages[0].Errors = nil
375 response.Packages[0].GoFiles = []string{path}
376 response.Packages[0].CompiledGoFiles = []string{path}
385 // Fields must match go list;
386 // see $GOROOT/src/cmd/go/internal/load/pkg.go.
387 type jsonPackage struct {
393 CompiledGoFiles []string
394 IgnoredGoFiles []string
395 IgnoredOtherFiles []string
404 SwigCXXFiles []string
407 ImportMap map[string]string
412 XTestGoFiles []string
413 XTestImports []string
414 ForTest string // q in a "p [q.test]" package, else ""
417 Error *packagesinternal.PackageError
418 DepsErrors []*packagesinternal.PackageError
421 type jsonPackageError struct {
427 func otherFiles(p *jsonPackage) [][]string {
428 return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
431 // createDriverResponse uses the "go list" command to expand the pattern
432 // words and return a response for the specified packages.
433 func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) {
434 // go list uses the following identifiers in ImportPath and Imports:
436 // "p" -- importable package or main (command)
437 // "q.test" -- q's test executable
438 // "p [q.test]" -- variant of p as built for q's test executable
439 // "q_test [q.test]" -- q's external test package
441 // The packages p that are built differently for a test q.test
442 // are q itself, plus any helpers used by the external test q_test,
443 // typically including "testing" and all its dependencies.
445 // Run "go list" for complete
446 // information on the specified packages.
447 buf, err := state.invokeGo("list", golistargs(state.cfg, words)...)
451 seen := make(map[string]*jsonPackage)
452 pkgs := make(map[string]*Package)
453 additionalErrors := make(map[string][]Error)
454 // Decode the JSON and convert it to Package form.
455 var response driverResponse
456 for dec := json.NewDecoder(buf); dec.More(); {
457 p := new(jsonPackage)
458 if err := dec.Decode(p); err != nil {
459 return nil, fmt.Errorf("JSON decoding failed: %v", err)
462 if p.ImportPath == "" {
463 // The documentation for go list says that “[e]rroneous packages will have
464 // a non-empty ImportPath”. If for some reason it comes back empty, we
465 // prefer to error out rather than silently discarding data or handing
466 // back a package without any way to refer to it.
473 return nil, fmt.Errorf("package missing import path: %+v", p)
476 // Work around https://golang.org/issue/33157:
477 // go list -e, when given an absolute path, will find the package contained at
478 // that directory. But when no package exists there, it will return a fake package
479 // with an error and the ImportPath set to the absolute path provided to go list.
480 // Try to convert that absolute path to what its package path would be if it's
481 // contained in a known module or GOPATH entry. This will allow the package to be
482 // properly "reclaimed" when overlays are processed.
483 if filepath.IsAbs(p.ImportPath) && p.Error != nil {
484 pkgPath, ok, err := state.getPkgPath(p.ImportPath)
489 p.ImportPath = pkgPath
493 if old, found := seen[p.ImportPath]; found {
494 // If one version of the package has an error, and the other doesn't, assume
495 // that this is a case where go list is reporting a fake dependency variant
496 // of the imported package: When a package tries to invalidly import another
497 // package, go list emits a variant of the imported package (with the same
498 // import path, but with an error on it, and the package will have a
499 // DepError set on it). An example of when this can happen is for imports of
500 // main packages: main packages can not be imported, but they may be
501 // separately matched and listed by another pattern.
502 // See golang.org/issue/36188 for more details.
504 // The plan is that eventually, hopefully in Go 1.15, the error will be
505 // reported on the importing package rather than the duplicate "fake"
506 // version of the imported package. Once all supported versions of Go
507 // have the new behavior this logic can be deleted.
508 // TODO(matloob): delete the workaround logic once all supported versions of
509 // Go return the errors on the proper package.
511 // There should be exactly one version of a package that doesn't have an
513 if old.Error == nil && p.Error == nil {
514 if !reflect.DeepEqual(p, old) {
515 return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
520 // Determine if this package's error needs to be bubbled up.
521 // This is a hack, and we expect for go list to eventually set the error
523 if old.Error != nil {
525 if strings.Contains(old.Error.Err, "not an importable package") {
526 errkind = "not an importable package"
527 } else if strings.Contains(old.Error.Err, "use of internal package") && strings.Contains(old.Error.Err, "not allowed") {
528 errkind = "use of internal package not allowed"
531 if len(old.Error.ImportStack) < 1 {
532 return nil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, errkind)
534 importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-1]
535 if importingPkg == old.ImportPath {
536 // Using an older version of Go which put this package itself on top of import
537 // stack, instead of the importer. Look for importer in second from top
539 if len(old.Error.ImportStack) < 2 {
540 return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack without importing package`, errkind)
542 importingPkg = old.Error.ImportStack[len(old.Error.ImportStack)-2]
544 additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{
552 // Make sure that if there's a version of the package without an error,
553 // that's the one reported to the user.
554 if old.Error == nil {
558 // This package will replace the old one at the end of the loop.
560 seen[p.ImportPath] = p
565 GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
566 CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
567 OtherFiles: absJoin(p.Dir, otherFiles(p)...),
568 IgnoredFiles: absJoin(p.Dir, p.IgnoredGoFiles, p.IgnoredOtherFiles),
570 depsErrors: p.DepsErrors,
574 if (state.cfg.Mode&typecheckCgo) != 0 && len(p.CgoFiles) != 0 {
575 if len(p.CompiledGoFiles) > len(p.GoFiles) {
576 // We need the cgo definitions, which are in the first
577 // CompiledGoFile after the non-cgo ones. This is a hack but there
578 // isn't currently a better way to find it. We also need the pure
579 // Go files and unprocessed cgo files, all of which are already
581 cgoTypes := p.CompiledGoFiles[len(p.GoFiles)]
582 pkg.CompiledGoFiles = append([]string{cgoTypes}, pkg.GoFiles...)
584 // golang/go#38990: go list silently fails to do cgo processing
585 pkg.CompiledGoFiles = nil
586 pkg.Errors = append(pkg.Errors, Error{
587 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.",
593 // Work around https://golang.org/issue/28749:
594 // cmd/go puts assembly, C, and C++ files in CompiledGoFiles.
595 // Filter out any elements of CompiledGoFiles that are also in OtherFiles.
596 // We have to keep this workaround in place until go1.12 is a distant memory.
597 if len(pkg.OtherFiles) > 0 {
598 other := make(map[string]bool, len(pkg.OtherFiles))
599 for _, f := range pkg.OtherFiles {
603 out := pkg.CompiledGoFiles[:0]
604 for _, f := range pkg.CompiledGoFiles {
610 pkg.CompiledGoFiles = out
613 // Extract the PkgPath from the package's ID.
614 if i := strings.IndexByte(pkg.ID, ' '); i >= 0 {
615 pkg.PkgPath = pkg.ID[:i]
620 if pkg.PkgPath == "unsafe" {
621 pkg.GoFiles = nil // ignore fake unsafe.go file
624 // Assume go list emits only absolute paths for Dir.
625 if p.Dir != "" && !filepath.IsAbs(p.Dir) {
626 log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir)
629 if p.Export != "" && !filepath.IsAbs(p.Export) {
630 pkg.ExportFile = filepath.Join(p.Dir, p.Export)
632 pkg.ExportFile = p.Export
637 // Imports contains the IDs of all imported packages.
638 // ImportsMap records (path, ID) only where they differ.
639 ids := make(map[string]bool)
640 for _, id := range p.Imports {
643 pkg.Imports = make(map[string]*Package)
644 for path, id := range p.ImportMap {
645 pkg.Imports[path] = &Package{ID: id} // non-identity import
648 for id := range ids {
653 pkg.Imports[id] = &Package{ID: id} // identity import
656 response.Roots = append(response.Roots, pkg.ID)
659 // Work around for pre-go.1.11 versions of go list.
660 // TODO(matloob): they should be handled by the fallback.
661 // Can we delete this?
662 if len(pkg.CompiledGoFiles) == 0 {
663 pkg.CompiledGoFiles = pkg.GoFiles
666 // Temporary work-around for golang/go#39986. Parse filenames out of
667 // error messages. This happens if there are unrecoverable syntax
668 // errors in the source, so we can't match on a specific error message.
669 if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
670 addFilenameFromPos := func(pos string) bool {
671 split := strings.Split(pos, ":")
675 filename := strings.TrimSpace(split[0])
679 if !filepath.IsAbs(filename) {
680 filename = filepath.Join(state.cfg.Dir, filename)
682 info, _ := os.Stat(filename)
686 pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename)
687 pkg.GoFiles = append(pkg.GoFiles, filename)
690 found := addFilenameFromPos(err.Pos)
691 // In some cases, go list only reports the error position in the
692 // error text, not the error position. One such case is when the
693 // file's package name is a keyword (see golang.org/issue/39763).
695 addFilenameFromPos(err.Err)
700 msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363.
701 // Address golang.org/issue/35964 by appending import stack to error message.
702 if msg == "import cycle not allowed" && len(p.Error.ImportStack) != 0 {
703 msg += fmt.Sprintf(": import stack: %v", p.Error.ImportStack)
705 pkg.Errors = append(pkg.Errors, Error{
715 for id, errs := range additionalErrors {
716 if p, ok := pkgs[id]; ok {
717 p.Errors = append(p.Errors, errs...)
720 for _, pkg := range pkgs {
721 response.Packages = append(response.Packages, pkg)
723 sort.Slice(response.Packages, func(i, j int) bool { return response.Packages[i].ID < response.Packages[j].ID })
725 return &response, nil
728 func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool {
729 if len(p.GoFiles) > 0 || len(p.CompiledGoFiles) > 0 {
733 goV, err := state.getGoVersion()
738 // On Go 1.14 and earlier, only add filenames from errors if the import stack is empty.
739 // The import stack behaves differently for these versions than newer Go versions.
741 return len(p.Error.ImportStack) == 0
744 // On Go 1.15 and later, only parse filenames out of error if there's no import stack,
745 // or the current package is at the top of the import stack. This is not guaranteed
746 // to work perfectly, but should avoid some cases where files in errors don't belong to this
748 return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath
751 func (state *golistState) getGoVersion() (int, error) {
752 state.goVersionOnce.Do(func() {
753 state.goVersion, state.goVersionError = gocommand.GoVersion(state.ctx, state.cfgInvocation(), state.cfg.gocmdRunner)
755 return state.goVersion, state.goVersionError
758 // getPkgPath finds the package path of a directory if it's relative to a root
760 func (state *golistState) getPkgPath(dir string) (string, bool, error) {
761 absDir, err := filepath.Abs(dir)
763 return "", false, err
765 roots, err := state.determineRootDirs()
767 return "", false, err
770 for rdir, rpath := range roots {
771 // Make sure that the directory is in the module,
772 // to avoid creating a path relative to another module.
773 if !strings.HasPrefix(absDir, rdir) {
776 // TODO(matloob): This doesn't properly handle symlinks.
777 r, err := filepath.Rel(rdir, dir)
782 // We choose only one root even though the directory even it can belong in multiple modules
783 // or GOPATH entries. This is okay because we only need to work with absolute dirs when a
784 // file is missing from disk, for instance when gopls calls go/packages in an overlay.
785 // Once the file is saved, gopls, or the next invocation of the tool will get the correct
786 // result straight from golist.
787 // TODO(matloob): Implement module tiebreaking?
788 return path.Join(rpath, filepath.ToSlash(r)), true, nil
790 return filepath.ToSlash(r), true, nil
792 return "", false, nil
795 // absJoin absolutizes and flattens the lists of files.
796 func absJoin(dir string, fileses ...[]string) (res []string) {
797 for _, files := range fileses {
798 for _, file := range files {
799 if !filepath.IsAbs(file) {
800 file = filepath.Join(dir, file)
802 res = append(res, file)
808 func golistargs(cfg *Config, words []string) []string {
809 const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
810 fullargs := []string{
812 fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0),
813 fmt.Sprintf("-test=%t", cfg.Tests),
814 fmt.Sprintf("-export=%t", usesExportData(cfg)),
815 fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0),
816 // go list doesn't let you pass -test and -find together,
817 // probably because you'd just get the TestMain.
818 fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0),
820 fullargs = append(fullargs, cfg.BuildFlags...)
821 fullargs = append(fullargs, "--")
822 fullargs = append(fullargs, words...)
826 // cfgInvocation returns an Invocation that reflects cfg's settings.
827 func (state *golistState) cfgInvocation() gocommand.Invocation {
829 return gocommand.Invocation{
830 BuildFlags: cfg.BuildFlags,
831 ModFile: cfg.modFile,
832 ModFlag: cfg.modFlag,
833 CleanEnv: cfg.Env != nil,
840 // invokeGo returns the stdout of a go command invocation.
841 func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
844 inv := state.cfgInvocation()
846 // For Go versions 1.16 and above, `go list` accepts overlays directly via
847 // the -overlay flag. Set it, if it's available.
849 // The check for "list" is not necessarily required, but we should avoid
850 // getting the go version if possible.
852 goVersion, err := state.getGoVersion()
857 filename, cleanup, err := state.writeOverlays()
862 inv.Overlay = filename
867 gocmdRunner := cfg.gocmdRunner
868 if gocmdRunner == nil {
869 gocmdRunner = &gocommand.Runner{}
871 stdout, stderr, friendlyErr, err := gocmdRunner.RunRaw(cfg.Context, inv)
873 // Check for 'go' executable not being found.
874 if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
875 return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound)
878 exitErr, ok := err.(*exec.ExitError)
880 // Catastrophic error:
881 // - context cancellation
882 return nil, xerrors.Errorf("couldn't run 'go': %w", err)
886 if strings.Contains(stderr.String(), "flag provided but not defined") {
887 return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)}
891 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "unexpected directory layout") {
892 return nil, friendlyErr
895 // Is there an error running the C compiler in cgo? This will be reported in the "Error" field
896 // and should be suppressed by go list -e.
898 // This condition is not perfect yet because the error message can include other error messages than runtime/cgo.
899 isPkgPathRune := func(r rune) bool {
900 // From https://golang.org/ref/spec#Import_declarations:
901 // Implementation restriction: A compiler may restrict ImportPaths to non-empty strings
902 // using only characters belonging to Unicode's L, M, N, P, and S general categories
903 // (the Graphic characters without spaces) and may also exclude the
904 // characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character U+FFFD.
905 return unicode.IsOneOf([]*unicode.RangeTable{unicode.L, unicode.M, unicode.N, unicode.P, unicode.S}, r) &&
906 !strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", r)
908 // golang/go#36770: Handle case where cmd/go prints module download messages before the error.
909 msg := stderr.String()
910 for strings.HasPrefix(msg, "go: downloading") {
911 msg = msg[strings.IndexRune(msg, '\n')+1:]
913 if len(stderr.String()) > 0 && strings.HasPrefix(stderr.String(), "# ") {
914 msg := msg[len("# "):]
915 if strings.HasPrefix(strings.TrimLeftFunc(msg, isPkgPathRune), "\n") {
918 // Treat pkg-config errors as a special case (golang.org/issue/36770).
919 if strings.HasPrefix(msg, "pkg-config") {
924 // This error only appears in stderr. See golang.org/cl/166398 for a fix in go list to show
925 // the error in the Err section of stdout in case -e option is provided.
926 // This fix is provided for backwards compatibility.
927 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must be .go files") {
928 output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
929 strings.Trim(stderr.String(), "\n"))
930 return bytes.NewBufferString(output), nil
933 // Similar to the previous error, but currently lacks a fix in Go.
934 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must all be in one directory") {
935 output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
936 strings.Trim(stderr.String(), "\n"))
937 return bytes.NewBufferString(output), nil
940 // Backwards compatibility for Go 1.11 because 1.12 and 1.13 put the directory in the ImportPath.
941 // If the package doesn't exist, put the absolute path of the directory into the error message,
942 // as Go 1.13 list does.
943 const noSuchDirectory = "no such directory"
944 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), noSuchDirectory) {
945 errstr := stderr.String()
946 abspath := strings.TrimSpace(errstr[strings.Index(errstr, noSuchDirectory)+len(noSuchDirectory):])
947 output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
948 abspath, strings.Trim(stderr.String(), "\n"))
949 return bytes.NewBufferString(output), nil
952 // Workaround for #29280: go list -e has incorrect behavior when an ad-hoc package doesn't exist.
953 // Note that the error message we look for in this case is different that the one looked for above.
954 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no such file or directory") {
955 output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
956 strings.Trim(stderr.String(), "\n"))
957 return bytes.NewBufferString(output), nil
960 // Workaround for #34273. go list -e with GO111MODULE=on has incorrect behavior when listing a
961 // directory outside any module.
962 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside available modules") {
963 output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
964 // TODO(matloob): command-line-arguments isn't correct here.
965 "command-line-arguments", strings.Trim(stderr.String(), "\n"))
966 return bytes.NewBufferString(output), nil
969 // Another variation of the previous error
970 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside module root") {
971 output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
972 // TODO(matloob): command-line-arguments isn't correct here.
973 "command-line-arguments", strings.Trim(stderr.String(), "\n"))
974 return bytes.NewBufferString(output), nil
977 // Workaround for an instance of golang.org/issue/26755: go list -e will return a non-zero exit
978 // status if there's a dependency on a package that doesn't exist. But it should return
979 // a zero exit status and set an error on that package.
980 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no Go files in") {
981 // Don't clobber stdout if `go list` actually returned something.
982 if len(stdout.String()) > 0 {
985 // try to extract package name from string
986 stderrStr := stderr.String()
987 var importPath string
988 colon := strings.Index(stderrStr, ":")
989 if colon > 0 && strings.HasPrefix(stderrStr, "go build ") {
990 importPath = stderrStr[len("go build "):colon]
992 output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
993 importPath, strings.Trim(stderrStr, "\n"))
994 return bytes.NewBufferString(output), nil
997 // Export mode entails a build.
998 // If that build fails, errors appear on stderr
999 // (despite the -e flag) and the Export field is blank.
1000 // Do not fail in that case.
1001 // The same is true if an ad-hoc package given to go list doesn't exist.
1002 // TODO(matloob): Remove these once we can depend on go list to exit with a zero status with -e even when
1003 // packages don't exist or a build fails.
1004 if !usesExportData(cfg) && !containsGoFile(args) {
1005 return nil, friendlyErr
1011 // OverlayJSON is the format overlay files are expected to be in.
1012 // The Replace map maps from overlaid paths to replacement paths:
1013 // the Go command will forward all reads trying to open
1014 // each overlaid path to its replacement path, or consider the overlaid
1015 // path not to exist if the replacement path is empty.
1017 // From golang/go#39958.
1018 type OverlayJSON struct {
1019 Replace map[string]string `json:"replace,omitempty"`
1022 // writeOverlays writes out files for go list's -overlay flag, as described
1024 func (state *golistState) writeOverlays() (filename string, cleanup func(), err error) {
1025 // Do nothing if there are no overlays in the config.
1026 if len(state.cfg.Overlay) == 0 {
1027 return "", func() {}, nil
1029 dir, err := ioutil.TempDir("", "gopackages-*")
1033 // The caller must clean up this directory, unless this function returns an
1043 overlays := map[string]string{}
1044 for k, v := range state.cfg.Overlay {
1045 // Create a unique filename for the overlaid files, to avoid
1046 // creating nested directories.
1047 noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "")
1048 f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator))
1050 return "", func() {}, err
1052 if _, err := f.Write(v); err != nil {
1053 return "", func() {}, err
1055 if err := f.Close(); err != nil {
1056 return "", func() {}, err
1058 overlays[k] = f.Name()
1060 b, err := json.Marshal(OverlayJSON{Replace: overlays})
1062 return "", func() {}, err
1064 // Write out the overlay file that contains the filepath mappings.
1065 filename = filepath.Join(dir, "overlay.json")
1066 if err := ioutil.WriteFile(filename, b, 0665); err != nil {
1067 return "", func() {}, err
1069 return filename, cleanup, nil
1072 func containsGoFile(s []string) bool {
1073 for _, f := range s {
1074 if strings.HasSuffix(f, ".go") {
1081 func cmdDebugStr(cmd *exec.Cmd) string {
1082 env := make(map[string]string)
1083 for _, kv := range cmd.Env {
1084 split := strings.SplitN(kv, "=", 2)
1085 k, v := split[0], split[1]
1090 for _, arg := range cmd.Args {
1091 quoted := strconv.Quote(arg)
1092 if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") {
1093 args = append(args, quoted)
1095 args = append(args, arg)
1098 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, " "))