1
2
3
4
5 package testimporter
6
7 import (
8 "bufio"
9 "fmt"
10 "go/build"
11 "internal/exportdata"
12 "internal/pkgbits"
13 "os"
14 "os/exec"
15 "path/filepath"
16 "strings"
17 "sync"
18
19 "cmd/compile/internal/base"
20 "cmd/compile/internal/importer"
21 "cmd/compile/internal/types2"
22 )
23
24
25
26
27 type Importer struct {
28 dir string
29 mu sync.Mutex
30 readPkgs map[string]*types2.Package
31 bldOnces map[string]*sync.Once
32 bldCache map[string]*bldResult
33 }
34
35 type bldResult struct {
36 out string
37 err error
38 }
39
40
41 func NewImporter() *Importer {
42 dir, err := os.MkdirTemp("", "")
43 if err != nil {
44 panic("could not create temp directory")
45 }
46 return &Importer{
47 dir: dir,
48 mu: sync.Mutex{},
49 readPkgs: make(map[string]*types2.Package),
50 bldOnces: make(map[string]*sync.Once),
51 bldCache: make(map[string]*bldResult),
52 }
53 }
54
55
56 func (imp *Importer) Import(path string) (*types2.Package, error) {
57 return imp.ImportFrom(path, "", 0)
58 }
59
60
61 func (imp *Importer) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
62 base.Assert(mode == 0)
63 if path == "unsafe" {
64 return types2.Unsafe, nil
65 }
66 bld, err := build.Import(path, srcDir, build.FindOnly)
67 if err != nil {
68 return nil, err
69 }
70
71 if !bld.Goroot {
72 base.Assert(filepath.IsAbs(srcDir))
73 }
74 path = bld.ImportPath
75
76
77 imp.mu.Lock()
78 if pkg, ok := imp.readPkgs[path]; ok && pkg.Complete() {
79 imp.mu.Unlock()
80 return pkg, nil
81 }
82 imp.mu.Unlock()
83 return imp.readArchive(path, bld.Dir)
84 }
85
86 func (imp *Importer) readArchive(path, dir string) (*types2.Package, error) {
87 out, err := imp.compile(path, dir)
88 if err != nil {
89 return nil, err
90 }
91
92 f, err := os.Open(out)
93 if err != nil {
94 return nil, err
95 }
96 defer f.Close()
97 buf := bufio.NewReader(f)
98 data, err := exportdata.ReadUnified(buf)
99 if err != nil {
100 return nil, err
101 }
102
103 imp.mu.Lock()
104 defer imp.mu.Unlock()
105
106
107 return importer.ReadPackage(nil, imp.readPkgs, pkgbits.NewPkgDecoder(path, string(data))), nil
108 }
109
110 func (imp *Importer) compile(path, dir string) (string, error) {
111 imp.mu.Lock()
112 once, ok := imp.bldOnces[path]
113 if !ok {
114 once = &sync.Once{}
115 imp.bldOnces[path] = once
116 }
117 imp.mu.Unlock()
118 once.Do(func() {
119
120 out := filepath.Join(imp.dir, strings.ReplaceAll(path, "/", "_")+".a")
121 cmd := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "build", "-o", out, dir)
122 var res *bldResult
123 if bytes, err := cmd.CombinedOutput(); err != nil {
124 res = &bldResult{err: fmt.Errorf("building %s failed: %s", path, bytes)}
125 } else {
126 res = &bldResult{out: out}
127 }
128 imp.mu.Lock()
129 imp.bldCache[path] = res
130 imp.mu.Unlock()
131 })
132 imp.mu.Lock()
133 res := imp.bldCache[path]
134 imp.mu.Unlock()
135 return res.out, res.err
136 }
137
View as plain text