...

Source file src/cuelang.org/go/cue/build/import.go

Documentation: cuelang.org/go/cue/build

     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/ast"
    22  	"cuelang.org/go/cue/errors"
    23  	"cuelang.org/go/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  }
   171  

View as plain text