1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package load
16
17 import (
18 "fmt"
19 "os"
20 pathpkg "path"
21 "path/filepath"
22 "sort"
23 "strings"
24
25 "cuelang.org/go/cue/ast"
26 "cuelang.org/go/cue/build"
27 "cuelang.org/go/cue/errors"
28 "cuelang.org/go/cue/token"
29 "cuelang.org/go/internal/filetypes"
30 "cuelang.org/go/mod/module"
31 )
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 func (l *loader) importPkg(pos token.Pos, p *build.Instance) []*build.Instance {
56 retErr := func(errs errors.Error) []*build.Instance {
57
58 for _, err := range errors.Errors(errs) {
59 p.ReportError(err)
60 }
61 return []*build.Instance{p}
62 }
63
64 for _, item := range l.stk {
65 if item == p.ImportPath {
66 return retErr(&PackageError{Message: errors.NewMessagef("package import cycle not allowed")})
67 }
68 }
69 l.stk.Push(p.ImportPath)
70 defer l.stk.Pop()
71
72 cfg := l.cfg
73 ctxt := &cfg.fileSystem
74
75 if p.Err != nil {
76 return []*build.Instance{p}
77 }
78
79 fp := newFileProcessor(cfg, p, l.tagger)
80
81 if p.PkgName == "" {
82 if l.cfg.Package == "*" {
83 fp.ignoreOther = true
84 fp.allPackages = true
85 p.PkgName = "_"
86 } else {
87 p.PkgName = l.cfg.Package
88 }
89 }
90 if p.PkgName != "" {
91
92 fp.ignoreOther = true
93 }
94
95 var dirs [][2]string
96 genDir := GenPath(cfg.ModuleRoot)
97 if strings.HasPrefix(p.Dir, genDir) {
98 dirs = append(dirs, [2]string{genDir, p.Dir})
99
100 for _, sub := range []string{"pkg", "usr"} {
101 rel, err := filepath.Rel(genDir, p.Dir)
102 if err != nil {
103
104 return retErr(errors.Wrapf(err, token.NoPos, "invalid path"))
105 }
106 base := filepath.Join(cfg.ModuleRoot, modDir, sub)
107 dir := filepath.Join(base, rel)
108 dirs = append(dirs, [2]string{base, dir})
109 }
110 } else {
111 dirs = append(dirs, [2]string{cfg.ModuleRoot, p.Dir})
112 }
113
114 found := false
115 for _, d := range dirs {
116 info, err := ctxt.stat(d[1])
117 if err == nil && info.IsDir() {
118 found = true
119 break
120 }
121 }
122
123 if !found {
124 return retErr(
125 &PackageError{
126 Message: errors.NewMessagef("cannot find package %q", p.DisplayPath),
127 })
128 }
129
130
131
132 inModule := false
133 for _, d := range dirs {
134 if l.cfg.findRoot(d[1]) != "" {
135 inModule = true
136 break
137 }
138 }
139
140 for _, d := range dirs {
141 for dir := filepath.Clean(d[1]); ctxt.isDir(dir); {
142 files, err := ctxt.readDir(dir)
143 if err != nil && !os.IsNotExist(err) {
144 return retErr(errors.Wrapf(err, pos, "import failed reading dir %v", dirs[0][1]))
145 }
146 for _, f := range files {
147 if f.IsDir() {
148 continue
149 }
150 if f.Name() == "-" {
151 if _, err := cfg.fileSystem.stat("-"); !os.IsNotExist(err) {
152 continue
153 }
154 }
155 file, err := filetypes.ParseFile(f.Name(), filetypes.Input)
156 if err != nil {
157 p.UnknownFiles = append(p.UnknownFiles, &build.File{
158 Filename: f.Name(),
159 ExcludeReason: errors.Newf(token.NoPos, "unknown filetype"),
160 })
161 continue
162 }
163 fp.add(dir, file, importComment)
164 }
165
166 if p.PkgName == "" || !inModule || l.cfg.isRoot(dir) || dir == d[0] {
167 break
168 }
169
170
171
172 fp.ignoreOther = true
173
174 parent, _ := filepath.Split(dir)
175 parent = filepath.Clean(parent)
176
177 if parent == dir || len(parent) < len(d[0]) {
178 break
179 }
180 dir = parent
181 }
182 }
183
184 all := []*build.Instance{}
185
186 for _, p := range fp.pkgs {
187 impPath, err := addImportQualifier(importPath(p.ImportPath), p.PkgName)
188 p.ImportPath = string(impPath)
189 if err != nil {
190 p.ReportError(err)
191 }
192
193 all = append(all, p)
194 rewriteFiles(p, cfg.ModuleRoot, false)
195 if errs := fp.finalize(p); errs != nil {
196 p.ReportError(errs)
197 return all
198 }
199
200 l.addFiles(cfg.ModuleRoot, p)
201 _ = p.Complete()
202 }
203 sort.Slice(all, func(i, j int) bool {
204 return all[i].Dir < all[j].Dir
205 })
206 return all
207 }
208
209
210 func (l *loader) _loadFunc(pos token.Pos, path string) *build.Instance {
211 impPath := importPath(path)
212 if isLocalImport(path) {
213 return l.cfg.newErrInstance(errors.Newf(pos, "relative import paths not allowed (%q)", path))
214 }
215
216
217 if strings.IndexByte(strings.Split(path, "/")[0], '.') == -1 {
218 if l.cfg.StdRoot != "" {
219 p := l.newInstance(pos, impPath)
220 _ = l.importPkg(pos, p)
221 return p
222 }
223 return nil
224 }
225
226 p := l.newInstance(pos, impPath)
227 _ = l.importPkg(pos, p)
228 return p
229 }
230
231
232
233 func (l *loader) newRelInstance(pos token.Pos, path, pkgName string) *build.Instance {
234 if !isLocalImport(path) {
235 panic(fmt.Errorf("non-relative import path %q passed to newRelInstance", path))
236 }
237
238 var err errors.Error
239 dir := path
240
241 p := l.cfg.Context.NewInstance(path, l.loadFunc)
242 p.PkgName = pkgName
243 p.DisplayPath = filepath.ToSlash(path)
244
245 p.Root = l.cfg.ModuleRoot
246 p.Module = l.cfg.Module
247
248 dir = filepath.Join(l.cfg.Dir, filepath.FromSlash(path))
249
250 if path != cleanImport(path) {
251 err = errors.Append(err, l.errPkgf(nil,
252 "non-canonical import path: %q should be %q", path, pathpkg.Clean(path)))
253 }
254
255 if importPath, e := l.importPathFromAbsDir(fsPath(dir), path); e != nil {
256
257 } else {
258 p.ImportPath = string(importPath)
259 }
260
261 p.Dir = dir
262
263 if filepath.IsAbs(path) || strings.HasPrefix(path, "/") {
264 err = errors.Append(err, errors.Newf(pos,
265 "absolute import path %q not allowed", path))
266 }
267 if err != nil {
268 p.Err = errors.Append(p.Err, err)
269 p.Incomplete = true
270 }
271
272 return p
273 }
274
275 func (l *loader) importPathFromAbsDir(absDir fsPath, key string) (importPath, errors.Error) {
276 if l.cfg.ModuleRoot == "" {
277 return "", errors.Newf(token.NoPos,
278 "cannot determine import path for %q (root undefined)", key)
279 }
280
281 dir := filepath.Clean(string(absDir))
282 if !strings.HasPrefix(dir, l.cfg.ModuleRoot) {
283 return "", errors.Newf(token.NoPos,
284 "cannot determine import path for %q (dir outside of root)", key)
285 }
286
287 pkg := filepath.ToSlash(dir[len(l.cfg.ModuleRoot):])
288 switch {
289 case strings.HasPrefix(pkg, "/cue.mod/"):
290 pkg = pkg[len("/cue.mod/"):]
291 if pkg == "" {
292 return "", errors.Newf(token.NoPos,
293 "invalid package %q (root of %s)", key, modDir)
294 }
295
296 case l.cfg.Module == "":
297 return "", errors.Newf(token.NoPos,
298 "cannot determine import path for %q (no module)", key)
299 default:
300 pkg = l.cfg.Module + pkg
301 }
302
303 name := l.cfg.Package
304 switch name {
305 case "_", "*":
306 name = ""
307 }
308
309 return addImportQualifier(importPath(pkg), name)
310 }
311
312 func (l *loader) newInstance(pos token.Pos, p importPath) *build.Instance {
313 dir, name, err := l.absDirFromImportPath(pos, p)
314 i := l.cfg.Context.NewInstance(dir, l.loadFunc)
315 i.Dir = dir
316 i.PkgName = name
317 i.DisplayPath = string(p)
318 i.ImportPath = string(p)
319 i.Root = l.cfg.ModuleRoot
320 i.Module = l.cfg.Module
321 i.Err = errors.Append(i.Err, err)
322
323 return i
324 }
325
326
327
328
329
330 func (l *loader) absDirFromImportPath(pos token.Pos, p importPath) (absDir, name string, err errors.Error) {
331 if l.cfg.ModuleRoot == "" {
332 return "", "", errors.Newf(pos, "cannot import %q (root undefined)", p)
333 }
334 origp := p
335
336 parts := module.ParseImportPath(string(p))
337 name = parts.Qualifier
338 p = importPath(parts.Unqualified().String())
339 if name == "" {
340 err = errors.Newf(pos, "empty package name in import path %q", p)
341 } else if strings.IndexByte(name, '.') >= 0 {
342 err = errors.Newf(pos,
343 "cannot determine package name for %q (set explicitly with ':')", p)
344 } else if !ast.IsValidIdent(name) {
345 err = errors.Newf(pos,
346 "implied package identifier %q from import path %q is not valid", name, p)
347 }
348 if l.cfg.Registry != nil {
349 if l.pkgs == nil {
350 return "", name, errors.Newf(pos, "imports are unavailable because there is no cue.mod/module.cue file")
351 }
352
353
354
355
356
357 pkg := l.pkgs.Pkg(string(origp))
358 if pkg == nil {
359 return "", name, errors.Newf(pos, "no dependency found for package %q", p)
360 }
361 if err := pkg.Error(); err != nil {
362 return "", name, errors.Newf(pos, "cannot find package %q: %v", p, err)
363 }
364 if mv := pkg.Mod(); mv.IsLocal() {
365
366
367
368
369 absDir = filepath.Join(GenPath(l.cfg.ModuleRoot), parts.Path)
370 } else {
371 locs := pkg.Locations()
372 if len(locs) > 1 {
373 return "", "", errors.Newf(pos, "package %q unexpectedly found in multiple locations", p)
374 }
375 var err error
376 absDir, err = absPathForSourceLoc(locs[0])
377 if err != nil {
378 return "", name, errors.Newf(pos, "cannot determine source directory for package %q: %v", p, err)
379 }
380 }
381 return absDir, name, nil
382 }
383
384
385
386 sub := filepath.FromSlash(string(p))
387 switch hasPrefix := strings.HasPrefix(string(p), l.cfg.Module); {
388 case hasPrefix && len(sub) == len(l.cfg.Module):
389 absDir = l.cfg.ModuleRoot
390
391 case hasPrefix && p[len(l.cfg.Module)] == '/':
392 absDir = filepath.Join(l.cfg.ModuleRoot, sub[len(l.cfg.Module)+1:])
393
394 default:
395 absDir = filepath.Join(GenPath(l.cfg.ModuleRoot), sub)
396 }
397 return absDir, name, err
398 }
399
400 func absPathForSourceLoc(loc module.SourceLoc) (string, error) {
401 osfs, ok := loc.FS.(module.OSRootFS)
402 if !ok {
403 return "", fmt.Errorf("cannot get absolute path for FS of type %T", loc.FS)
404 }
405 osPath := osfs.OSRoot()
406 if osPath == "" {
407 return "", fmt.Errorf("cannot get absolute path for FS of type %T", loc.FS)
408 }
409 return filepath.Join(osPath, loc.Dir), nil
410 }
411
View as plain text