1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package build
16
17 import (
18 "fmt"
19 pathpkg "path"
20 "path/filepath"
21 "strings"
22 "unicode"
23
24 "cuelang.org/go/cue/ast"
25 "cuelang.org/go/cue/ast/astutil"
26 "cuelang.org/go/cue/errors"
27 "cuelang.org/go/cue/parser"
28 "cuelang.org/go/cue/token"
29 "cuelang.org/go/internal"
30 )
31
32
33
34
35
36 type Instance struct {
37 ctxt *Context
38
39 BuildFiles []*File
40 IgnoredFiles []*File
41 OrphanedFiles []*File
42 InvalidFiles []*File
43 UnknownFiles []*File
44
45 User bool
46
47
48
49 Files []*ast.File
50
51 loadFunc LoadFunc
52 done bool
53
54
55 PkgName string
56 hasName bool
57
58
59
60
61 ImportPath string
62
63
64 Imports []*Instance
65
66
67
68
69 Err errors.Error
70
71 parent *Instance
72
73
74
75
76
77 DisplayPath string
78
79
80
81
82 Module string
83
84
85
86
87
88 Root string
89
90
91
92 Dir string
93
94
95
96
97 ImportComment string `api:"alpha"`
98
99
100
101 AllTags []string `api:"alpha"`
102
103
104 Incomplete bool `api:"alpha"`
105
106
107
108 ImportPaths []string `api:"alpha"`
109 ImportPos map[string][]token.Pos `api:"alpha"`
110
111 Deps []string `api:"alpha"`
112 DepsErrors []error `api:"alpha"`
113 Match []string `api:"alpha"`
114 }
115
116
117
118 func (inst *Instance) RelPath(f *File) string {
119 p, err := filepath.Rel(inst.Root, f.Filename)
120 if err != nil {
121 return f.Filename
122 }
123 return p
124 }
125
126
127 func (inst *Instance) ID() string {
128 if s := inst.ImportPath; s != "" {
129 return s
130 }
131 if inst.PkgName == "" {
132 return "_"
133 }
134 s := fmt.Sprintf("%s:%s", inst.Module, inst.PkgName)
135 return s
136 }
137
138
139 func (inst *Instance) Dependencies() []*Instance {
140
141
142 return appendDependencies(nil, inst, map[*Instance]bool{})
143 }
144
145 func appendDependencies(a []*Instance, inst *Instance, done map[*Instance]bool) []*Instance {
146 for _, d := range inst.Imports {
147 if done[d] {
148 continue
149 }
150 a = append(a, d)
151 done[d] = true
152 a = appendDependencies(a, d, done)
153 }
154 return a
155 }
156
157
158
159 func (inst *Instance) Abs(path string) string {
160 if filepath.IsAbs(path) {
161 return path
162 }
163 return filepath.Join(inst.Root, path)
164 }
165
166 func (inst *Instance) setPkg(pkg string) bool {
167 if !inst.hasName {
168 inst.hasName = true
169 inst.PkgName = pkg
170 return true
171 }
172 return false
173 }
174
175
176 func (inst *Instance) ReportError(err errors.Error) {
177 inst.Err = errors.Append(inst.Err, err)
178 }
179
180
181
182
183 func (inst *Instance) Context() *Context {
184 return inst.ctxt
185 }
186
187 func (inst *Instance) parse(name string, src interface{}) (*ast.File, error) {
188 if inst.ctxt != nil && inst.ctxt.parseFunc != nil {
189 return inst.ctxt.parseFunc(name, src)
190 }
191 return parser.ParseFile(name, src, parser.ParseComments)
192 }
193
194
195 func (inst *Instance) LookupImport(path string) *Instance {
196 path = inst.expandPath(path)
197 for _, inst := range inst.Imports {
198 if inst.ImportPath == path {
199 return inst
200 }
201 }
202 return nil
203 }
204
205 func (inst *Instance) addImport(imp *Instance) {
206 for _, inst := range inst.Imports {
207 if inst.ImportPath == imp.ImportPath {
208 if inst != imp {
209 panic("import added multiple times with different instances")
210 }
211 return
212 }
213 }
214 inst.Imports = append(inst.Imports, imp)
215 }
216
217
218
219
220
221
222
223
224 func (inst *Instance) AddFile(filename string, src interface{}) error {
225 file, err := inst.parse(filename, src)
226 if err != nil {
227
228 err := errors.Promote(err, "error adding file")
229 inst.ReportError(err)
230 return err
231 }
232
233 return inst.AddSyntax(file)
234 }
235
236
237
238 func (inst *Instance) AddSyntax(file *ast.File) errors.Error {
239 astutil.Resolve(file, func(pos token.Pos, msg string, args ...interface{}) {
240 inst.Err = errors.Append(inst.Err, errors.Newf(pos, msg, args...))
241 })
242 _, pkg, pos := internal.PackageInfo(file)
243 if pkg != "" && pkg != "_" && !inst.setPkg(pkg) && pkg != inst.PkgName {
244 err := errors.Newf(pos,
245 "package name %q conflicts with previous package name %q",
246 pkg, inst.PkgName)
247 inst.ReportError(err)
248 return err
249 }
250 inst.Files = append(inst.Files, file)
251 return nil
252 }
253
254 func (inst *Instance) expandPath(path string) string {
255 isLocal := IsLocalImport(path)
256 if isLocal {
257 path = dirToImportPath(filepath.Join(inst.Dir, path))
258 }
259 return path
260 }
261
262
263
264
265
266
267
268
269 func dirToImportPath(dir string) string {
270 return pathpkg.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir)))
271 }
272
273 func makeImportValid(r rune) rune {
274
275 const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
276 if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
277 return '_'
278 }
279 return r
280 }
281
282
283
284 func IsLocalImport(path string) bool {
285 return path == "." || path == ".." ||
286 strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
287 }
288
View as plain text