| // Copyright 2018 The CUE Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package build |
| |
| import ( |
| "sort" |
| "strconv" |
| |
| "cuelang.org/go/cue/ast" |
| "cuelang.org/go/cue/errors" |
| "cuelang.org/go/cue/token" |
| ) |
| |
| type LoadFunc func(pos token.Pos, path string) *Instance |
| |
| type cueError = errors.Error |
| |
| type buildError struct { |
| cueError |
| inputs []token.Pos |
| } |
| |
| func (e *buildError) InputPositions() []token.Pos { |
| return e.inputs |
| } |
| |
| func (inst *Instance) complete() errors.Error { |
| // TODO: handle case-insensitive collisions. |
| // dir := inst.Dir |
| // names := []string{} |
| // for _, src := range sources { |
| // names = append(names, src.path) |
| // } |
| // f1, f2 := str.FoldDup(names) |
| // if f1 != "" { |
| // return nil, fmt.Errorf("case-insensitive file name collision: %q and %q", f1, f2) |
| // } |
| |
| var ( |
| c = inst.ctxt |
| imported = map[string][]token.Pos{} |
| ) |
| |
| for _, f := range inst.Files { |
| for _, decl := range f.Decls { |
| d, ok := decl.(*ast.ImportDecl) |
| if !ok { |
| continue |
| } |
| for _, spec := range d.Specs { |
| quoted := spec.Path.Value |
| path, err := strconv.Unquote(quoted) |
| if err != nil { |
| inst.Err = errors.Append(inst.Err, |
| errors.Newf( |
| spec.Path.Pos(), |
| "%s: parser returned invalid quoted string: <%s>", |
| f.Filename, quoted)) |
| } |
| imported[path] = append(imported[path], spec.Pos()) |
| } |
| } |
| } |
| |
| paths := make([]string, 0, len(imported)) |
| for path := range imported { |
| paths = append(paths, path) |
| if path == "" { |
| return &buildError{ |
| errors.Newf(token.NoPos, "empty import path"), |
| imported[path], |
| } |
| } |
| } |
| |
| sort.Strings(paths) |
| |
| if inst.loadFunc != nil { |
| for i, path := range paths { |
| isLocal := IsLocalImport(path) |
| if isLocal { |
| // path = dirToImportPath(filepath.Join(dir, path)) |
| } |
| |
| imp := c.imports[path] |
| if imp == nil { |
| pos := token.NoPos |
| if len(imported[path]) > 0 { |
| pos = imported[path][0] |
| } |
| imp = inst.loadFunc(pos, path) |
| if imp == nil { |
| continue |
| } |
| if imp.Err != nil { |
| return imp.Err |
| } |
| imp.ImportPath = path |
| // imp.parent = inst |
| c.imports[path] = imp |
| // imp.parent = nil |
| } else if imp.parent != nil { |
| // TODO: report a standard cycle message. |
| // cycle is now handled explicitly in loader |
| } |
| paths[i] = imp.ImportPath |
| |
| inst.addImport(imp) |
| if imp.Incomplete { |
| inst.Incomplete = true |
| } |
| } |
| } |
| |
| inst.ImportPaths = paths |
| inst.ImportPos = imported |
| |
| // Build full dependencies |
| deps := make(map[string]*Instance) |
| var q []*Instance |
| q = append(q, inst.Imports...) |
| for i := 0; i < len(q); i++ { |
| p1 := q[i] |
| path := p1.ImportPath |
| // The same import path could produce an error or not, |
| // depending on what tries to import it. |
| // Prefer to record entries with errors, so we can report them. |
| // p0 := deps[path] |
| // if err0, err1 := lastError(p0), lastError(p1); p0 == nil || err1 != nil && (err0 == nil || len(err0.ImportStack) > len(err1.ImportStack)) { |
| // deps[path] = p1 |
| // for _, p2 := range p1.Imports { |
| // if deps[p2.ImportPath] != p2 { |
| // q = append(q, p2) |
| // } |
| // } |
| // } |
| if _, ok := deps[path]; !ok { |
| deps[path] = p1 |
| } |
| } |
| inst.Deps = make([]string, 0, len(deps)) |
| for dep := range deps { |
| inst.Deps = append(inst.Deps, dep) |
| } |
| sort.Strings(inst.Deps) |
| |
| for _, dep := range inst.Deps { |
| p1 := deps[dep] |
| if p1 == nil { |
| panic("impossible: missing entry in package cache for " + dep + " imported by " + inst.ImportPath) |
| } |
| if p1.Err != nil { |
| inst.DepsErrors = append(inst.DepsErrors, p1.Err) |
| } |
| } |
| |
| return nil |
| } |