...

Source file src/cuelang.org/go/cue/load/instances.go

Documentation: cuelang.org/go/cue/load

     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 load
    16  
    17  // Files in package are to a large extent based on Go files from the following
    18  // Go packages:
    19  //    - cmd/go/internal/load
    20  //    - go/build
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  
    26  	"cuelang.org/go/cue/ast"
    27  	"cuelang.org/go/cue/build"
    28  	"cuelang.org/go/internal/cueexperiment"
    29  	"cuelang.org/go/internal/filetypes"
    30  	"cuelang.org/go/internal/mod/modimports"
    31  	"cuelang.org/go/internal/mod/modpkgload"
    32  	"cuelang.org/go/internal/mod/modrequirements"
    33  	"cuelang.org/go/mod/module"
    34  
    35  	// Trigger the unconditional loading of all core builtin packages if load
    36  	// is used. This was deemed the simplest way to avoid having to import
    37  	// this line explicitly, and thus breaking existing code, for the majority
    38  	// of cases, while not introducing an import cycle.
    39  	_ "cuelang.org/go/pkg"
    40  )
    41  
    42  // Instances returns the instances named by the command line arguments 'args'.
    43  // If errors occur trying to load an instance it is returned with Incomplete
    44  // set. Errors directly related to loading the instance are recorded in this
    45  // instance, but errors that occur loading dependencies are recorded in these
    46  // dependencies.
    47  func Instances(args []string, c *Config) []*build.Instance {
    48  	ctx := context.TODO()
    49  	if c == nil {
    50  		c = &Config{}
    51  	}
    52  	// We want to consult the CUE_EXPERIMENT flag to see whether
    53  	// consult external registries by default.
    54  	if err := cueexperiment.Init(); err != nil {
    55  		return []*build.Instance{c.newErrInstance(err)}
    56  	}
    57  	newC, err := c.complete()
    58  	if err != nil {
    59  		return []*build.Instance{c.newErrInstance(err)}
    60  	}
    61  	c = newC
    62  
    63  	// TODO: This requires packages to be placed before files. At some point this
    64  	// could be relaxed.
    65  	i := 0
    66  	for ; i < len(args) && filetypes.IsPackage(args[i]); i++ {
    67  	}
    68  	pkgArgs := args[:i]
    69  	otherArgs := args[i:]
    70  
    71  	// Pass all arguments that look like packages to loadPackages
    72  	// so that they'll be available when looking up the packages
    73  	// that are specified on the command line.
    74  	// Relative import paths create a package with an associated
    75  	// error but it turns out that's actually OK because the cue/load
    76  	// logic resolves such paths without consulting pkgs.
    77  	pkgs, err := loadPackages(ctx, c, pkgArgs)
    78  	if err != nil {
    79  		return []*build.Instance{c.newErrInstance(err)}
    80  	}
    81  	tg := newTagger(c)
    82  	l := newLoader(c, tg, pkgs)
    83  
    84  	if c.Context == nil {
    85  		c.Context = build.NewContext(
    86  			build.Loader(l.buildLoadFunc()),
    87  			build.ParseFile(c.ParseFile),
    88  		)
    89  	}
    90  
    91  	a := []*build.Instance{}
    92  	if len(args) == 0 || i > 0 {
    93  		for _, m := range l.importPaths(pkgArgs) {
    94  			if m.Err != nil {
    95  				inst := c.newErrInstance(m.Err)
    96  				a = append(a, inst)
    97  				continue
    98  			}
    99  			a = append(a, m.Pkgs...)
   100  		}
   101  	}
   102  
   103  	if len(otherArgs) > 0 {
   104  		files, err := filetypes.ParseArgs(otherArgs)
   105  		if err != nil {
   106  			return []*build.Instance{c.newErrInstance(err)}
   107  		}
   108  		a = append(a, l.cueFilesPackage(files))
   109  	}
   110  
   111  	for _, p := range a {
   112  		tags, err := findTags(p)
   113  		if err != nil {
   114  			p.ReportError(err)
   115  		}
   116  		tg.tags = append(tg.tags, tags...)
   117  	}
   118  
   119  	// TODO(api): have API call that returns an error which is the aggregate
   120  	// of all build errors. Certain errors, like these, hold across builds.
   121  	if err := tg.injectTags(c.Tags); err != nil {
   122  		for _, p := range a {
   123  			p.ReportError(err)
   124  		}
   125  		return a
   126  	}
   127  
   128  	if tg.replacements == nil {
   129  		return a
   130  	}
   131  
   132  	for _, p := range a {
   133  		for _, f := range p.Files {
   134  			ast.Walk(f, nil, func(n ast.Node) {
   135  				if ident, ok := n.(*ast.Ident); ok {
   136  					if v, ok := tg.replacements[ident.Node]; ok {
   137  						ident.Node = v
   138  					}
   139  				}
   140  			})
   141  		}
   142  	}
   143  
   144  	return a
   145  }
   146  
   147  func loadPackages(ctx context.Context, cfg *Config, extraPkgs []string) (*modpkgload.Packages, error) {
   148  	if cfg.Registry == nil || cfg.modFile == nil || cfg.modFile.Module == "" {
   149  		return nil, nil
   150  	}
   151  	reqs := modrequirements.NewRequirements(
   152  		cfg.modFile.Module,
   153  		cfg.Registry,
   154  		cfg.modFile.DepVersions(),
   155  		cfg.modFile.DefaultMajorVersions(),
   156  	)
   157  	mainModLoc := module.SourceLoc{
   158  		FS:  cfg.fileSystem.ioFS(cfg.ModuleRoot),
   159  		Dir: ".",
   160  	}
   161  	allImports, err := modimports.AllImports(modimports.AllModuleFiles(mainModLoc.FS, mainModLoc.Dir))
   162  	if err != nil {
   163  		return nil, fmt.Errorf("cannot enumerate all module imports: %v", err)
   164  	}
   165  	// Add any packages specified on the command line so they're always
   166  	// available.
   167  	allImports = append(allImports, extraPkgs...)
   168  	return modpkgload.LoadPackages(
   169  		ctx,
   170  		cfg.Module,
   171  		mainModLoc,
   172  		reqs,
   173  		cfg.Registry,
   174  		allImports,
   175  	), nil
   176  }
   177  

View as plain text