// Copyright 2011 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.

// This file is a reduced copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go.

// Package gcimporter provides various functions for reading // gc-generated object files that can be used to implement the // Importer interface defined by the Go 1.5 standard library package.
package gcimporter // import "golang.org/x/tools/internal/gcimporter" import ( ) const ( // Enable debug during development: it adds some additional checks, and // prevents errors from being recovered. debug = false // If trace is set, debugging output is printed to std out. trace = false ) var exportMap sync.Map // package dir → func() (string, bool) // lookupGorootExport returns the location of the export data // (normally found in the build cache, but located in GOROOT/pkg // in prior Go releases) for the package located in pkgDir. // // (We use the package's directory instead of its import path // mainly to simplify handling of the packages in src/vendor // and cmd/vendor.) func ( string) (string, bool) { , := exportMap.Load() if ! { var ( sync.Once string ) , _ = exportMap.LoadOrStore(, func() (string, bool) { .Do(func() { := exec.Command("go", "list", "-export", "-f", "{{.Export}}", ) .Dir = build.Default.GOROOT var []byte , := .Output() if != nil { return } := strings.Split(string(bytes.TrimSpace()), "\n") if len() != 1 { return } = [0] }) return , != "" }) } return .(func() (string, bool))() } var pkgExts = [...]string{".a", ".o"} // FindPkg returns the filename and unique package id for an import // path based on package information provided by build.Import (using // the build.Default build.Context). A relative srcDir is interpreted // relative to the current working directory. // If no file was found, an empty filename is returned. func (, string) (, string) { if == "" { return } var string switch { default: // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" // Don't require the source files to be present. if , := filepath.Abs(); == nil { // see issue 14282 = } , := build.Import(, , build.FindOnly|build.AllowBinary) if .PkgObj == "" { var bool if .Goroot && .Dir != "" { , = lookupGorootExport(.Dir) } if ! { = // make sure we have an id to print in error message return } } else { = strings.TrimSuffix(.PkgObj, ".a") = .ImportPath } case build.IsLocalImport(): // "./x" -> "/this/directory/x.ext", "/this/directory/x" = filepath.Join(, ) = case filepath.IsAbs(): // for completeness only - go/build.Import // does not support absolute imports // "/x" -> "/x.ext", "/x" = = } if false { // for debugging if != { fmt.Printf("%s -> %s\n", , ) } } if != "" { if , := os.Stat(); == nil && !.IsDir() { return } } // try extensions for , := range pkgExts { = + if , := os.Stat(); == nil && !.IsDir() { return } } = "" // not found return } // Import imports a gc-generated package given its import path and srcDir, adds // the corresponding package object to the packages map, and returns the object. // The packages map must contain all packages already imported. func ( map[string]*types.Package, , string, func( string) (io.ReadCloser, error)) ( *types.Package, error) { var io.ReadCloser var , string if != nil { // With custom lookup specified, assume that caller has // converted path to a canonical import path for use in the map. if == "unsafe" { return types.Unsafe, nil } = // No need to re-import if the package was imported completely before. if = []; != nil && .Complete() { return } , := () if != nil { return nil, } = } else { , = FindPkg(, ) if == "" { if == "unsafe" { return types.Unsafe, nil } return nil, fmt.Errorf("can't find import: %q", ) } // no need to re-import if the package was imported completely before if = []; != nil && .Complete() { return } // open file , := os.Open() if != nil { return nil, } defer func() { if != nil { // add file name to error = fmt.Errorf("%s: %v", , ) } }() = } defer .Close() var string var int64 := bufio.NewReader() if , , = FindExportData(); != nil { return } switch { case "$$B\n": var []byte , = ioutil.ReadAll() if != nil { break } // TODO(gri): allow clients of go/importer to provide a FileSet. // Or, define a new standard go/types/gcexportdata package. := token.NewFileSet() // The indexed export format starts with an 'i'; the older // binary export format starts with a 'c', 'd', or 'v' // (from "version"). Select appropriate importer. if len() > 0 { switch [0] { case 'i': , , := IImportData(, , [1:], ) return , case 'v', 'c', 'd': , , := BImportData(, , , ) return , case 'u': , , := UImportData(, , [1:], ) return , default: := len() if > 10 { = 10 } return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string([:]), ) } } default: = fmt.Errorf("unknown export data header: %q", ) } return } func ( types.Type) types.Type { if , := .(*types.Pointer); != nil { return .Elem() } return } type byPath []*types.Package func ( byPath) () int { return len() } func ( byPath) (, int) { [], [] = [], [] } func ( byPath) (, int) bool { return [].Path() < [].Path() }