Skip to content
This repository has been archived by the owner on Nov 18, 2021. It is now read-only.

Commit

Permalink
cue/load: implement package name qualifiers
Browse files Browse the repository at this point in the history
Implements the spec.
The package name of imported files now _must_ be
included in the import path. If it it doesn't match
the last path component of the import path, it can
be modified with a :<name> at the end.

Closes #77
#77

GitOrigin-RevId: d2ad114
Change-Id: I6bec03ed931e4007bc85ece6b73a1ad89c0e0952
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3000
Reviewed-by: Marcel van Lohuizen <[email protected]>
  • Loading branch information
mpvl committed Aug 25, 2019
1 parent 6a9badd commit 96ef9aa
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 191 deletions.
2 changes: 1 addition & 1 deletion cue/build/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (inst *Instance) complete() errors.Error {
continue
}
if imp.Err != nil {
return nil
return imp.Err
}
imp.ImportPath = path
// imp.parent = inst
Expand Down
18 changes: 18 additions & 0 deletions cue/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,24 @@ func appendToList(a list, err Error) list {
// The zero value for an list is an empty list ready to use.
type list []Error

func (p list) Is(err, target error) bool {
for _, e := range p {
if xerrors.Is(e, target) {
return true
}
}
return false
}

func (p list) As(err error, target interface{}) bool {
for _, e := range p {
if xerrors.As(e, target) {
return true
}
}
return false
}

// AddNewf adds an Error with given position and error message to an List.
func (p *list) AddNewf(pos token.Pos, msg string, args ...interface{}) {
err := &posError{pos: pos, Message: Message{format: msg, args: args}}
Expand Down
195 changes: 183 additions & 12 deletions cue/load/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ package load

import (
"os"
pathpkg "path"
"path/filepath"
"runtime"
"strings"

"cuelang.org/go/cue"
"cuelang.org/go/cue/build"
Expand All @@ -29,7 +31,7 @@ const (
cueSuffix = ".cue"
defaultDir = "cue"
modFile = "cue.mod"
pkgDir = "pkg" // TODO: vendor?
pkgDir = "pkg" // TODO: vendor, third_party, _imports?
)

// FromArgsUsage is a partial usage message that applications calling
Expand Down Expand Up @@ -133,21 +135,186 @@ type Config struct {
Overlay map[string]Source

fileSystem

loadFunc build.LoadFunc
}

func (c Config) newInstance(path string) *build.Instance {
i := c.Context.NewInstance(path, nil)
i.DisplayPath = path
func (c *Config) newInstance(pos token.Pos, p importPath) *build.Instance {
dir, name, err := c.absDirFromImportPath(pos, p)
i := c.Context.NewInstance(dir, c.loadFunc)
i.Dir = dir
i.PkgName = name
i.DisplayPath = string(p)
i.ImportPath = string(p)
i.Root = c.ModuleRoot
i.Module = c.Module
i.Err = errors.Append(i.Err, err)

return i
}

func (c Config) newErrInstance(m *match, path string, err error) *build.Instance {
i := c.Context.NewInstance(path, nil)
i.DisplayPath = path
i.ReportError(errors.Promote(err, "instance"))
func (c *Config) newRelInstance(pos token.Pos, path string) *build.Instance {
fs := c.fileSystem

var err errors.Error
dir := path

p := c.Context.NewInstance(path, c.loadFunc)
p.PkgName = c.Package
p.DisplayPath = filepath.ToSlash(path)
// p.ImportPath = string(dir) // compute unique ID.
p.Root = c.ModuleRoot
p.Module = c.Module

if isLocalImport(path) {
p.Local = true
if c.Dir == "" {
err = errors.Append(err, errors.Newf(pos, "cwd unknown"))
}
dir = filepath.Join(c.Dir, filepath.FromSlash(path))
}

if path == "" {
err = errors.Append(err, errors.Newf(pos,
"import %q: invalid import path", path))
} else if path != cleanImport(path) {
err = errors.Append(err, c.loader.errPkgf(nil,
"non-canonical import path: %q should be %q", path, pathpkg.Clean(path)))
}

if importPath, e := c.importPathFromAbsDir(fsPath(dir), path, c.Package); e != nil {
// Detect later to keep error messages consistent.
} else {
p.ImportPath = string(importPath)
}

p.Dir = dir

if fs.isAbsPath(path) || strings.HasPrefix(path, "/") {
err = errors.Append(err, errors.Newf(pos,
"absolute import path %q not allowed", path))
}
if err != nil {
p.Err = errors.Append(p.Err, err)
p.Incomplete = true
}

return p
}

func (c Config) newErrInstance(pos token.Pos, path importPath, err error) *build.Instance {
i := c.newInstance(pos, path)
i.Err = errors.Promote(err, "instance")
return i
}

func toImportPath(dir string) importPath {
return importPath(filepath.ToSlash(dir))
}

type importPath string

type fsPath string

func (c *Config) importPathFromAbsDir(absDir fsPath, key, name string) (importPath, errors.Error) {
if c.ModuleRoot == "" {
return "", errors.Newf(token.NoPos,
"cannot determine import path for %q (root undefined)", key)
}

dir := filepath.Clean(string(absDir))
if !strings.HasPrefix(dir, c.ModuleRoot) {
return "", errors.Newf(token.NoPos,
"cannot determine import path for %q (dir outside of root)", key)
}

pkg := filepath.ToSlash(dir[len(c.ModuleRoot):])
switch {
case strings.HasPrefix(pkg, "/pkg/"):
pkg = pkg[len("/pkg/"):]
if pkg == "" {
return "", errors.Newf(token.NoPos,
"invalid package %q (root of %s)", key, pkgDir)
}

case c.Module == "":
return "", errors.Newf(token.NoPos,
"cannot determine import path for %q (no module)", key)
default:
pkg = c.Module + pkg
}

return addImportQualifier(importPath(pkg), name)
}

func addImportQualifier(pkg importPath, name string) (importPath, errors.Error) {
if name != "" {
s := string(pkg)
if i := strings.LastIndexByte(s, '/'); i >= 0 {
s = s[i+1:]
}
if i := strings.LastIndexByte(s, ':'); i >= 0 {
// should never happen, but just in case.
s = s[i+1:]
if s != name {
return "", errors.Newf(token.NoPos,
"non-matching package names (%s != %s)", s, name)
}
} else if s != name {
pkg += importPath(":" + name)
}
}

return pkg, nil
}

// absDirFromImportPath converts a giving import path to an absolute directory
// and a package name. The root directory must be set.
//
// The returned directory may not exist.
func (c *Config) absDirFromImportPath(pos token.Pos, p importPath) (absDir, name string, err errors.Error) {
if c.ModuleRoot == "" {
return "", "", errors.Newf(pos, "cannot import %q (root undefined)", p)
}

// Extract the package name.

name = string(p)
switch i := strings.LastIndexAny(name, "/:"); {
case i < 0:
case p[i] == ':':
name = string(p[i+1:])
p = p[:i]

default: // p[i] == '/'
name = string(p[i+1:])
}

// TODO: fully test that name is a valid identifier.
if name == "" {
err = errors.Newf(pos, "empty package name in import path %q", p)
} else if strings.IndexByte(name, '.') >= 0 {
err = errors.Newf(pos,
"cannot determine package name for %q (set explicitly with ':')", p)
}

// Determine the directory.

sub := filepath.FromSlash(string(p))
switch hasPrefix := strings.HasPrefix(string(p), c.Module); {
case hasPrefix && len(sub) == len(c.Module):
absDir = c.ModuleRoot

case hasPrefix && p[len(c.Module)] == '/':
absDir = filepath.Join(c.ModuleRoot, sub[len(c.Module)+1:])

default:
absDir = filepath.Join(c.ModuleRoot, "pkg", sub)
}

return absDir, name, nil
}

// Complete updates the configuration information. After calling complete,
// the following invariants hold:
// - c.ModuleRoot != ""
Expand All @@ -168,6 +335,8 @@ func (c Config) complete() (cfg *Config, err error) {
if err != nil {
return nil, err
}
} else if c.Dir, err = filepath.Abs(c.Dir); err != nil {
return nil, err
}

// TODO: we could populate this already with absolute file paths,
Expand All @@ -191,10 +360,6 @@ func (c Config) complete() (cfg *Config, err error) {

c.loader = &loader{cfg: &c}

if c.Context == nil {
c.Context = build.NewContext(build.Loader(c.loader.loadFunc(c.Dir)))
}

if c.cache == "" {
c.cache = filepath.Join(home(), defaultDir)
}
Expand Down Expand Up @@ -225,6 +390,12 @@ func (c Config) complete() (cfg *Config, err error) {
}
}

c.loadFunc = c.loader.loadFunc()

if c.Context == nil {
c.Context = build.NewContext(build.Loader(c.loadFunc))
}

return &c, nil
}

Expand Down
4 changes: 0 additions & 4 deletions cue/load/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ func (fs *fileSystem) init(c *Config) error {

// Organize overlay
for filename, src := range overlay {
if !strings.HasSuffix(filename, ".cue") {
return errors.Newf(token.NoPos, "overlay file %s not a .cue file", filename)
}

// TODO: do we need to further clean the path or check that the
// specified files are within the root/ absolute files?
dir, base := filepath.Split(filename)
Expand Down
Loading

0 comments on commit 96ef9aa

Please sign in to comment.