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

// Package gcimporter implements Import for gc-generated object files.
package gcimporter // import "go/internal/gcimporter" import ( ) // debugging/development support const debug = false var exportMap sync.Map // package dir → func() (string, error) // 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 lookupGorootExport( string) (string, error) { , := exportMap.Load() if ! { var ( sync.Once string error ) , _ = exportMap.LoadOrStore(, func() (string, error) { .Do(func() { := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "list", "-export", "-f", "{{.Export}}", ) .Dir = build.Default.GOROOT .Env = append(os.Environ(), "PWD="+.Dir, "GOROOT="+build.Default.GOROOT) var []byte , = .Output() if != nil { if , := .(*exec.ExitError); && len(.Stderr) > 0 { = errors.New(string(.Stderr)) } return } := strings.Split(string(bytes.TrimSpace()), "\n") if len() != 1 { = fmt.Errorf("go list reported %d exports; expected 1", len()) return } = [0] }) return , }) } return .(func() (string, error))() } var pkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extension // 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. func (, string) (, string, error) { if == "" { return "", "", errors.New("path is empty") } 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 = } var *build.Package , = build.Import(, , build.FindOnly|build.AllowBinary) if .PkgObj == "" { if .Goroot && .Dir != "" { , = lookupGorootExport(.Dir) if == nil { _, = os.Stat() } if == nil { return , .ImportPath, nil } } goto } 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", , ) } } // try extensions for , := range pkgExts { = + , := os.Stat() if == nil && !.IsDir() { return , , nil } if == nil { = } } : if == nil { return "", , fmt.Errorf("can't find import: %q", ) } return "", , fmt.Errorf("can't find import: %q: %w", , ) } // 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 ( *token.FileSet, 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 { var string , , = FindPkg(, ) if == "" { if == "unsafe" { return types.Unsafe, nil } return nil, } // 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() := bufio.NewReader() , , := FindExportData() if != nil { return } switch { case "$$\n": = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", ) case "$$B\n": var byte if , = .ReadByte(); != nil { return } -- // The unified export format starts with a 'u'; the indexed export // format starts with an 'i'; and the older binary export format // starts with a 'c', 'd', or 'v' (from "version"). Select // appropriate importer. switch { case 'u': var []byte var io.Reader = if >= 0 { if , = saferio.ReadData(, uint64()); != nil { return } } else if , = io.ReadAll(); != nil { return } := string() = [:strings.LastIndex(, "\n$$\n")] := pkgbits.NewPkgDecoder(, ) = readUnifiedPackage(, nil, , ) case 'i': , = iImportData(, , , ) default: = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", ) } default: = fmt.Errorf("import %q: unknown export data header: %q", , ) } return }