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