package packages
import (
exec
)
var debug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG"))
type goTooOldError struct {
error
}
type responseDeduper struct {
seenRoots map[string]bool
seenPackages map[string]*Package
dr *driverResponse
}
func () *responseDeduper {
return &responseDeduper{
dr: &driverResponse{},
seenRoots: map[string]bool{},
seenPackages: map[string]*Package{},
}
}
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, )
}
type golistState struct {
cfg *Config
ctx context.Context
envOnce sync.Once
goEnvError error
goEnv map[string]string
rootsOnce sync.Once
rootDirsError error
rootDirs map[string]string
goVersionOnce sync.Once
goVersionError error
goVersion int
vendorDirs map[string]bool
}
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
}
func ( *golistState) () map[string]string {
, := .getEnv()
if != nil {
panic(fmt.Sprintf("mustGetEnv: %v", ))
}
return
}
func ( *Config, ...string) (*driverResponse, error) {
:= .Context
if == nil {
= context.Background()
}
, := context.WithCancel()
defer ()
:= newDeduper()
:= &golistState{
cfg: ,
ctx: ,
vendorDirs: map[string]bool{},
}
var error
var sync.WaitGroup
if .Mode&NeedTypesSizes != 0 || .Mode&NeedTypes != 0 {
.Add(1)
go func() {
var types.Sizes
, = packagesdriver.GetSizesGolist(, .cfgInvocation(), .gocmdRunner)
.dr.Sizes, _ = .(*types.StdSizes)
.Done()
}()
}
var []string
:= make([]string, 0, len())
:
for , := range {
:= strings.Index(, "=")
if < 0 {
= append(, )
} else {
, := [:], [+len("="):]
switch {
case "file":
= append(, )
case "pattern":
= append(, )
case "":
= append(, )
default:
for , := range {
if < 'a' || > 'z' {
= append(, )
continue
}
}
return nil, fmt.Errorf("invalid query type %q in query pattern %q", , )
}
}
}
if len() > 0 || len() == 0 {
, := .createDriverResponse(...)
if != nil {
return nil,
}
.addAll()
}
if len() != 0 {
if := .runContainsQueries(, ); != nil {
return nil,
}
}
if , := .getGoVersion(); == nil && < 16 {
, , := .processGolistOverlay()
if != nil {
return nil,
}
var []string
if len() > 0 {
= append(, ...)
= append(, ...)
}
if := .addNeededOverlayPackages(, ); != nil {
return nil,
}
if len() > 0 {
for , := range {
, := .seenPackages[]
if ! {
.addPackage(&Package{
ID: ,
Errors: []Error{{
Kind: ListError,
Msg: fmt.Sprintf("package %s expected but not seen", ),
}},
})
continue
}
for , := range {
for , := range .GoFiles {
if sameFile(, ) {
.addRoot()
}
}
}
}
}
for , := range {
:= matchPattern()
for , := range {
, := .seenPackages[]
if ! {
continue
}
if (.PkgPath) {
.addRoot(.ID)
}
}
}
}
.Wait()
if != nil {
return nil,
}
return .dr, nil
}
func ( *golistState) ( *responseDeduper, []string) error {
if len() == 0 {
return nil
}
, := .createDriverResponse(...)
if != nil {
return
}
for , := range .Packages {
.addPackage()
}
, , := .processGolistOverlay()
if != nil {
return
}
return .(, )
}
func ( *golistState) ( *responseDeduper, []string) error {
for , := range {
:= filepath.Dir()
, := filepath.Abs()
if != nil {
return fmt.Errorf("could not determine absolute path of file= query path %q: %v", , )
}
, := .createDriverResponse()
if != nil || len(.Packages) == 0 || len(.Packages) == 1 && len(.Packages[0].GoFiles) == 0 &&
len(.Packages[0].Errors) == 1 {
var error
if , = .adhocPackage(, ); != nil {
return
}
}
:= make(map[string]bool, len(.Roots))
for , := range .Roots {
[] = true
}
for , := range .Packages {
.addPackage()
if ![.ID] {
continue
}
for , := range .GoFiles {
if filepath.Base() == filepath.Base() {
.addRoot(.ID)
break
}
}
}
}
return nil
}
func ( *golistState) (, string) (*driverResponse, error) {
, := .createDriverResponse()
if != nil {
return nil,
}
if len(.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")
}
if len(.Packages) == 1 {
if .Packages[0].ID == "command-line-arguments" ||
filepath.ToSlash(.Packages[0].PkgPath) == filepath.ToSlash() {
if len(.Packages[0].GoFiles) == 0 {
:= filepath.Join(, filepath.Base())
for := range .cfg.Overlay {
if == {
.Packages[0].Errors = nil
.Packages[0].GoFiles = []string{}
.Packages[0].CompiledGoFiles = []string{}
}
}
}
}
}
return , nil
}
type jsonPackage struct {
ImportPath string
Dir string
Name string
Export string
GoFiles []string
CompiledGoFiles []string
IgnoredGoFiles []string
IgnoredOtherFiles []string
EmbedPatterns []string
EmbedFiles []string
CFiles []string
CgoFiles []string
CXXFiles []string
MFiles []string
HFiles []string
FFiles []string
SFiles []string
SwigFiles []string
SwigCXXFiles []string
SysoFiles []string
Imports []string
ImportMap map[string]string
Deps []string
Module *Module
TestGoFiles []string
TestImports []string
XTestGoFiles []string
XTestImports []string
ForTest string
DepOnly bool
Error *packagesinternal.PackageError
DepsErrors []*packagesinternal.PackageError
}
type jsonPackageError struct {
ImportStack []string
Pos string
Err string
}
func ( *jsonPackage) [][]string {
return [][]string{.CFiles, .CXXFiles, .MFiles, .HFiles, .FFiles, .SFiles, .SwigFiles, .SwigCXXFiles, .SysoFiles}
}
func ( *golistState) ( ...string) (*driverResponse, error) {
, := .getGoVersion()
if != nil {
return nil,
}
, := .invokeGo("list", golistargs(.cfg, , )...)
if != nil {
return nil,
}
:= make(map[string]*jsonPackage)
:= make(map[string]*Package)
:= make(map[string][]Error)
:= &driverResponse{
GoVersion: ,
}
for := json.NewDecoder(); .More(); {
:= new(jsonPackage)
if := .Decode(); != nil {
return nil, fmt.Errorf("JSON decoding failed: %v", )
}
if .ImportPath == "" {
if .Error != nil {
return nil, Error{
Pos: .Error.Pos,
Msg: .Error.Err,
}
}
return nil, fmt.Errorf("package missing import path: %+v", )
}
if filepath.IsAbs(.ImportPath) && .Error != nil {
, , := .getPkgPath(.ImportPath)
if != nil {
return nil,
}
if {
.ImportPath =
}
}
if , := [.ImportPath]; {
if .Error == nil && .Error == nil {
if !reflect.DeepEqual(, ) {
return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", .ImportPath)
}
continue
}
if .Error != nil {
var string
if strings.Contains(.Error.Err, "not an importable package") {
= "not an importable package"
} else if strings.Contains(.Error.Err, "use of internal package") && strings.Contains(.Error.Err, "not allowed") {
= "use of internal package not allowed"
}
if != "" {
if len(.Error.ImportStack) < 1 {
return nil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, )
}
:= .Error.ImportStack[len(.Error.ImportStack)-1]
if == .ImportPath {
if len(.Error.ImportStack) < 2 {
return nil, 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,
})
}
}
if .Error == nil {
continue
}
}
[.ImportPath] =
:= &Package{
Name: .Name,
ID: .ImportPath,
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 {
if len(.CompiledGoFiles) > len(.GoFiles) {
:= .CompiledGoFiles[len(.GoFiles)]
.CompiledGoFiles = append([]string{}, .GoFiles...)
} else {
.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,
})
}
}
if len(.CompiledGoFiles) > 0 {
:= .CompiledGoFiles[:0]
for , := range .CompiledGoFiles {
if := filepath.Ext(); != ".go" && != "" {
continue
}
= append(, )
}
.CompiledGoFiles =
}
if := strings.IndexByte(.ID, ' '); >= 0 {
.PkgPath = .ID[:]
} else {
.PkgPath = .ID
}
if .PkgPath == "unsafe" {
.GoFiles = nil
}
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
}
:= make(map[string]bool)
for , := range .Imports {
[] = true
}
.Imports = make(map[string]*Package)
for , := range .ImportMap {
.Imports[] = &Package{ID: }
delete(, )
}
for := range {
if == "C" {
continue
}
.Imports[] = &Package{ID: }
}
if !.DepOnly {
.Roots = append(.Roots, .ID)
}
if len(.CompiledGoFiles) == 0 {
.CompiledGoFiles = .GoFiles
}
if := .Error; != nil && .shouldAddFilenameFromError() {
:= func( string) bool {
:= strings.Split(, ":")
if len() < 1 {
return false
}
:= strings.TrimSpace([0])
if == "" {
return false
}
if !filepath.IsAbs() {
= filepath.Join(.cfg.Dir, )
}
, := os.Stat()
if == nil {
return false
}
.CompiledGoFiles = append(.CompiledGoFiles, )
.GoFiles = append(.GoFiles, )
return true
}
:= (.Pos)
if ! {
(.Err)
}
}
if .Error != nil {
:= strings.TrimSpace(.Error.Err)
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 {
if len(.GoFiles) > 0 || len(.CompiledGoFiles) > 0 {
return false
}
, := .getGoVersion()
if != nil {
return false
}
if < 15 {
return len(.Error.ImportStack) == 0
}
return len(.Error.ImportStack) == 0 || .Error.ImportStack[len(.Error.ImportStack)-1] == .ImportPath
}
func ( *golistState) () (int, error) {
.goVersionOnce.Do(func() {
.goVersion, .goVersionError = gocommand.GoVersion(.ctx, .cfgInvocation(), .cfg.gocmdRunner)
})
return .goVersion, .goVersionError
}
func ( *golistState) ( string) (string, bool, error) {
, := filepath.Abs()
if != nil {
return "", false,
}
, := .determineRootDirs()
if != nil {
return "", false,
}
for , := range {
if !strings.HasPrefix(, ) {
continue
}
, := filepath.Rel(, )
if != nil {
continue
}
if != "" {
return path.Join(, filepath.ToSlash()), true, nil
}
return filepath.ToSlash(), true, nil
}
return "", false, nil
}
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")
if .Mode&NeedFiles != 0 || .Mode&NeedTypes != 0 {
("Dir", "GoFiles", "IgnoredGoFiles", "IgnoredOtherFiles", "CFiles",
"CgoFiles", "CXXFiles", "MFiles", "HFiles", "FFiles", "SFiles",
"SwigFiles", "SwigCXXFiles", "SysoFiles")
if .Tests {
("TestGoFiles", "XTestGoFiles")
}
}
if .Mode&NeedTypes != 0 {
("Dir", "CompiledGoFiles")
}
if .Mode&NeedCompiledGoFiles != 0 {
("Dir", "CompiledGoFiles", "Export")
}
if .Mode&NeedImports != 0 {
("DepOnly", "Imports", "ImportMap")
if .Tests {
("TestImports", "XTestImports")
}
}
if .Mode&NeedDeps != 0 {
("DepOnly")
}
if usesExportData() {
("Dir", "Export")
}
if .Mode&needInternalForTest != 0 {
("ForTest")
}
if .Mode&needInternalDepsErrors != 0 {
("DepsErrors")
}
if .Mode&NeedModule != 0 {
("Module")
}
if .Mode&NeedEmbedFiles != 0 {
("EmbedFiles")
}
if .Mode&NeedEmbedPatterns != 0 {
("EmbedPatterns")
}
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),
fmt.Sprintf("-find=%t", !.Tests && .Mode& == 0 && !usesExportData()),
}
= append(, .BuildFlags...)
= append(, "--")
= append(, ...)
return
}
func ( *golistState) () gocommand.Invocation {
:= .cfg
return gocommand.Invocation{
BuildFlags: .BuildFlags,
ModFile: .modFile,
ModFlag: .modFlag,
CleanEnv: .Env != nil,
Env: .Env,
Logf: .Logf,
WorkingDir: .Dir,
}
}
func ( *golistState) ( string, ...string) (*bytes.Buffer, error) {
:= .cfg
:= .cfgInvocation()
if == "list" {
, := .getGoVersion()
if != nil {
return nil,
}
if >= 16 {
, , := .writeOverlays()
if != nil {
return nil,
}
defer ()
.Overlay =
}
}
.Verb =
.Args =
:= .gocmdRunner
if == nil {
= &gocommand.Runner{}
}
, , , := .RunRaw(.Context, )
if != nil {
if , := .(*exec.Error); && .Err == exec.ErrNotFound {
return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound)
}
, := .(*exec.ExitError)
if ! {
return nil, fmt.Errorf("couldn't run 'go': %w", )
}
if strings.Contains(.String(), "flag provided but not defined") {
return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", , )}
}
if len(.String()) > 0 && strings.Contains(.String(), "unexpected directory layout") {
return nil,
}
:= func( rune) bool {
return unicode.IsOneOf([]*unicode.RangeTable{unicode.L, unicode.M, unicode.N, unicode.P, unicode.S}, ) &&
!strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", )
}
:= .String()
for strings.HasPrefix(, "go: downloading") {
= [strings.IndexRune(, '\n')+1:]
}
if len(.String()) > 0 && strings.HasPrefix(.String(), "# ") {
:= [len("# "):]
if strings.HasPrefix(strings.TrimLeftFunc(, ), "\n") {
return , nil
}
if strings.HasPrefix(, "pkg-config") {
return , nil
}
}
if len(.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"))
return bytes.NewBufferString(), nil
}
if len(.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"))
return bytes.NewBufferString(), nil
}
const = "no such directory"
if len(.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"))
return bytes.NewBufferString(), nil
}
if len(.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"))
return bytes.NewBufferString(), nil
}
if len(.String()) > 0 && strings.Contains(.String(), "outside available modules") {
:= fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
"command-line-arguments", strings.Trim(.String(), "\n"))
return bytes.NewBufferString(), nil
}
if len(.String()) > 0 && strings.Contains(.String(), "outside module root") {
:= fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
"command-line-arguments", strings.Trim(.String(), "\n"))
return bytes.NewBufferString(), nil
}
if len(.String()) > 0 && strings.Contains(.String(), "no Go files in") {
if len(.String()) > 0 {
return , nil
}
:= .String()
var string
:= strings.Index(, ":")
if > 0 && strings.HasPrefix(, "go build ") {
= [len("go build "):]
}
:= fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
, strings.Trim(, "\n"))
return bytes.NewBufferString(), nil
}
if !usesExportData() && !containsGoFile() {
return nil,
}
}
return , nil
}
type OverlayJSON struct {
Replace map[string]string `json:"replace,omitempty"`
}
func ( *golistState) () ( string, func(), error) {
if len(.cfg.Overlay) == 0 {
return "", func() {}, nil
}
, := ioutil.TempDir("", "gopackages-*")
if != nil {
return "", nil,
}
= func() {
os.RemoveAll()
}
defer func() {
if != nil {
()
}
}()
:= map[string]string{}
for , := range .cfg.Overlay {
:= strings.Join(strings.Split(filepath.ToSlash(), "/"), "")
, := ioutil.TempFile(, fmt.Sprintf("*-%s", ))
if != nil {
return "", func() {},
}
if , := .Write(); != nil {
return "", func() {},
}
if := .Close(); != nil {
return "", func() {},
}
[] = .Name()
}
, := json.Marshal(OverlayJSON{Replace: })
if != nil {
return "", func() {},
}
= filepath.Join(, "overlay.json")
if := ioutil.WriteFile(, , 0665); != nil {
return "", func() {},
}
return , , nil
}
func ( []string) bool {
for , := range {
if strings.HasSuffix(, ".go") {
return true
}
}
return false
}
func ( *exec.Cmd) string {
:= make(map[string]string)
for , := range .Env {
:= strings.SplitN(, "=", 2)
, := [0], [1]
[] =
}
var []string
for , := range .Args {
:= strconv.Quote()
if [1:len()-1] != || strings.Contains(, " ") {
= append(, )
} else {
= append(, )
}
}
return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", ["GOROOT"], ["GOPATH"], ["GO111MODULE"], ["GOPROXY"], ["PWD"], strings.Join(, " "))
}