Skip to content

Commit eb29f48

Browse files
matloobfindleyr
authored andcommitted
go/internal/gcimporter: update to anticipate missing targets and .as
This cl updates go/internal/gcimporter the to anticiapte missing targets and .a files the same way CL 442303 updates the two other versions of gcimporter to do the same. It also adds a couple of helpers to create importcfg files for the compiler and list the locations of cached stdlib .a files in internal/goroot and internal/testenv, the analogues of their import paths in the go distribution. Change-Id: Ie207882c13df0e886a51d31e7957a1e508331f10 Reviewed-on: https://go-review.googlesource.com/c/tools/+/445455 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Bryan Mills <[email protected]> Run-TryBot: Michael Matloob <[email protected]> gopls-CI: kokoro <[email protected]> Reviewed-by: Michael Matloob <[email protected]> (cherry picked from commit e4bb343) Reviewed-on: https://go-review.googlesource.com/c/tools/+/446858 Reviewed-by: Robert Findley <[email protected]>
1 parent cf906bb commit eb29f48

File tree

4 files changed

+151
-19
lines changed

4 files changed

+151
-19
lines changed

go/internal/gcimporter/gcimporter.go

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@ import (
2222
"io"
2323
"io/ioutil"
2424
"os"
25+
"path"
2526
"path/filepath"
2627
"sort"
2728
"strconv"
2829
"strings"
2930
"text/scanner"
31+
32+
"golang.org/x/tools/internal/goroot"
3033
)
3134

3235
const (
@@ -38,6 +41,25 @@ const (
3841
trace = false
3942
)
4043

44+
func lookupGorootExport(pkgpath, srcRoot, srcDir string) (string, bool) {
45+
pkgpath = filepath.ToSlash(pkgpath)
46+
m, err := goroot.PkgfileMap()
47+
if err != nil {
48+
return "", false
49+
}
50+
if export, ok := m[pkgpath]; ok {
51+
return export, true
52+
}
53+
vendorPrefix := "vendor"
54+
if strings.HasPrefix(srcDir, filepath.Join(srcRoot, "cmd")) {
55+
vendorPrefix = path.Join("cmd", vendorPrefix)
56+
}
57+
pkgpath = path.Join(vendorPrefix, pkgpath)
58+
fmt.Fprintln(os.Stderr, "looking up ", pkgpath)
59+
export, ok := m[pkgpath]
60+
return export, ok
61+
}
62+
4163
var pkgExts = [...]string{".a", ".o"}
4264

4365
// FindPkg returns the filename and unique package id for an import
@@ -60,11 +82,18 @@ func FindPkg(path, srcDir string) (filename, id string) {
6082
}
6183
bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
6284
if bp.PkgObj == "" {
63-
id = path // make sure we have an id to print in error message
64-
return
85+
var ok bool
86+
if bp.Goroot {
87+
filename, ok = lookupGorootExport(path, bp.SrcRoot, srcDir)
88+
}
89+
if !ok {
90+
id = path // make sure we have an id to print in error message
91+
return
92+
}
93+
} else {
94+
noext = strings.TrimSuffix(bp.PkgObj, ".a")
95+
id = bp.ImportPath
6596
}
66-
noext = strings.TrimSuffix(bp.PkgObj, ".a")
67-
id = bp.ImportPath
6897

6998
case build.IsLocalImport(path):
7099
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
@@ -85,6 +114,12 @@ func FindPkg(path, srcDir string) (filename, id string) {
85114
}
86115
}
87116

