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