cuelang.org/go@v0.10.1/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  	"cuelang.org/go/cue/errors"
    22  	"cuelang.org/go/cue/token"
    23  )
    24  
    25  type LoadFunc func(pos token.Pos, path string) *Instance
    26  
    27  type cueError = errors.Error
    28  
    29  type buildError struct {
    30  	cueError
    31  	inputs []token.Pos
    32  }
    33  
    34  func (e *buildError) InputPositions() []token.Pos {
    35  	return e.inputs
    36  }
    37  
    38  func (inst *Instance) complete() errors.Error {
    39  	// TODO: handle case-insensitive collisions.
    40  	// dir := inst.Dir
    41  	// names := []string{}
    42  	// for _, src := range sources {
    43  	// 	names = append(names, src.path)
    44  	// }
    45  	// f1, f2 := str.FoldDup(names)
    46  	// if f1 != "" {
    47  	// 	return nil, fmt.Errorf("case-insensitive file name collision: %q and %q", f1, f2)
    48  	// }
    49  
    50  	var (
    51  		c        = inst.ctxt
    52  		imported = map[string][]token.Pos{}
    53  	)
    54  
    55  	for _, f := range inst.Files {
    56  		for _, spec := range f.Imports {
    57  			quoted := spec.Path.Value
    58  			path, err := strconv.Unquote(quoted)
    59  			if err != nil {
    60  				inst.Err = errors.Append(inst.Err,
    61  					errors.Newf(
    62  						spec.Path.Pos(),
    63  						"%s: parser returned invalid quoted string: <%s>",
    64  						f.Filename, quoted))
    65  			}
    66  			imported[path] = append(imported[path], spec.Pos())
    67  		}
    68  	}
    69  
    70  	paths := make([]string, 0, len(imported))
    71  	for path := range imported {
    72  		paths = append(paths, path)
    73  		if path == "" {
    74  			return &buildError{
    75  				errors.Newf(token.NoPos, "empty import path"),
    76  				imported[path],
    77  			}
    78  		}
    79  	}
    80  
    81  	sort.Strings(paths)
    82  
    83  	if inst.loadFunc != nil {
    84  		for i, path := range paths {
    85  			// isLocal := IsLocalImport(path)
    86  			// if isLocal {
    87  			// 	path = dirToImportPath(filepath.Join(dir, path))
    88  			// }
    89  
    90  			imp := c.imports[path]
    91  			if imp == nil {
    92  				pos := token.NoPos
    93  				if len(imported[path]) > 0 {
    94  					pos = imported[path][0]
    95  				}
    96  				imp = inst.loadFunc(pos, path)
    97  				if imp == nil {
    98  					continue
    99  				}
   100  				if imp.Err != nil {
   101  					return errors.Wrapf(imp.Err, pos, "import failed")
   102  				}
   103  				imp.ImportPath = path
   104  				// imp.parent = inst
   105  				c.imports[path] = imp
   106  				// imp.parent = nil
   107  			} else if imp.parent != nil {
   108  				// TODO: report a standard cycle message.
   109  				//       cycle is now handled explicitly in loader
   110  			}
   111  			paths[i] = imp.ImportPath
   112  
   113  			inst.addImport(imp)
   114  			if imp.Incomplete {
   115  				inst.Incomplete = true
   116  			}
   117  		}
   118  	}
   119  
   120  	inst.ImportPaths = paths
   121  	inst.ImportPos = imported
   122  
   123  	// Build full dependencies
   124  	deps := make(map[string]*Instance)
   125  	var q []*Instance
   126  	q = append(q, inst.Imports...)
   127  	for i := 0; i < len(q); i++ {
   128  		p1 := q[i]
   129  		path := p1.ImportPath
   130  		// The same import path could produce an error or not,
   131  		// depending on what tries to import it.
   132  		// Prefer to record entries with errors, so we can report them.
   133  		// p0 := deps[path]
   134  		// if err0, err1 := lastError(p0), lastError(p1); p0 == nil || err1 != nil && (err0 == nil || len(err0.ImportStack) > len(err1.ImportStack)) {
   135  		// 	deps[path] = p1
   136  		// 	for _, p2 := range p1.Imports {
   137  		// 		if deps[p2.ImportPath] != p2 {
   138  		// 			q = append(q, p2)
   139  		// 		}
   140  		// 	}
   141  		// }
   142  		if _, ok := deps[path]; !ok {
   143  			deps[path] = p1
   144  		}
   145  	}
   146  	inst.Deps = make([]string, 0, len(deps))
   147  	for dep := range deps {
   148  		inst.Deps = append(inst.Deps, dep)
   149  	}
   150  	sort.Strings(inst.Deps)
   151  
   152  	for _, dep := range inst.Deps {
   153  		p1 := deps[dep]
   154  		if p1 == nil {
   155  			panic("impossible: missing entry in package cache for " + dep + " imported by " + inst.ImportPath)
   156  		}
   157  		if p1.Err != nil {
   158  			inst.DepsErrors = append(inst.DepsErrors, p1.Err)
   159  		}
   160  	}
   161  
   162  	return nil
   163  }