github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/cue/build/import.go (about) 1 // Copyright 2018 The CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package build 16 17 import ( 18 "sort" 19 "strconv" 20 21 "github.com/joomcode/cue/cue/ast" 22 "github.com/joomcode/cue/cue/errors" 23 "github.com/joomcode/cue/cue/token" 24 ) 25 26 type LoadFunc func(pos token.Pos, path string) *Instance 27 28 type cueError = errors.Error 29 30 type buildError struct { 31 cueError 32 inputs []token.Pos 33 } 34 35 func (e *buildError) InputPositions() []token.Pos { 36 return e.inputs 37 } 38 39 func (inst *Instance) complete() errors.Error { 40 // TODO: handle case-insensitive collisions. 41 // dir := inst.Dir 42 // names := []string{} 43 // for _, src := range sources { 44 // names = append(names, src.path) 45 // } 46 // f1, f2 := str.FoldDup(names) 47 // if f1 != "" { 48 // return nil, fmt.Errorf("case-insensitive file name collision: %q and %q", f1, f2) 49 // } 50 51 var ( 52 c = inst.ctxt 53 imported = map[string][]token.Pos{} 54 ) 55 56 for _, f := range inst.Files { 57 for _, decl := range f.Decls { 58 d, ok := decl.(*ast.ImportDecl) 59 if !ok { 60 continue 61 } 62 for _, spec := range d.Specs { 63 quoted := spec.Path.Value 64 path, err := strconv.Unquote(quoted) 65 if err != nil { 66 inst.Err = errors.Append(inst.Err, 67 errors.Newf( 68 spec.Path.Pos(), 69 "%s: parser returned invalid quoted string: <%s>", 70 f.Filename, quoted)) 71 } 72 imported[path] = append(imported[path], spec.Pos()) 73 } 74 } 75 } 76 77 paths := make([]string, 0, len(imported)) 78 for path := range imported { 79 paths = append(paths, path) 80 if path == "" { 81 return &buildError{ 82 errors.Newf(token.NoPos, "empty import path"), 83 imported[path], 84 } 85 } 86 } 87 88 sort.Strings(paths) 89 90 if inst.loadFunc != nil { 91 for i, path := range paths { 92 isLocal := IsLocalImport(path) 93 if isLocal { 94 // path = dirToImportPath(filepath.Join(dir, path)) 95 } 96 97 imp := c.imports[path] 98 if imp == nil { 99 pos := token.NoPos 100 if len(imported[path]) > 0 { 101 pos = imported[path][0] 102 } 103 imp = inst.loadFunc(pos, path) 104 if imp == nil { 105 continue 106 } 107 if imp.Err != nil { 108 return errors.Wrapf(imp.Err, pos, "import failed") 109 } 110 imp.ImportPath = path 111 // imp.parent = inst 112 c.imports[path] = imp 113 // imp.parent = nil 114 } else if imp.parent != nil { 115 // TODO: report a standard cycle message. 116 // cycle is now handled explicitly in loader 117 } 118 paths[i] = imp.ImportPath 119 120 inst.addImport(imp) 121 if imp.Incomplete { 122 inst.Incomplete = true 123 } 124 } 125 } 126 127 inst.ImportPaths = paths 128 inst.ImportPos = imported 129 130 // Build full dependencies 131 deps := make(map[string]*Instance) 132 var q []*Instance 133 q = append(q, inst.Imports...) 134 for i := 0; i < len(q); i++ { 135 p1 := q[i] 136 path := p1.ImportPath 137 // The same import path could produce an error or not, 138 // depending on what tries to import it. 139 // Prefer to record entries with errors, so we can report them. 140 // p0 := deps[path] 141 // if err0, err1 := lastError(p0), lastError(p1); p0 == nil || err1 != nil && (err0 == nil || len(err0.ImportStack) > len(err1.ImportStack)) { 142 // deps[path] = p1 143 // for _, p2 := range p1.Imports { 144 // if deps[p2.ImportPath] != p2 { 145 // q = append(q, p2) 146 // } 147 // } 148 // } 149 if _, ok := deps[path]; !ok { 150 deps[path] = p1 151 } 152 } 153 inst.Deps = make([]string, 0, len(deps)) 154 for dep := range deps { 155 inst.Deps = append(inst.Deps, dep) 156 } 157 sort.Strings(inst.Deps) 158 159 for _, dep := range inst.Deps { 160 p1 := deps[dep] 161 if p1 == nil { 162 panic("impossible: missing entry in package cache for " + dep + " imported by " + inst.ImportPath) 163 } 164 if p1.Err != nil { 165 inst.DepsErrors = append(inst.DepsErrors, p1.Err) 166 } 167 } 168 169 return nil 170 }