1
2
3
4
5
6 package importer
7
8 import (
9 "bufio"
10 "bytes"
11 "errors"
12 "fmt"
13 "go/build"
14 "internal/pkgbits"
15 "io"
16 "os"
17 "os/exec"
18 "path/filepath"
19 "strings"
20 "sync"
21
22 "cmd/compile/internal/types2"
23 )
24
25 var exportMap sync.Map
26
27
28
29
30
31
32
33
34 func lookupGorootExport(pkgDir string) (string, error) {
35 f, ok := exportMap.Load(pkgDir)
36 if !ok {
37 var (
38 listOnce sync.Once
39 exportPath string
40 err error
41 )
42 f, _ = exportMap.LoadOrStore(pkgDir, func() (string, error) {
43 listOnce.Do(func() {
44 cmd := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "list", "-export", "-f", "{{.Export}}", pkgDir)
45 cmd.Dir = build.Default.GOROOT
46 cmd.Env = append(os.Environ(), "PWD="+cmd.Dir, "GOROOT="+build.Default.GOROOT)
47 var output []byte
48 output, err = cmd.Output()
49 if err != nil {
50 if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
51 err = errors.New(string(ee.Stderr))
52 }
53 return
54 }
55
56 exports := strings.Split(string(bytes.TrimSpace(output)), "\n")
57 if len(exports) != 1 {
58 err = fmt.Errorf("go list reported %d exports; expected 1", len(exports))
59 return
60 }
61
62 exportPath = exports[0]
63 })
64
65 return exportPath, err
66 })
67 }
68
69 return f.(func() (string, error))()
70 }
71
72 var pkgExts = [...]string{".a", ".o"}
73
74
75
76
77
78 func FindPkg(path, srcDir string) (filename, id string, err error) {
79 if path == "" {
80 return "", "", errors.New("path is empty")
81 }
82
83 var noext string
84 switch {
85 default:
86
87
88 if abs, err := filepath.Abs(srcDir); err == nil {
89 srcDir = abs
90 }
91 var bp *build.Package
92 bp, err = build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
93 if bp.PkgObj == "" {
94 if bp.Goroot && bp.Dir != "" {
95 filename, err = lookupGorootExport(bp.Dir)
96 if err == nil {
97 _, err = os.Stat(filename)
98 }
99 if err == nil {
100 return filename, bp.ImportPath, nil
101 }
102 }
103 goto notfound
104 } else {
105 noext = strings.TrimSuffix(bp.PkgObj, ".a")
106 }
107 id = bp.ImportPath
108
109 case build.IsLocalImport(path):
110
111 noext = filepath.Join(srcDir, path)
112 id = noext
113
114 case filepath.IsAbs(path):
115
116
117
118 noext = path
119 id = path
120 }
121
122 if false {
123 if path != id {
124 fmt.Printf("%s -> %s\n", path, id)
125 }
126 }
127
128
129 for _, ext := range pkgExts {
130 filename = noext + ext
131 f, statErr := os.Stat(filename)
132 if statErr == nil && !f.IsDir() {
133 return filename, id, nil
134 }
135 if err == nil {
136 err = statErr
137 }
138 }
139
140 notfound:
141 if err == nil {
142 return "", path, fmt.Errorf("can't find import: %q", path)
143 }
144 return "", path, fmt.Errorf("can't find import: %q: %w", path, err)
145 }
146
147
148
149
150 func Import(packages map[string]*types2.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types2.Package, err error) {
151 var rc io.ReadCloser
152 var id string
153 if lookup != nil {
154
155
156 if path == "unsafe" {
157 return types2.Unsafe, nil
158 }
159 id = path
160
161
162 if pkg = packages[id]; pkg != nil && pkg.Complete() {
163 return
164 }
165 f, err := lookup(path)
166 if err != nil {
167 return nil, err
168 }
169 rc = f
170 } else {
171 var filename string
172 filename, id, err = FindPkg(path, srcDir)
173 if filename == "" {
174 if path == "unsafe" {
175 return types2.Unsafe, nil
176 }
177 return nil, err
178 }
179
180
181 if pkg = packages[id]; pkg != nil && pkg.Complete() {
182 return
183 }
184
185
186 f, err := os.Open(filename)
187 if err != nil {
188 return nil, err
189 }
190 defer func() {
191 if err != nil {
192
193 err = fmt.Errorf("%s: %v", filename, err)
194 }
195 }()
196 rc = f
197 }
198 defer rc.Close()
199
200 buf := bufio.NewReader(rc)
201 hdr, size, err := FindExportData(buf)
202 if err != nil {
203 return
204 }
205
206 switch hdr {
207 case "$$\n":
208 err = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", path)
209
210 case "$$B\n":
211 var data []byte
212 var r io.Reader = buf
213 if size >= 0 {
214 r = io.LimitReader(r, int64(size))
215 }
216 data, err = io.ReadAll(r)
217 if err != nil {
218 break
219 }
220
221 if len(data) == 0 {
222 err = fmt.Errorf("import %q: missing export data", path)
223 break
224 }
225 exportFormat := data[0]
226 s := string(data[1:])
227
228
229
230
231 switch exportFormat {
232 case 'u':
233 s = s[:strings.Index(s, "\n$$\n")]
234 input := pkgbits.NewPkgDecoder(id, s)
235 pkg = ReadPackage(nil, packages, input)
236 case 'i':
237 pkg, err = ImportData(packages, s, id)
238 default:
239 err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path)
240 }
241
242 default:
243 err = fmt.Errorf("import %q: unknown export data header: %q", path, hdr)
244 }
245
246 return
247 }
248
249 type byPath []*types2.Package
250
251 func (a byPath) Len() int { return len(a) }
252 func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
253 func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }
254
View as plain text