117+
if filename != "" {
118+
if f, err := os.Stat(filename); err == nil && !f.IsDir() {
119+
return
120+
}
121+
}
122+
88123
// try extensions
89124
for _, ext := range pkgExts {
90125
filename = noext + ext

go/internal/gcimporter/gcimporter_test.go

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,25 +44,31 @@ func needsCompiler(t *testing.T, compiler string) {
4444

4545
// compile runs the compiler on filename, with dirname as the working directory,
4646
// and writes the output file to outdirname.
47-
func compile(t *testing.T, dirname, filename, outdirname string) string {
48-
return compilePkg(t, dirname, filename, outdirname, "p")
47+
// compile gives the resulting package a packagepath of p.
48+
func compile(t *testing.T, dirname, filename, outdirname string, packagefiles map[string]string) string {
49+
return compilePkg(t, dirname, filename, outdirname, packagefiles, "p")
4950
}
5051

51-
func compilePkg(t *testing.T, dirname, filename, outdirname, pkg string) string {
52+
func compilePkg(t *testing.T, dirname, filename, outdirname string, packagefiles map[string]string, pkg string) string {
5253
testenv.NeedsGoBuild(t)
5354

5455
// filename must end with ".go"
55-
if !strings.HasSuffix(filename, ".go") {
56+
basename := strings.TrimSuffix(filepath.Base(filename), ".go")
57+
ok := filename != basename
58+
if !ok {
5659
t.Fatalf("filename doesn't end in .go: %s", filename)
5760
}
58-
basename := filepath.Base(filename)
59-
outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o")
60-
cmd := exec.Command("go", "tool", "compile", "-p="+pkg, "-o", outname, filename)
61+
objname := basename + ".o"
62+
outname := filepath.Join(outdirname, objname)
63+
importcfgfile := filepath.Join(outdirname, basename) + ".importcfg"
64+
testenv.WriteImportcfg(t, importcfgfile, packagefiles)
65+
importreldir := strings.ReplaceAll(outdirname, string(os.PathSeparator), "/")
66+
cmd := exec.Command("go", "tool", "compile", "-p", pkg, "-D", importreldir, "-importcfg", importcfgfile, "-o", outname, filename)
6167
cmd.Dir = dirname
6268
out, err := cmd.CombinedOutput()
6369
if err != nil {
6470
t.Logf("%s", out)
65-
t.Fatalf("(cd %v && %v) failed: %s", cmd.Dir, cmd, err)
71+
t.Fatalf("go tool compile %s failed: %s", filename, err)
6672
}
6773
return outname
6874
}
@@ -129,7 +135,7 @@ func TestImportTestdata(t *testing.T) {
129135
tmpdir := mktmpdir(t)
130136
defer os.RemoveAll(tmpdir)
131137

132-
compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"))
138+
compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"), nil)
133139

134140
// filename should end with ".go"
135141
filename := testfile[:len(testfile)-3]
@@ -459,8 +465,8 @@ func TestIssue13566(t *testing.T) {
459465
if err != nil {
460466
t.Fatal(err)
461467
}
462-
compilePkg(t, "testdata", "a.go", testoutdir, apkg(testoutdir))
463-
compile(t, testoutdir, bpath, testoutdir)
468+
compilePkg(t, "testdata", "a.go", testoutdir, nil, apkg(testoutdir))
469+
compile(t, testoutdir, bpath, testoutdir, map[string]string{apkg(testoutdir): filepath.Join(testoutdir, "a.o")})
464470

465471
// import must succeed (test for issue at hand)
466472
pkg := importPkg(t, "./testdata/b", tmpdir)
@@ -528,7 +534,7 @@ func TestIssue15517(t *testing.T) {
528534
tmpdir := mktmpdir(t)
529535
defer os.RemoveAll(tmpdir)
530536

531-
compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"))
537+
compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"), nil)
532538

533539
// Multiple imports of p must succeed without redeclaration errors.
534540
// We use an import path that's not cleaned up so that the eventual
@@ -619,8 +625,8 @@ func TestIssue51836(t *testing.T) {
619625
if err != nil {
620626
t.Fatal(err)
621627
}
622-
compilePkg(t, dir, "a.go", testoutdir, apkg(testoutdir))
623-
compile(t, testoutdir, bpath, testoutdir)
628+
compilePkg(t, dir, "a.go", testoutdir, nil, apkg(testoutdir))
629+
compile(t, testoutdir, bpath, testoutdir, map[string]string{apkg(testoutdir): filepath.Join(testoutdir, "a.o")})
624630

625631
// import must succeed (test for issue at hand)
626632
_ = importPkg(t, "./testdata/aa", tmpdir)
@@ -646,7 +652,7 @@ func importPkg(t *testing.T, path, srcDir string) *types.Package {
646652
func compileAndImportPkg(t *testing.T, name string) *types.Package {
647653
tmpdir := mktmpdir(t)
648654
defer os.RemoveAll(tmpdir)
649-
compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"))
655+
compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"), nil)
650656
return importPkg(t, "./testdata/"+name, tmpdir)
651657
}
652658

internal/goroot/importcfg.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Package goroot is a copy of package internal/goroot
6+
// in the main GO repot. It provides a utility to produce
7+
// an importcfg and import path to package file map mapping
8+
// standard library packages to the locations of their export
9+
// data files.
10+
package goroot
11+
12+
import (
13+
"bytes"
14+
"fmt"
15+
"os/exec"
16+
"strings"
17+
"sync"
18+
)
19+
20+
// Importcfg returns an importcfg file to be passed to the
21+
// Go compiler that contains the cached paths for the .a files for the
22+
// standard library.
23+
func Importcfg() (string, error) {
24+
var icfg bytes.Buffer
25+
26+
m, err := PkgfileMap()
27+
if err != nil {
28+
return "", err
29+
}
30+
fmt.Fprintf(&icfg, "# import config")
31+
for importPath, export := range m {
32+
if importPath != "unsafe" && export != "" { // unsafe
33+
fmt.Fprintf(&icfg, "\npackagefile %s=%s", importPath, export)
34+
}
35+
}
36+
s := icfg.String()
37+
return s, nil
38+
}
39+
40+
var (
41+
stdlibPkgfileMap map[string]string
42+
stdlibPkgfileErr error
43+
once sync.Once
44+
)
45+
46+
// PkgfileMap returns a map of package paths to the location on disk
47+
// of the .a file for the package.
48+
// The caller must not modify the map.
49+
func PkgfileMap() (map[string]string, error) {
50+
once.Do(func() {
51+
m := make(map[string]string)
52+
output, err := exec.Command("go", "list", "-export", "-e", "-f", "{{.ImportPath}} {{.Export}}", "std", "cmd").Output()
53+
if err != nil {
54+
stdlibPkgfileErr = err
55+
}
56+
for _, line := range strings.Split(string(output), "\n") {
57+
if line == "" {
58+
continue
59+
}
60+
sp := strings.SplitN(line, " ", 2)
61+
if len(sp) != 2 {
62+
err = fmt.Errorf("determining pkgfile map: invalid line in go list output: %q", line)
63+
return
64+
}
65+
importPath, export := sp[0], sp[1]
66+
m[importPath] = export
67+
}
68+
stdlibPkgfileMap = m
69+
})
70+
return stdlibPkgfileMap, stdlibPkgfileErr
71+
}

internal/testenv/testenv.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ import (
1616
"runtime/debug"
1717
"strings"
1818
"sync"
19+
"testing"
1920
"time"
2021

22+
"golang.org/x/tools/internal/goroot"
23+
2124
exec "golang.org/x/sys/execabs"
2225
)
2326

@@ -329,3 +332,20 @@ func Deadline(t Testing) (time.Time, bool) {
329332
}
330333
return td.Deadline()
331334
}
335+
336+
// WriteImportcfg writes an importcfg file used by the compiler or linker to
337+
// dstPath containing entries for the packages in std and cmd in addition
338+
// to the package to package file mappings in additionalPackageFiles.
339+
func WriteImportcfg(t testing.TB, dstPath string, additionalPackageFiles map[string]string) {
340+
importcfg, err := goroot.Importcfg()
341+
for k, v := range additionalPackageFiles {
342+
importcfg += fmt.Sprintf("\npackagefile %s=%s", k, v)
343+
}
344+
if err != nil {
345+
t.Fatalf("preparing the importcfg failed: %s", err)
346+
}
347+
ioutil.WriteFile(dstPath, []byte(importcfg), 0655)
348+
if err != nil {
349+
t.Fatalf("writing the importcfg failed: %s", err)
350+
}
351+
}

0 commit comments

Comments
 (0)