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