import (
"bufio"
+ "bytes"
"fmt"
"go/build"
- "internal/goroot"
"internal/pkgbits"
"io"
"os"
- "path"
+ "os/exec"
"path/filepath"
"strings"
+ "sync"
"cmd/compile/internal/types2"
)
-func lookupGorootExport(pkgpath, srcRoot, srcDir string) (string, bool) {
- pkgpath = filepath.ToSlash(pkgpath)
- m, err := goroot.PkgfileMap()
- if err != nil {
- return "", false
- }
- if export, ok := m[pkgpath]; ok {
- return export, true
- }
- vendorPrefix := "vendor"
- if strings.HasPrefix(srcDir, filepath.Join(srcRoot, "cmd")) {
- vendorPrefix = path.Join("cmd", vendorPrefix)
+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 lookupGorootExport(pkgDir string) (string, bool) {
+ f, ok := exportMap.Load(pkgDir)
+ if !ok {
+ var (
+ listOnce sync.Once
+ exportPath string
+ )
+ f, _ = exportMap.LoadOrStore(pkgDir, func() (string, bool) {
+ listOnce.Do(func() {
+ cmd := exec.Command("go", "list", "-export", "-f", "{{.Export}}", pkgDir)
+ cmd.Dir = build.Default.GOROOT
+ var output []byte
+ output, err := cmd.Output()
+ if err != nil {
+ return
+ }
+
+ exports := strings.Split(string(bytes.TrimSpace(output)), "\n")
+ if len(exports) != 1 {
+ return
+ }
+
+ exportPath = exports[0]
+ })
+
+ return exportPath, exportPath != ""
+ })
}
- pkgpath = path.Join(vendorPrefix, pkgpath)
- if false { // for debugging
- fmt.Fprintln(os.Stderr, "looking up ", pkgpath)
- }
- export, ok := m[pkgpath]
- return export, ok
+
+ return f.(func() (string, bool))()
}
var pkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extension
bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
if bp.PkgObj == "" {
var ok bool
- if bp.Goroot {
- filename, ok = lookupGorootExport(path, bp.SrcRoot, srcDir)
+ if bp.Goroot && bp.Dir != "" {
+ filename, ok = lookupGorootExport(bp.Dir)
}
if !ok {
id = path // make sure we have an id to print in error message
import (
"bufio"
+ "bytes"
"fmt"
"go/build"
"go/token"
"go/types"
- "internal/goroot"
"internal/pkgbits"
"io"
"os"
- "path"
+ "os/exec"
"path/filepath"
"strings"
+ "sync"
)
// debugging/development support
const debug = false
-func lookupGorootExport(pkgpath, srcRoot, srcDir string) (string, bool) {
- pkgpath = filepath.ToSlash(pkgpath)
- m, err := goroot.PkgfileMap()
- if err != nil {
- return "", false
- }
- if export, ok := m[pkgpath]; ok {
- return export, true
+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 lookupGorootExport(pkgDir string) (string, bool) {
+ f, ok := exportMap.Load(pkgDir)
+ if !ok {
+ var (
+ listOnce sync.Once
+ exportPath string
+ )
+ f, _ = exportMap.LoadOrStore(pkgDir, func() (string, bool) {
+ listOnce.Do(func() {
+ cmd := exec.Command("go", "list", "-export", "-f", "{{.Export}}", pkgDir)
+ cmd.Dir = build.Default.GOROOT
+ var output []byte
+ output, err := cmd.Output()
+ if err != nil {
+ return
+ }
+
+ exports := strings.Split(string(bytes.TrimSpace(output)), "\n")
+ if len(exports) != 1 {
+ return
+ }
+
+ exportPath = exports[0]
+ })
+
+ return exportPath, exportPath != ""
+ })
}
- vendorPrefix := "vendor"
- if strings.HasPrefix(srcDir, filepath.Join(srcRoot, "cmd")) {
- vendorPrefix = path.Join("cmd", vendorPrefix)
- }
- pkgpath = path.Join(vendorPrefix, pkgpath)
- export, ok := m[pkgpath]
- return export, ok
+
+ return f.(func() (string, bool))()
}
var pkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extension
bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
if bp.PkgObj == "" {
var ok bool
- if bp.Goroot {
- filename, ok = lookupGorootExport(path, bp.SrcRoot, srcDir)
+ if bp.Goroot && bp.Dir != "" {
+ filename, ok = lookupGorootExport(bp.Dir)
}
if !ok {
id = path // make sure we have an id to print in error message
}
fmt.Fprintf(&icfg, "# import config")
for importPath, export := range m {
- if importPath != "unsafe" && export != "" { // unsafe
- fmt.Fprintf(&icfg, "\npackagefile %s=%s", importPath, export)
- }
+ fmt.Fprintf(&icfg, "\npackagefile %s=%s", importPath, export)
}
s := icfg.String()
return s, nil
return
}
importPath, export := sp[0], sp[1]
- m[importPath] = export
+ if export != "" {
+ m[importPath] = export
+ }
}
stdlibPkgfileMap = m
})
if err != nil {
t.Fatalf("preparing the importcfg failed: %s", err)
}
- os.WriteFile(dstPath, []byte(importcfg), 0655)
+ err = os.WriteFile(dstPath, []byte(importcfg), 0655)
if err != nil {
t.Fatalf("writing the importcfg failed: %s", err)
}