// Copyright 2018 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package packagesimport ()// debug controls verbose logging.vardebug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG"))// A goTooOldError reports that the go command// found by exec.LookPath is too old to use the new go list behavior.typegoTooOldErrorstruct {error}// responseDeduper wraps a DriverResponse, deduplicating its contents.typeresponseDeduperstruct {seenRootsmap[string]boolseenPackagesmap[string]*Packagedr *DriverResponse}func () *responseDeduper {return &responseDeduper{dr: &DriverResponse{},seenRoots: map[string]bool{},seenPackages: map[string]*Package{}, }}// addAll fills in r with a DriverResponse.func ( *responseDeduper) ( *DriverResponse) {for , := range .Packages { .addPackage() }for , := range .Roots { .addRoot() } .dr.GoVersion = .GoVersion}func ( *responseDeduper) ( *Package) {if .seenPackages[.ID] != nil {return } .seenPackages[.ID] = .dr.Packages = append(.dr.Packages, )}func ( *responseDeduper) ( string) {if .seenRoots[] {return } .seenRoots[] = true .dr.Roots = append(.dr.Roots, )}typegolistStatestruct {cfg *Configctxcontext.Contextrunner *gocommand.Runner// overlay is the JSON file that encodes the Config.Overlay // mapping, used by 'go list -overlay=...'.overlaystringenvOncesync.OncegoEnvErrorerrorgoEnvmap[string]stringrootsOncesync.OncerootDirsErrorerrorrootDirsmap[string]stringgoVersionOncesync.OncegoVersionErrorerrorgoVersionint// The X in Go 1.X.// vendorDirs caches the (non)existence of vendor directories.vendorDirsmap[string]bool}// getEnv returns Go environment variables. Only specific variables are// populated -- computing all of them is slow.func ( *golistState) () (map[string]string, error) { .envOnce.Do(func() {var *bytes.Buffer , .goEnvError = .invokeGo("env", "-json", "GOMOD", "GOPATH")if .goEnvError != nil {return } .goEnv = make(map[string]string) := json.NewDecoder()if .goEnvError = .Decode(&.goEnv); .goEnvError != nil {return } })return .goEnv, .goEnvError}// mustGetEnv is a convenience function that can be used if getEnv has already succeeded.func ( *golistState) () map[string]string { , := .getEnv()if != nil {panic(fmt.Sprintf("mustGetEnv: %v", )) }return}// goListDriver uses the go list command to interpret the patterns and produce// the build system package structure.// See driver for more details.//// overlay is the JSON file that encodes the cfg.Overlay// mapping, used by 'go list -overlay=...'func ( *Config, *gocommand.Runner, string, []string) ( *DriverResponse, error) {// Make sure that any asynchronous go commands are killed when we return. := .Contextif == nil { = context.Background() } , := context.WithCancel()defer () := newDeduper() := &golistState{cfg: ,ctx: ,vendorDirs: map[string]bool{},overlay: ,runner: , }// Fill in response.Sizes asynchronously if necessary.if .Mode&NeedTypesSizes != 0 || .Mode&(NeedTypes|NeedTypesInfo) != 0 { := make(chanerror)gofunc() { , , := getSizesForArgs(, .cfgInvocation(), ) .dr.Compiler = .dr.Arch = <- }()deferfunc() {if := <-; != nil { = } }() }// Determine files requested in contains patternsvar []string := make([]string, 0, len())// Extract file= and other [querytype]= patterns. Report an error if querytype // doesn't exist.:for , := range { := strings.Index(, "=")if < 0 { = append(, ) } else { , := [:], [+len("="):]switch {case"file": = append(, )case"pattern": = append(, )case"": // not a reserved query = append(, )default:for , := range {if < 'a' || > 'z' { // not a reserved query = append(, )continue } }// Reject all other patterns containing "="returnnil, fmt.Errorf("invalid query type %q in query pattern %q", , ) } } }// See if we have any patterns to pass through to go list. Zero initial // patterns also requires a go list call, since it's the equivalent of // ".".iflen() > 0 || len() == 0 { , := .createDriverResponse(...)if != nil {returnnil, } .addAll() }iflen() != 0 {if := .runContainsQueries(, ); != nil {returnnil, } }// (We may yet return an error due to defer.)return .dr, nil}// abs returns an absolute representation of path, based on cfg.Dir.func ( *Config) ( string) (string, error) {iffilepath.IsAbs() {return , nil }// In case cfg.Dir is relative, pass it to filepath.Abs.returnfilepath.Abs(filepath.Join(.Dir, ))}func ( *golistState) ( *responseDeduper, []string) error {for , := range {// TODO(matloob): Do only one query per directory. := filepath.Dir()// Pass absolute path of directory to go list so that it knows to treat it as a directory, // not a package path. , := .cfg.abs()if != nil {returnfmt.Errorf("could not determine absolute path of file= query path %q: %v", , ) } , := .createDriverResponse()// If there was an error loading the package, or no packages are returned, // or the package is returned with errors, try to load the file as an // ad-hoc package. // Usually the error will appear in a returned package, but may not if we're // in module mode and the ad-hoc is located outside a module.if != nil || len(.Packages) == 0 || len(.Packages) == 1 && len(.Packages[0].GoFiles) == 0 &&len(.Packages[0].Errors) == 1 {varerrorif , = .adhocPackage(, ); != nil {return// return the original error } } := make(map[string]bool, len(.Roots))for , := range .Roots { [] = true }for , := range .Packages {// Add any new packages to the main set // We don't bother to filter packages that will be dropped by the changes of roots, // that will happen anyway during graph construction outside this function. // Over-reporting packages is not a problem. .addPackage()// if the package was not a root one, it cannot have the fileif ![.ID] {continue }for , := range .GoFiles {iffilepath.Base() == filepath.Base() { .addRoot(.ID)break } } } }returnnil}// adhocPackage attempts to load or construct an ad-hoc package for a given// query, if the original call to the driver produced inadequate results.func ( *golistState) (, string) (*DriverResponse, error) { , := .createDriverResponse()if != nil {returnnil, }// If we get nothing back from `go list`, // try to make this file into its own ad-hoc package. // TODO(rstambler): Should this check against the original response?iflen(.Packages) == 0 { .Packages = append(.Packages, &Package{ID: "command-line-arguments",PkgPath: ,GoFiles: []string{},CompiledGoFiles: []string{},Imports: make(map[string]*Package), }) .Roots = append(.Roots, "command-line-arguments") }// Handle special cases.iflen(.Packages) == 1 {// golang/go#33482: If this is a file= query for ad-hoc packages where // the file only exists on an overlay, and exists outside of a module, // add the file to the package and remove the errors.if .Packages[0].ID == "command-line-arguments" ||filepath.ToSlash(.Packages[0].PkgPath) == filepath.ToSlash() {iflen(.Packages[0].GoFiles) == 0 { := filepath.Join(, filepath.Base()) // avoid recomputing abspath// TODO(matloob): check if the file is outside of a root dir?for := range .cfg.Overlay {if == { .Packages[0].Errors = nil .Packages[0].GoFiles = []string{} .Packages[0].CompiledGoFiles = []string{} } } } } }return , nil}// Fields must match go list;// see $GOROOT/src/cmd/go/internal/load/pkg.go.typejsonPackagestruct {ImportPathstringDirstringNamestringTargetstringExportstringGoFiles []stringCompiledGoFiles []stringIgnoredGoFiles []stringIgnoredOtherFiles []stringEmbedPatterns []stringEmbedFiles []stringCFiles []stringCgoFiles []stringCXXFiles []stringMFiles []stringHFiles []stringFFiles []stringSFiles []stringSwigFiles []stringSwigCXXFiles []stringSysoFiles []stringImports []stringImportMapmap[string]stringDeps []stringModule *ModuleTestGoFiles []stringTestImports []stringXTestGoFiles []stringXTestImports []stringForTeststring// q in a "p [q.test]" package, else ""DepOnlyboolError *packagesinternal.PackageErrorDepsErrors []*packagesinternal.PackageError}typejsonPackageErrorstruct {ImportStack []stringPosstringErrstring}func ( *jsonPackage) [][]string {return [][]string{.CFiles, .CXXFiles, .MFiles, .HFiles, .FFiles, .SFiles, .SwigFiles, .SwigCXXFiles, .SysoFiles}}// createDriverResponse uses the "go list" command to expand the pattern// words and return a response for the specified packages.func ( *golistState) ( ...string) (*DriverResponse, error) {// go list uses the following identifiers in ImportPath and Imports: // // "p" -- importable package or main (command) // "q.test" -- q's test executable // "p [q.test]" -- variant of p as built for q's test executable // "q_test [q.test]" -- q's external test package // // The packages p that are built differently for a test q.test // are q itself, plus any helpers used by the external test q_test, // typically including "testing" and all its dependencies.// Run "go list" for complete // information on the specified packages. , := .getGoVersion()if != nil {returnnil, } , := .invokeGo("list", golistargs(.cfg, , )...)if != nil {returnnil, } := make(map[string]*jsonPackage) := make(map[string]*Package) := make(map[string][]Error)// Decode the JSON and convert it to Package form. := &DriverResponse{GoVersion: , }for := json.NewDecoder(); .More(); { := new(jsonPackage)if := .Decode(); != nil {returnnil, fmt.Errorf("JSON decoding failed: %v", ) }if .ImportPath == "" {// The documentation for go list says that “[e]rroneous packages will have // a non-empty ImportPath”. If for some reason it comes back empty, we // prefer to error out rather than silently discarding data or handing // back a package without any way to refer to it.if .Error != nil {returnnil, Error{Pos: .Error.Pos,Msg: .Error.Err, } }returnnil, fmt.Errorf("package missing import path: %+v", ) }// Work around https://golang.org/issue/33157: // go list -e, when given an absolute path, will find the package contained at // that directory. But when no package exists there, it will return a fake package // with an error and the ImportPath set to the absolute path provided to go list. // Try to convert that absolute path to what its package path would be if it's // contained in a known module or GOPATH entry. This will allow the package to be // properly "reclaimed" when overlays are processed.iffilepath.IsAbs(.ImportPath) && .Error != nil { , , := .getPkgPath(.ImportPath)if != nil {returnnil, }if { .ImportPath = } }if , := [.ImportPath]; {// If one version of the package has an error, and the other doesn't, assume // that this is a case where go list is reporting a fake dependency variant // of the imported package: When a package tries to invalidly import another // package, go list emits a variant of the imported package (with the same // import path, but with an error on it, and the package will have a // DepError set on it). An example of when this can happen is for imports of // main packages: main packages can not be imported, but they may be // separately matched and listed by another pattern. // See golang.org/issue/36188 for more details.// The plan is that eventually, hopefully in Go 1.15, the error will be // reported on the importing package rather than the duplicate "fake" // version of the imported package. Once all supported versions of Go // have the new behavior this logic can be deleted. // TODO(matloob): delete the workaround logic once all supported versions of // Go return the errors on the proper package.// There should be exactly one version of a package that doesn't have an // error.if .Error == nil && .Error == nil {if !reflect.DeepEqual(, ) {returnnil, fmt.Errorf("internal error: go list gives conflicting information for package %v", .ImportPath) }continue }// Determine if this package's error needs to be bubbled up. // This is a hack, and we expect for go list to eventually set the error // on the package.if .Error != nil {varstringifstrings.Contains(.Error.Err, "not an importable package") { = "not an importable package" } elseifstrings.Contains(.Error.Err, "use of internal package") && strings.Contains(.Error.Err, "not allowed") { = "use of internal package not allowed" }if != "" {iflen(.Error.ImportStack) < 1 {returnnil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, ) } := .Error.ImportStack[len(.Error.ImportStack)-1]if == .ImportPath {// Using an older version of Go which put this package itself on top of import // stack, instead of the importer. Look for importer in second from top // position.iflen(.Error.ImportStack) < 2 {returnnil, fmt.Errorf(`internal error: go list gave a %q error with an import stack without importing package`, ) } = .Error.ImportStack[len(.Error.ImportStack)-2] } [] = append([], Error{Pos: .Error.Pos,Msg: .Error.Err,Kind: ListError, }) } }// Make sure that if there's a version of the package without an error, // that's the one reported to the user.if .Error == nil {continue }// This package will replace the old one at the end of the loop. } [.ImportPath] = := &Package{Name: .Name,ID: .ImportPath,Dir: .Dir,Target: .Target,GoFiles: absJoin(.Dir, .GoFiles, .CgoFiles),CompiledGoFiles: absJoin(.Dir, .CompiledGoFiles),OtherFiles: absJoin(.Dir, otherFiles()...),EmbedFiles: absJoin(.Dir, .EmbedFiles),EmbedPatterns: absJoin(.Dir, .EmbedPatterns),IgnoredFiles: absJoin(.Dir, .IgnoredGoFiles, .IgnoredOtherFiles),ForTest: .ForTest,depsErrors: .DepsErrors,Module: .Module, }if (.cfg.Mode&typecheckCgo) != 0 && len(.CgoFiles) != 0 {iflen(.CompiledGoFiles) > len(.GoFiles) {// We need the cgo definitions, which are in the first // CompiledGoFile after the non-cgo ones. This is a hack but there // isn't currently a better way to find it. We also need the pure // Go files and unprocessed cgo files, all of which are already // in pkg.GoFiles. := .CompiledGoFiles[len(.GoFiles)] .CompiledGoFiles = append([]string{}, .GoFiles...) } else {// golang/go#38990: go list silently fails to do cgo processing .CompiledGoFiles = nil .Errors = append(.Errors, Error{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.",Kind: ListError, }) } }// Work around https://golang.org/issue/28749: // cmd/go puts assembly, C, and C++ files in CompiledGoFiles. // Remove files from CompiledGoFiles that are non-go files // (or are not files that look like they are from the cache).iflen(.CompiledGoFiles) > 0 { := .CompiledGoFiles[:0]for , := range .CompiledGoFiles {if := filepath.Ext(); != ".go" && != "" { // ext == "" means the file is from the cache, so probably cgo-processed filecontinue } = append(, ) } .CompiledGoFiles = }// Extract the PkgPath from the package's ID.if := strings.IndexByte(.ID, ' '); >= 0 { .PkgPath = .ID[:] } else { .PkgPath = .ID }if .PkgPath == "unsafe" { .CompiledGoFiles = nil// ignore fake unsafe.go file (#59929) } elseiflen(.CompiledGoFiles) == 0 {// Work around for pre-go.1.11 versions of go list. // TODO(matloob): they should be handled by the fallback. // Can we delete this? .CompiledGoFiles = .GoFiles }// Assume go list emits only absolute paths for Dir.if .Dir != "" && !filepath.IsAbs(.Dir) {log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", .Dir) }if .Export != "" && !filepath.IsAbs(.Export) { .ExportFile = filepath.Join(.Dir, .Export) } else { .ExportFile = .Export }// imports // // Imports contains the IDs of all imported packages. // ImportsMap records (path, ID) only where they differ. := make(map[string]bool)for , := range .Imports { [] = true } .Imports = make(map[string]*Package)for , := range .ImportMap { .Imports[] = &Package{ID: } // non-identity importdelete(, ) }for := range {if == "C" {continue } .Imports[] = &Package{ID: } // identity import }if !.DepOnly { .Roots = append(.Roots, .ID) }// Temporary work-around for golang/go#39986. Parse filenames out of // error messages. This happens if there are unrecoverable syntax // errors in the source, so we can't match on a specific error message. // // TODO(rfindley): remove this heuristic, in favor of considering // InvalidGoFiles from the list driver.if := .Error; != nil && .shouldAddFilenameFromError() { := func( string) bool { := strings.Split(, ":")iflen() < 1 {returnfalse } := strings.TrimSpace([0])if == "" {returnfalse }if !filepath.IsAbs() { = filepath.Join(.cfg.Dir, ) } , := os.Stat()if == nil {returnfalse } .CompiledGoFiles = append(.CompiledGoFiles, ) .GoFiles = append(.GoFiles, )returntrue } := (.Pos)// In some cases, go list only reports the error position in the // error text, not the error position. One such case is when the // file's package name is a keyword (see golang.org/issue/39763).if ! { (.Err) } }if .Error != nil { := strings.TrimSpace(.Error.Err) // Trim to work around golang.org/issue/32363.// Address golang.org/issue/35964 by appending import stack to error message.if == "import cycle not allowed" && len(.Error.ImportStack) != 0 { += fmt.Sprintf(": import stack: %v", .Error.ImportStack) } .Errors = append(.Errors, Error{Pos: .Error.Pos,Msg: ,Kind: ListError, }) } [.ID] = }for , := range {if , := []; { .Errors = append(.Errors, ...) } }for , := range { .Packages = append(.Packages, ) }sort.Slice(.Packages, func(, int) bool { return .Packages[].ID < .Packages[].ID })return , nil}func ( *golistState) ( *jsonPackage) bool {iflen(.GoFiles) > 0 || len(.CompiledGoFiles) > 0 {returnfalse } , := .getGoVersion()if != nil {returnfalse }// On Go 1.14 and earlier, only add filenames from errors if the import stack is empty. // The import stack behaves differently for these versions than newer Go versions.if < 15 {returnlen(.Error.ImportStack) == 0 }// On Go 1.15 and later, only parse filenames out of error if there's no import stack, // or the current package is at the top of the import stack. This is not guaranteed // to work perfectly, but should avoid some cases where files in errors don't belong to this // package.returnlen(.Error.ImportStack) == 0 || .Error.ImportStack[len(.Error.ImportStack)-1] == .ImportPath}// getGoVersion returns the effective minor version of the go command.func ( *golistState) () (int, error) { .goVersionOnce.Do(func() { .goVersion, .goVersionError = gocommand.GoVersion(.ctx, .cfgInvocation(), .runner) })return .goVersion, .goVersionError}// getPkgPath finds the package path of a directory if it's relative to a root// directory.func ( *golistState) ( string) (string, bool, error) {if !filepath.IsAbs() {panic("non-absolute dir passed to getPkgPath") } , := .determineRootDirs()if != nil {return"", false, }for , := range {// Make sure that the directory is in the module, // to avoid creating a path relative to another module.if !strings.HasPrefix(, ) {continue }// TODO(matloob): This doesn't properly handle symlinks. , := filepath.Rel(, )if != nil {continue }if != "" {// We choose only one root even though the directory even it can belong in multiple modules // or GOPATH entries. This is okay because we only need to work with absolute dirs when a // file is missing from disk, for instance when gopls calls go/packages in an overlay. // Once the file is saved, gopls, or the next invocation of the tool will get the correct // result straight from golist. // TODO(matloob): Implement module tiebreaking?returnpath.Join(, filepath.ToSlash()), true, nil }returnfilepath.ToSlash(), true, nil }return"", false, nil}// absJoin absolutizes and flattens the lists of files.func ( string, ...[]string) ( []string) {for , := range {for , := range {if !filepath.IsAbs() { = filepath.Join(, ) } = append(, ) } }return}func ( *Config, int) string {if < 19 {return"-json" }var []string := make(map[string]bool) := func( ...string) {for , := range {if ![] { [] = true = append(, ) } } } ("Name", "ImportPath", "Error") // These fields are always neededif .Mode&NeedFiles != 0 || .Mode&(NeedTypes|NeedTypesInfo) != 0 { ("Dir", "GoFiles", "IgnoredGoFiles", "IgnoredOtherFiles", "CFiles","CgoFiles", "CXXFiles", "MFiles", "HFiles", "FFiles", "SFiles","SwigFiles", "SwigCXXFiles", "SysoFiles")if .Tests { ("TestGoFiles", "XTestGoFiles") } }if .Mode&(NeedTypes|NeedTypesInfo) != 0 {// CompiledGoFiles seems to be required for the test case TestCgoNoSyntax, // even when -compiled isn't passed in. // TODO(#52435): Should we make the test ask for -compiled, or automatically // request CompiledGoFiles in certain circumstances? ("Dir", "CompiledGoFiles") }if .Mode&NeedCompiledGoFiles != 0 { ("Dir", "CompiledGoFiles", "Export") }if .Mode&NeedImports != 0 {// When imports are requested, DepOnly is used to distinguish between packages // explicitly requested and transitive imports of those packages. ("DepOnly", "Imports", "ImportMap")if .Tests { ("TestImports", "XTestImports") } }if .Mode&NeedDeps != 0 { ("DepOnly") }ifusesExportData() {// Request Dir in the unlikely case Export is not absolute. ("Dir", "Export") }if .Mode&NeedForTest != 0 { ("ForTest") }if .Mode&needInternalDepsErrors != 0 { ("DepsErrors") }if .Mode&NeedModule != 0 { ("Module") }if .Mode&NeedEmbedFiles != 0 { ("EmbedFiles") }if .Mode&NeedEmbedPatterns != 0 { ("EmbedPatterns") }if .Mode&NeedTarget != 0 { ("Target") }return"-json=" + strings.Join(, ",")}func ( *Config, []string, int) []string {const = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo := []string{"-e", jsonFlag(, ),fmt.Sprintf("-compiled=%t", .Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0),fmt.Sprintf("-test=%t", .Tests),fmt.Sprintf("-export=%t", usesExportData()),fmt.Sprintf("-deps=%t", .Mode&NeedImports != 0),// go list doesn't let you pass -test and -find together, // probably because you'd just get the TestMain.fmt.Sprintf("-find=%t", !.Tests && .Mode& == 0 && !usesExportData()), }// golang/go#60456: with go1.21 and later, go list serves pgo variants, which // can be costly to compute and may result in redundant processing for the // caller. Disable these variants. If someone wants to add e.g. a NeedPGO // mode flag, that should be a separate proposal.if >= 21 { = append(, "-pgo=off") } = append(, .BuildFlags...) = append(, "--") = append(, ...)return}// cfgInvocation returns an Invocation that reflects cfg's settings.func ( *golistState) () gocommand.Invocation { := .cfgreturngocommand.Invocation{BuildFlags: .BuildFlags,CleanEnv: .Env != nil,Env: .Env,Logf: .Logf,WorkingDir: .Dir,Overlay: .overlay, }}// invokeGo returns the stdout of a go command invocation.func ( *golistState) ( string, ...string) (*bytes.Buffer, error) { := .cfg := .cfgInvocation() .Verb = .Args = , , , := .runner.RunRaw(.Context, )if != nil {// Check for 'go' executable not being found.if , := .(*exec.Error); && .Err == exec.ErrNotFound {returnnil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound) } , := .(*exec.ExitError)if ! {// Catastrophic error: // - context cancellationreturnnil, fmt.Errorf("couldn't run 'go': %w", ) }// Old go version?ifstrings.Contains(.String(), "flag provided but not defined") {returnnil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", , )} }// Related to #24854iflen(.String()) > 0 && strings.Contains(.String(), "unexpected directory layout") {returnnil, }// Return an error if 'go list' failed due to missing tools in // $GOROOT/pkg/tool/$GOOS_$GOARCH (#69606).iflen(.String()) > 0 && strings.Contains(.String(), `go: no such tool`) {returnnil, }// Is there an error running the C compiler in cgo? This will be reported in the "Error" field // and should be suppressed by go list -e. // // This condition is not perfect yet because the error message can include other error messages than runtime/cgo. := func( rune) bool {// From https://golang.org/ref/spec#Import_declarations: // Implementation restriction: A compiler may restrict ImportPaths to non-empty strings // using only characters belonging to Unicode's L, M, N, P, and S general categories // (the Graphic characters without spaces) and may also exclude the // characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character U+FFFD.returnunicode.IsOneOf([]*unicode.RangeTable{unicode.L, unicode.M, unicode.N, unicode.P, unicode.S}, ) && !strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", ) }// golang/go#36770: Handle case where cmd/go prints module download messages before the error. := .String()forstrings.HasPrefix(, "go: downloading") { = [strings.IndexRune(, '\n')+1:] }iflen(.String()) > 0 && strings.HasPrefix(.String(), "# ") { := [len("# "):]ifstrings.HasPrefix(strings.TrimLeftFunc(, ), "\n") {return , nil }// Treat pkg-config errors as a special case (golang.org/issue/36770).ifstrings.HasPrefix(, "pkg-config") {return , nil } }// This error only appears in stderr. See golang.org/cl/166398 for a fix in go list to show // the error in the Err section of stdout in case -e option is provided. // This fix is provided for backwards compatibility.iflen(.String()) > 0 && strings.Contains(.String(), "named files must be .go files") { := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,strings.Trim(.String(), "\n"))returnbytes.NewBufferString(), nil }// Similar to the previous error, but currently lacks a fix in Go.iflen(.String()) > 0 && strings.Contains(.String(), "named files must all be in one directory") { := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,strings.Trim(.String(), "\n"))returnbytes.NewBufferString(), nil }// Backwards compatibility for Go 1.11 because 1.12 and 1.13 put the directory in the ImportPath. // If the package doesn't exist, put the absolute path of the directory into the error message, // as Go 1.13 list does.const = "no such directory"iflen(.String()) > 0 && strings.Contains(.String(), ) { := .String() := strings.TrimSpace([strings.Index(, )+len():]) := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`, , strings.Trim(.String(), "\n"))returnbytes.NewBufferString(), nil }// Workaround for #29280: go list -e has incorrect behavior when an ad-hoc package doesn't exist. // Note that the error message we look for in this case is different that the one looked for above.iflen(.String()) > 0 && strings.Contains(.String(), "no such file or directory") { := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,strings.Trim(.String(), "\n"))returnbytes.NewBufferString(), nil }// Workaround for #34273. go list -e with GO111MODULE=on has incorrect behavior when listing a // directory outside any module.iflen(.String()) > 0 && strings.Contains(.String(), "outside available modules") { := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,// TODO(matloob): command-line-arguments isn't correct here."command-line-arguments", strings.Trim(.String(), "\n"))returnbytes.NewBufferString(), nil }// Another variation of the previous erroriflen(.String()) > 0 && strings.Contains(.String(), "outside module root") { := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,// TODO(matloob): command-line-arguments isn't correct here."command-line-arguments", strings.Trim(.String(), "\n"))returnbytes.NewBufferString(), nil }// Workaround for an instance of golang.org/issue/26755: go list -e will return a non-zero exit // status if there's a dependency on a package that doesn't exist. But it should return // a zero exit status and set an error on that package.iflen(.String()) > 0 && strings.Contains(.String(), "no Go files in") {// Don't clobber stdout if `go list` actually returned something.iflen(.String()) > 0 {return , nil }// try to extract package name from string := .String()varstring := strings.Index(, ":")if > 0 && strings.HasPrefix(, "go build ") { = [len("go build "):] } := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`, , strings.Trim(, "\n"))returnbytes.NewBufferString(), nil }// Export mode entails a build. // If that build fails, errors appear on stderr // (despite the -e flag) and the Export field is blank. // Do not fail in that case. // The same is true if an ad-hoc package given to go list doesn't exist. // TODO(matloob): Remove these once we can depend on go list to exit with a zero status with -e even when // packages don't exist or a build fails.if !usesExportData() && !containsGoFile() {returnnil, } }return , nil}func ( []string) bool {for , := range {ifstrings.HasSuffix(, ".go") {returntrue } }returnfalse}func ( *exec.Cmd) string { := make(map[string]string)for , := range .Env { := strings.SplitN(, "=", 2) , := [0], [1] [] = }var []stringfor , := range .Args { := strconv.Quote()if [1:len()-1] != || strings.Contains(, " ") { = append(, ) } else { = append(, ) } }returnfmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", ["GOROOT"], ["GOPATH"], ["GO111MODULE"], ["GOPROXY"], ["PWD"], strings.Join(, " "))}// getSizesForArgs queries 'go list' for the appropriate// Compiler and GOARCH arguments to pass to [types.SizesFor].func ( context.Context, gocommand.Invocation, *gocommand.Runner) (string, string, error) { .Verb = "list" .Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"} , , , := .RunRaw(, )var , stringif != nil { := .Error()ifstrings.Contains(, "cannot find main module") ||strings.Contains(, "go.mod file not found") {// User's running outside of a module. // All bets are off. Get GOARCH and guess compiler is gc. // TODO(matloob): Is this a problem in practice? .Verb = "env" .Args = []string{"GOARCH"} , := .Run(, )if != nil {return"", "", } = strings.TrimSpace(.String()) = "gc" } elseif != nil {return"", "", } else {// This should be unreachable, but be defensive // in case RunRaw's error results are inconsistent.return"", "", } } else { := strings.Fields(.String())iflen() < 2 {return"", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>", .String(), .String()) } = [0] = [1] }return , , nil}
The pages are generated with Goldsv0.7.6. (GOOS=linux GOARCH=amd64)