src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

golist.go (39208B)


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