From cd35323be9e2e2fdf65e06b1a966774f2478aff8 Mon Sep 17 00:00:00 2001 From: Tim King Date: Fri, 8 Nov 2024 15:53:42 -0800 Subject: [PATCH] internal/exportdata: introduce shared library for exportdata Deduplicates FindPkg and FindExportData which were shared by go/internal/gcimporter and cmd/compile/internal/importer into a new package internal/exportdata. This change only moves code. Change-Id: I1daf24dd79fafbe9014b2b15671dcde46b54711e Reviewed-on: https://go-review.googlesource.com/c/go/+/626700 Commit-Queue: Tim King Reviewed-by: Robert Griesemer LUCI-TryBot-Result: Go LUCI --- .../compile/internal/importer/exportdata.go | 76 ------- .../compile/internal/importer/gcimporter.go | 140 +------------ .../internal/importer/gcimporter_test.go | 7 +- src/cmd/dist/buildtool.go | 1 + src/go/build/deps_test.go | 2 +- src/go/internal/gcimporter/exportdata.go | 87 -------- src/go/internal/gcimporter/gcimporter.go | 133 +----------- src/go/internal/gcimporter/gcimporter_test.go | 2 +- src/internal/exportdata/exportdata.go | 198 ++++++++++++++++++ src/internal/exportdata/support.go | 32 +++ 10 files changed, 244 insertions(+), 434 deletions(-) delete mode 100644 src/cmd/compile/internal/importer/exportdata.go delete mode 100644 src/go/internal/gcimporter/exportdata.go create mode 100644 src/internal/exportdata/exportdata.go create mode 100644 src/internal/exportdata/support.go diff --git a/src/cmd/compile/internal/importer/exportdata.go b/src/cmd/compile/internal/importer/exportdata.go deleted file mode 100644 index 8536440ade6..00000000000 --- a/src/cmd/compile/internal/importer/exportdata.go +++ /dev/null @@ -1,76 +0,0 @@ -// 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 implements FindExportData. - -package importer - -import ( - "bufio" - "cmd/internal/archive" - "fmt" - "strings" -) - -// FindExportData positions the reader r at the beginning of the -// export data section of an underlying GC-created object/archive -// file by reading from it. The reader must be positioned at the -// start of the file before calling this function. The hdr result -// is the string before the export data, either "$$" or "$$B". -// -// If size is non-negative, it's the number of bytes of export data -// still available to read from r. -// -// This function should only be used in tests. -func FindExportData(r *bufio.Reader) (hdr string, size int, err error) { - // TODO(taking): Move into a src/internal package then - // dedup with cmd/compile/internal/noder.findExportData and go/internal/gcimporter.FindExportData. - - // Read first line to make sure this is an object file. - line, err := r.ReadSlice('\n') - if err != nil { - err = fmt.Errorf("can't find export data (%v)", err) - return - } - - // Is the first line an archive file signature? - if string(line) != "!\n" { - err = fmt.Errorf("not the start of an archive file (%q)", line) - return - } - - // package export block should be first - size = archive.ReadHeader(r, "__.PKGDEF") - if size <= 0 { - err = fmt.Errorf("not a package file") - return - } - - // Read first line of __.PKGDEF data, so that line - // is once again the first line of the input. - if line, err = r.ReadSlice('\n'); err != nil { - err = fmt.Errorf("can't find export data (%v)", err) - return - } - - // Now at __.PKGDEF in archive. line should begin with "go object ". - if !strings.HasPrefix(string(line), "go object ") { - err = fmt.Errorf("not a Go object file") - return - } - size -= len(line) - - // Skip over object header to export data. - // Begins after first line starting with $$. - for line[0] != '$' { - if line, err = r.ReadSlice('\n'); err != nil { - err = fmt.Errorf("can't find export data (%v)", err) - return - } - size -= len(line) - } - hdr = string(line) - - return -} diff --git a/src/cmd/compile/internal/importer/gcimporter.go b/src/cmd/compile/internal/importer/gcimporter.go index fa780d386a9..9af257730d8 100644 --- a/src/cmd/compile/internal/importer/gcimporter.go +++ b/src/cmd/compile/internal/importer/gcimporter.go @@ -2,155 +2,23 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file contains the FindPkg and Import functions for tests -// to use gc-generated object files. +// This file implements the Import function for tests to use gc-generated object files. package importer import ( "bufio" - "bytes" - "errors" "fmt" - "go/build" + "internal/exportdata" "internal/pkgbits" "internal/saferio" "io" "os" - "os/exec" - "path/filepath" "strings" - "sync" "cmd/compile/internal/types2" ) -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(pkgDir string) (string, error) { - f, ok := exportMap.Load(pkgDir) - if !ok { - var ( - listOnce sync.Once - exportPath string - err error - ) - f, _ = exportMap.LoadOrStore(pkgDir, func() (string, error) { - listOnce.Do(func() { - cmd := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "list", "-export", "-f", "{{.Export}}", pkgDir) - cmd.Dir = build.Default.GOROOT - cmd.Env = append(os.Environ(), "PWD="+cmd.Dir, "GOROOT="+build.Default.GOROOT) - var output []byte - output, err = cmd.Output() - if err != nil { - if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { - err = errors.New(string(ee.Stderr)) - } - return - } - - exports := strings.Split(string(bytes.TrimSpace(output)), "\n") - if len(exports) != 1 { - err = fmt.Errorf("go list reported %d exports; expected 1", len(exports)) - return - } - - exportPath = exports[0] - }) - - return exportPath, err - }) - } - - return f.(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. -// -// This function should only be used in tests. -func FindPkg(path, srcDir string) (filename, id string, err error) { - // TODO(taking): move FindPkg into src/internal and dedup src/go/internal/gcimporter.FindPkg - - if path == "" { - return "", "", errors.New("path is empty") - } - - var noext string - switch { - default: - // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" - // Don't require the source files to be present. - if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 - srcDir = abs - } - var bp *build.Package - bp, err = build.Import(path, srcDir, build.FindOnly|build.AllowBinary) - if bp.PkgObj == "" { - if bp.Goroot && bp.Dir != "" { - filename, err = lookupGorootExport(bp.Dir) - if err == nil { - _, err = os.Stat(filename) - } - if err == nil { - return filename, bp.ImportPath, nil - } - } - goto notfound - } else { - noext = strings.TrimSuffix(bp.PkgObj, ".a") - } - id = bp.ImportPath - - case build.IsLocalImport(path): - // "./x" -> "/this/directory/x.ext", "/this/directory/x" - noext = filepath.Join(srcDir, path) - id = noext - - case filepath.IsAbs(path): - // for completeness only - go/build.Import - // does not support absolute imports - // "/x" -> "/x.ext", "/x" - noext = path - id = path - } - - if false { // for debugging - if path != id { - fmt.Printf("%s -> %s\n", path, id) - } - } - - // try extensions - for _, ext := range pkgExts { - filename = noext + ext - f, statErr := os.Stat(filename) - if statErr == nil && !f.IsDir() { - return filename, id, nil - } - if err == nil { - err = statErr - } - } - -notfound: - if err == nil { - return "", path, fmt.Errorf("can't find import: %q", path) - } - return "", path, fmt.Errorf("can't find import: %q: %w", path, err) -} - // 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. @@ -178,7 +46,7 @@ func Import(packages map[string]*types2.Package, path, srcDir string, lookup fun rc = f } else { var filename string - filename, id, err = FindPkg(path, srcDir) + filename, id, err = exportdata.FindPkg(path, srcDir) if filename == "" { if path == "unsafe" { return types2.Unsafe, nil @@ -207,7 +75,7 @@ func Import(packages map[string]*types2.Package, path, srcDir string, lookup fun defer rc.Close() buf := bufio.NewReader(rc) - hdr, size, err := FindExportData(buf) + hdr, size, err := exportdata.FindExportData(buf) if err != nil { return } diff --git a/src/cmd/compile/internal/importer/gcimporter_test.go b/src/cmd/compile/internal/importer/gcimporter_test.go index 4cf3bee061c..d913d3ca76f 100644 --- a/src/cmd/compile/internal/importer/gcimporter_test.go +++ b/src/cmd/compile/internal/importer/gcimporter_test.go @@ -10,6 +10,7 @@ import ( "cmd/compile/internal/types2" "fmt" "go/build" + "internal/exportdata" "internal/testenv" "os" "os/exec" @@ -101,7 +102,7 @@ func TestImportTestdata(t *testing.T) { importMap := map[string]string{} for _, pkg := range wantImports { - export, _, err := FindPkg(pkg, "testdata") + export, _, err := exportdata.FindPkg(pkg, "testdata") if export == "" { t.Fatalf("no export data found for %s: %v", pkg, err) } @@ -278,7 +279,7 @@ var importedObjectTests = []struct { {"math.Pi", "const Pi untyped float"}, {"math.Sin", "func Sin(x float64) float64"}, {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"}, - {"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string, err error)"}, + {"internal/exportdata.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string, err error)"}, // interfaces {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"}, @@ -440,7 +441,7 @@ func TestIssue13566(t *testing.T) { t.Fatal(err) } - jsonExport, _, err := FindPkg("encoding/json", "testdata") + jsonExport, _, err := exportdata.FindPkg("encoding/json", "testdata") if jsonExport == "" { t.Fatalf("no export data found for encoding/json: %v", err) } diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index 7b2b96fe002..013b769b90f 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -73,6 +73,7 @@ var bootstrapDirs = []string{ "cmd/internal/cov/covcmd", "internal/bisect", "internal/buildcfg", + "internal/exportdata", "internal/goarch", "internal/godebugs", "internal/goexperiment", diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 40d8ab1ba71..bb2bfba6e4f 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -549,7 +549,7 @@ var depsRules = ` # crypto-aware packages DEBUG, go/build, go/types, text/scanner, crypto/md5 - < internal/pkgbits + < internal/pkgbits, internal/exportdata < go/internal/gcimporter, go/internal/gccgoimporter, go/internal/srcimporter < go/importer; diff --git a/src/go/internal/gcimporter/exportdata.go b/src/go/internal/gcimporter/exportdata.go deleted file mode 100644 index a022c153ecc..00000000000 --- a/src/go/internal/gcimporter/exportdata.go +++ /dev/null @@ -1,87 +0,0 @@ -// 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 implements FindExportData. - -package gcimporter - -import ( - "bufio" - "fmt" - "io" - "strconv" - "strings" -) - -// Copy of cmd/internal/archive.ReadHeader. -func readArchiveHeader(b *bufio.Reader, name string) int { - // architecture-independent object file output - const HeaderSize = 60 - - var buf [HeaderSize]byte - if _, err := io.ReadFull(b, buf[:]); err != nil { - return -1 - } - aname := strings.Trim(string(buf[0:16]), " ") - if !strings.HasPrefix(aname, name) { - return -1 - } - asize := strings.Trim(string(buf[48:58]), " ") - i, _ := strconv.Atoi(asize) - return i -} - -// FindExportData positions the reader r at the beginning of the -// export data section of an underlying GC-created object/archive -// file by reading from it. The reader must be positioned at the -// start of the file before calling this function. The hdr result -// is the string before the export data, either "$$" or "$$B". -func FindExportData(r *bufio.Reader) (hdr string, size int, err error) { - // Read first line to make sure this is an object file. - line, err := r.ReadSlice('\n') - if err != nil { - err = fmt.Errorf("can't find export data (%v)", err) - return - } - - // Is the first line an archive file signature? - if string(line) != "!\n" { - err = fmt.Errorf("not the start of an archive file (%q)", line) - return - } - - // package export block should be first - size = readArchiveHeader(r, "__.PKGDEF") - if size <= 0 { - err = fmt.Errorf("not a package file") - return - } - - // Read first line of __.PKGDEF data, so that line - // is once again the first line of the input. - if line, err = r.ReadSlice('\n'); err != nil { - err = fmt.Errorf("can't find export data (%v)", err) - return - } - - // Now at __.PKGDEF in archive. line should begin with "go object ". - if !strings.HasPrefix(string(line), "go object ") { - err = fmt.Errorf("not a Go object file") - return - } - size -= len(line) - - // Skip over object header to export data. - // Begins after first line starting with $$. - for line[0] != '$' { - if line, err = r.ReadSlice('\n'); err != nil { - err = fmt.Errorf("can't find export data (%v)", err) - return - } - size -= len(line) - } - hdr = string(line) - - return -} diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go index a07fd8a6de6..451afe6fd5a 100644 --- a/src/go/internal/gcimporter/gcimporter.go +++ b/src/go/internal/gcimporter/gcimporter.go @@ -7,144 +7,17 @@ package gcimporter // import "go/internal/gcimporter" import ( "bufio" - "bytes" - "errors" "fmt" - "go/build" "go/token" "go/types" + "internal/exportdata" "internal/pkgbits" "internal/saferio" "io" "os" - "os/exec" - "path/filepath" "strings" - "sync" ) -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(pkgDir string) (string, error) { - f, ok := exportMap.Load(pkgDir) - if !ok { - var ( - listOnce sync.Once - exportPath string - err error - ) - f, _ = exportMap.LoadOrStore(pkgDir, func() (string, error) { - listOnce.Do(func() { - cmd := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "list", "-export", "-f", "{{.Export}}", pkgDir) - cmd.Dir = build.Default.GOROOT - cmd.Env = append(os.Environ(), "PWD="+cmd.Dir, "GOROOT="+build.Default.GOROOT) - var output []byte - output, err = cmd.Output() - if err != nil { - if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { - err = errors.New(string(ee.Stderr)) - } - return - } - - exports := strings.Split(string(bytes.TrimSpace(output)), "\n") - if len(exports) != 1 { - err = fmt.Errorf("go list reported %d exports; expected 1", len(exports)) - return - } - - exportPath = exports[0] - }) - - return exportPath, err - }) - } - - return f.(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 FindPkg(path, srcDir string) (filename, id string, err error) { - if path == "" { - return "", "", errors.New("path is empty") - } - - var noext string - switch { - default: - // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" - // Don't require the source files to be present. - if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 - srcDir = abs - } - var bp *build.Package - bp, err = build.Import(path, srcDir, build.FindOnly|build.AllowBinary) - if bp.PkgObj == "" { - if bp.Goroot && bp.Dir != "" { - filename, err = lookupGorootExport(bp.Dir) - if err == nil { - _, err = os.Stat(filename) - } - if err == nil { - return filename, bp.ImportPath, nil - } - } - goto notfound - } else { - noext = strings.TrimSuffix(bp.PkgObj, ".a") - } - id = bp.ImportPath - - case build.IsLocalImport(path): - // "./x" -> "/this/directory/x.ext", "/this/directory/x" - noext = filepath.Join(srcDir, path) - id = noext - - case filepath.IsAbs(path): - // for completeness only - go/build.Import - // does not support absolute imports - // "/x" -> "/x.ext", "/x" - noext = path - id = path - } - - if false { // for debugging - if path != id { - fmt.Printf("%s -> %s\n", path, id) - } - } - - // try extensions - for _, ext := range pkgExts { - filename = noext + ext - f, statErr := os.Stat(filename) - if statErr == nil && !f.IsDir() { - return filename, id, nil - } - if err == nil { - err = statErr - } - } - -notfound: - if err == nil { - return "", path, fmt.Errorf("can't find import: %q", path) - } - return "", path, fmt.Errorf("can't find import: %q: %w", path, err) -} - // 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. @@ -170,7 +43,7 @@ func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDi rc = f } else { var filename string - filename, id, err = FindPkg(path, srcDir) + filename, id, err = exportdata.FindPkg(path, srcDir) if filename == "" { if path == "unsafe" { return types.Unsafe, nil @@ -199,7 +72,7 @@ func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDi defer rc.Close() buf := bufio.NewReader(rc) - hdr, size, err := FindExportData(buf) + hdr, size, err := exportdata.FindExportData(buf) if err != nil { return } diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index bfbedf1a7df..b92c9c9c962 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -403,7 +403,7 @@ var importedObjectTests = []struct { {"math.Pi", "const Pi untyped float"}, {"math.Sin", "func Sin(x float64) float64"}, {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"}, - {"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string, err error)"}, + {"internal/exportdata.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string, err error)"}, // interfaces {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"}, diff --git a/src/internal/exportdata/exportdata.go b/src/internal/exportdata/exportdata.go new file mode 100644 index 00000000000..5cd7cb18c25 --- /dev/null +++ b/src/internal/exportdata/exportdata.go @@ -0,0 +1,198 @@ +// Copyright 2024 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 exportdata implements common utilities for finding +// and reading gc-generated object files. +package exportdata + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "go/build" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" +) + +// FindExportData positions the reader r at the beginning of the +// export data section of an underlying GC-created object/archive +// file by reading from it. The reader must be positioned at the +// start of the file before calling this function. The hdr result +// is the string before the export data, either "$$" or "$$B". +func FindExportData(r *bufio.Reader) (hdr string, size int, err error) { + // TODO(taking): Merge with cmd/compile/internal/noder.findExportData. + + // Read first line to make sure this is an object file. + line, err := r.ReadSlice('\n') + if err != nil { + err = fmt.Errorf("can't find export data (%v)", err) + return + } + + // Is the first line an archive file signature? + if string(line) != "!\n" { + err = fmt.Errorf("not the start of an archive file (%q)", line) + return + } + + // package export block should be first + size = readArchiveHeader(r, "__.PKGDEF") + if size <= 0 { + err = fmt.Errorf("not a package file") + return + } + + // Read first line of __.PKGDEF data, so that line + // is once again the first line of the input. + if line, err = r.ReadSlice('\n'); err != nil { + err = fmt.Errorf("can't find export data (%v)", err) + return + } + + // Now at __.PKGDEF in archive. line should begin with "go object ". + if !strings.HasPrefix(string(line), "go object ") { + err = fmt.Errorf("not a Go object file") + return + } + size -= len(line) + + // Skip over object header to export data. + // Begins after first line starting with $$. + for line[0] != '$' { + if line, err = r.ReadSlice('\n'); err != nil { + err = fmt.Errorf("can't find export data (%v)", err) + return + } + size -= len(line) + } + hdr = string(line) + + return +} + +// 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 FindPkg(path, srcDir string) (filename, id string, err error) { + if path == "" { + return "", "", errors.New("path is empty") + } + + var noext string + switch { + default: + // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" + // Don't require the source files to be present. + if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 + srcDir = abs + } + var bp *build.Package + bp, err = build.Import(path, srcDir, build.FindOnly|build.AllowBinary) + if bp.PkgObj == "" { + if bp.Goroot && bp.Dir != "" { + filename, err = lookupGorootExport(bp.Dir) + if err == nil { + _, err = os.Stat(filename) + } + if err == nil { + return filename, bp.ImportPath, nil + } + } + goto notfound + } else { + noext = strings.TrimSuffix(bp.PkgObj, ".a") + } + id = bp.ImportPath + + case build.IsLocalImport(path): + // "./x" -> "/this/directory/x.ext", "/this/directory/x" + noext = filepath.Join(srcDir, path) + id = noext + + case filepath.IsAbs(path): + // for completeness only - go/build.Import + // does not support absolute imports + // "/x" -> "/x.ext", "/x" + noext = path + id = path + } + + if false { // for debugging + if path != id { + fmt.Printf("%s -> %s\n", path, id) + } + } + + // try extensions + for _, ext := range pkgExts { + filename = noext + ext + f, statErr := os.Stat(filename) + if statErr == nil && !f.IsDir() { + return filename, id, nil + } + if err == nil { + err = statErr + } + } + +notfound: + if err == nil { + return "", path, fmt.Errorf("can't find import: %q", path) + } + return "", path, fmt.Errorf("can't find import: %q: %w", path, err) +} + +var pkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extension + +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(pkgDir string) (string, error) { + f, ok := exportMap.Load(pkgDir) + if !ok { + var ( + listOnce sync.Once + exportPath string + err error + ) + f, _ = exportMap.LoadOrStore(pkgDir, func() (string, error) { + listOnce.Do(func() { + cmd := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "list", "-export", "-f", "{{.Export}}", pkgDir) + cmd.Dir = build.Default.GOROOT + cmd.Env = append(os.Environ(), "PWD="+cmd.Dir, "GOROOT="+build.Default.GOROOT) + var output []byte + output, err = cmd.Output() + if err != nil { + if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { + err = errors.New(string(ee.Stderr)) + } + return + } + + exports := strings.Split(string(bytes.TrimSpace(output)), "\n") + if len(exports) != 1 { + err = fmt.Errorf("go list reported %d exports; expected 1", len(exports)) + return + } + + exportPath = exports[0] + }) + + return exportPath, err + }) + } + + return f.(func() (string, error))() +} diff --git a/src/internal/exportdata/support.go b/src/internal/exportdata/support.go new file mode 100644 index 00000000000..a06401db391 --- /dev/null +++ b/src/internal/exportdata/support.go @@ -0,0 +1,32 @@ +// Copyright 2024 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 contains support functions for exportdata. + +package exportdata + +import ( + "bufio" + "io" + "strconv" + "strings" +) + +// Copy of cmd/internal/archive.ReadHeader. +func readArchiveHeader(b *bufio.Reader, name string) int { + // architecture-independent object file output + const HeaderSize = 60 + + var buf [HeaderSize]byte + if _, err := io.ReadFull(b, buf[:]); err != nil { + return -1 + } + aname := strings.Trim(string(buf[0:16]), " ") + if !strings.HasPrefix(aname, name) { + return -1 + } + asize := strings.Trim(string(buf[48:58]), " ") + i, _ := strconv.Atoi(asize) + return i +}