cue/load: provide reason for exclusion of files
Fixes #741
Issue #52
Change-Id: I8b61262be1fec41cdafd5ff78ee096a6dd6893fd
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9682
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
diff --git a/cmd/cue/cmd/testdata/script/eval_hiddenfail.txt b/cmd/cue/cmd/testdata/script/eval_hiddenfail.txt
index c12d92f..d856bec 100644
--- a/cmd/cue/cmd/testdata/script/eval_hiddenfail.txt
+++ b/cmd/cue/cmd/testdata/script/eval_hiddenfail.txt
@@ -2,7 +2,8 @@
cmp stderr expect-stderr
-- expect-stderr --
-build constraints exclude all CUE files in . (ignored: .foo.cue)
+build constraints exclude all CUE files in .:
+ .foo.cue: filename starts with a '.'
-- .foo.cue --
package foo
diff --git a/cmd/cue/cmd/testdata/script/issue174.txt b/cmd/cue/cmd/testdata/script/issue174.txt
index a611481..a5f1cbd 100644
--- a/cmd/cue/cmd/testdata/script/issue174.txt
+++ b/cmd/cue/cmd/testdata/script/issue174.txt
@@ -1,7 +1,8 @@
! cue export ./issue174
cmp stderr expect-stderr
-- expect-stderr --
-build constraints exclude all CUE files in ./issue174 (ignored: issue174/issue174.cue)
+build constraints exclude all CUE files in ./issue174:
+ issue174/issue174.cue: no package name
-- issue174/issue174.cue --
import 'foo'
diff --git a/cue/build/file.go b/cue/build/file.go
index 7a5d6d7..7b22d2e 100644
--- a/cue/build/file.go
+++ b/cue/build/file.go
@@ -14,6 +14,8 @@
package build
+import "cuelang.org/go/cue/errors"
+
// A File represents a file that is part of the build process.
type File struct {
Filename string `json:"filename"`
@@ -23,7 +25,8 @@
Form Form `json:"form,omitempty"`
Tags map[string]string `json:"tags,omitempty"` // code=go
- Source interface{} `json:"-"` // TODO: swap out with concrete type.
+ ExcludeReason errors.Error `json:"-"`
+ Source interface{} `json:"-"` // TODO: swap out with concrete type.
}
// A Encoding indicates a file format for representing a program.
diff --git a/cue/errors/errors.go b/cue/errors/errors.go
index e8e5a0e..476d586 100644
--- a/cue/errors/errors.go
+++ b/cue/errors/errors.go
@@ -212,6 +212,14 @@
}
}
+func (e *wrapped) Is(target error) bool {
+ return Is(e.main, target)
+}
+
+func (e *wrapped) As(target interface{}) bool {
+ return As(e.main, target)
+}
+
func (e *wrapped) Msg() (format string, args []interface{}) {
return e.main.Msg()
}
diff --git a/cue/load/errors.go b/cue/load/errors.go
index 0b74316..541c94a 100644
--- a/cue/load/errors.go
+++ b/cue/load/errors.go
@@ -115,19 +115,20 @@
path := e.Package.DisplayPath
if len(e.Package.IgnoredFiles) > dummy {
+ b := strings.Builder{}
+ b.WriteString("build constraints exclude all CUE files in ")
+ b.WriteString(path)
+ b.WriteString(":")
// CUE files exist, but they were ignored due to build constraints.
- msg := "build constraints exclude all CUE files in " + path + " (ignored: "
- var files []string
- for i, f := range e.Package.IgnoredFiles {
- if i == 4 {
- files = append(files[:4], "...")
- break
+ for _, f := range e.Package.IgnoredFiles {
+ b.WriteString("\n ")
+ b.WriteString(filepath.ToSlash(e.Package.RelPath(f)))
+ if f.ExcludeReason != nil {
+ b.WriteString(": ")
+ b.WriteString(f.ExcludeReason.Error())
}
- files = append(files, filepath.ToSlash(e.Package.RelPath(f)))
}
- msg += strings.Join(files, ", ")
- msg += ")"
- return msg
+ return b.String()
}
// if len(e.Package.TestCUEFiles) > 0 {
// // Test CUE files exist, but we're not interested in them.
diff --git a/cue/load/import.go b/cue/load/import.go
index 657bcad..1f43a3d 100644
--- a/cue/load/import.go
+++ b/cue/load/import.go
@@ -185,7 +185,8 @@
file, err := filetypes.ParseFile(f.Name(), filetypes.Input)
if err != nil {
p.UnknownFiles = append(p.UnknownFiles, &build.File{
- Filename: f.Name(),
+ Filename: f.Name(),
+ ExcludeReason: errors.Newf(token.NoPos, "unknown filetype"),
})
continue // skip unrecognized file types
}
@@ -356,21 +357,31 @@
// badFile := func(p *build.Instance, err errors.Error) bool {
badFile := func(err errors.Error) bool {
fp.err = errors.Append(fp.err, err)
+ file.ExcludeReason = fp.err
p.InvalidFiles = append(p.InvalidFiles, file)
return true
}
match, data, err := matchFile(fp.c, file, true, fp.allFiles, fp.allTags)
- if err != nil {
+ switch {
+ case match:
+
+ case err == nil:
+ // Not a CUE file.
+ p.OrphanedFiles = append(p.OrphanedFiles, file)
+ return false
+
+ case !errors.Is(err, errExclude):
return badFile(err)
- }
- if !match {
- if file.Encoding == build.CUE && file.Interpretation == "" {
+
+ default:
+ file.ExcludeReason = err
+ if file.Interpretation == "" {
p.IgnoredFiles = append(p.IgnoredFiles, file)
} else {
p.OrphanedFiles = append(p.OrphanedFiles, file)
}
- return false // don't mark as added
+ return false
}
pf, perr := parser.ParseFile(fullPath, data, parser.ImportsOnly, parser.ParseComments)
@@ -379,7 +390,7 @@
return true
}
- _, pkg, _ := internal.PackageInfo(pf)
+ _, pkg, pos := internal.PackageInfo(pf)
if pkg == "" {
pkg = "_"
}
@@ -405,15 +416,17 @@
case pkg != "_":
default:
+ file.ExcludeReason = excludeError{errors.Newf(pos, "no package name")}
p.IgnoredFiles = append(p.IgnoredFiles, file)
return false // don't mark as added
}
if !fp.c.AllCUEFiles {
- if include, err := shouldBuildFile(pf, fp); !include {
- if err != nil {
+ if err := shouldBuildFile(pf, fp); err != nil {
+ if !errors.Is(err, errExclude) {
fp.err = errors.Append(fp.err, err)
}
+ file.ExcludeReason = err
p.IgnoredFiles = append(p.IgnoredFiles, file)
return false
}
@@ -425,6 +438,8 @@
fp.firstFile = base
} else if pkg != p.PkgName {
if fp.ignoreOther {
+ file.ExcludeReason = excludeError{errors.Newf(pos,
+ "package is %s, want %s", pkg, p.PkgName)}
p.IgnoredFiles = append(p.IgnoredFiles, file)
return false
}
@@ -478,12 +493,16 @@
if fp.c.loader.cfg.Tests {
p.BuildFiles = append(p.BuildFiles, file)
} else {
+ file.ExcludeReason = excludeError{errors.Newf(pos,
+ "_test.cue files excluded in non-test mode")}
p.IgnoredFiles = append(p.IgnoredFiles, file)
}
case isTool:
if fp.c.loader.cfg.Tools {
p.BuildFiles = append(p.BuildFiles, file)
} else {
+ file.ExcludeReason = excludeError{errors.Newf(pos,
+ "_tool.cue files excluded in non-cmd mode")}
p.IgnoredFiles = append(p.IgnoredFiles, file)
}
default:
diff --git a/cue/load/loader_test.go b/cue/load/loader_test.go
index 43b9be2..2fe5f24 100644
--- a/cue/load/loader_test.go
+++ b/cue/load/loader_test.go
@@ -96,7 +96,8 @@
cfg: dirCfg,
args: args("./anon"),
want: `
-err: build constraints exclude all CUE files in ./anon (ignored: anon/anon.cue)
+err: build constraints exclude all CUE files in ./anon:
+ anon/anon.cue: no package name
path: example.org/test/anon
module: example.org/test
root: $CWD/testdata
@@ -154,7 +155,10 @@
cfg: dirCfg,
args: args("example.org/test/hello:nonexist"),
want: `
-err: build constraints exclude all CUE files in example.org/test/hello:nonexist (ignored: anon.cue, test.cue, hello/test.cue)
+err: build constraints exclude all CUE files in example.org/test/hello:nonexist:
+ anon.cue: no package name
+ test.cue: package is test, want nonexist
+ hello/test.cue: package is test, want nonexist
path: example.org/test/hello:nonexist
module: example.org/test
root: $CWD/testdata
@@ -247,7 +251,10 @@
},
args: args("./toolonly"),
want: `
-err: build constraints exclude all CUE files in ./toolonly (ignored: anon.cue, test.cue, toolonly/foo_tool.cue)
+err: build constraints exclude all CUE files in ./toolonly:
+ anon.cue: no package name
+ test.cue: package is test, want foo
+ toolonly/foo_tool.cue: _tool.cue files excluded in non-cmd mode
path: example.org/test/toolonly:foo
module: example.org/test
root: $CWD/testdata
diff --git a/cue/load/match.go b/cue/load/match.go
index 4ff21b4..c58e057 100644
--- a/cue/load/match.go
+++ b/cue/load/match.go
@@ -24,6 +24,15 @@
"cuelang.org/go/cue/token"
)
+var errExclude = errors.New("file rejected")
+
+type cueError = errors.Error
+type excludeError struct {
+ cueError
+}
+
+func (e excludeError) Is(err error) bool { return err == errExclude }
+
// matchFile determines whether the file with the given name in the given directory
// should be included in the package being constructed.
// It returns the data read from the file.
@@ -42,7 +51,7 @@
}
if file.Encoding != build.CUE {
- return
+ return false, nil, nil // not a CUE file, don't record.
}
if file.Filename == "-" {
@@ -52,32 +61,33 @@
return
}
file.Source = b
- data = b
- match = true // don't check shouldBuild for stdin
- return
+ return true, b, nil // don't check shouldBuild for stdin
}
name := filepath.Base(file.Filename)
if !cfg.filesMode && strings.HasPrefix(name, ".") {
- return
+ return false, nil, &excludeError{
+ errors.Newf(token.NoPos, "filename starts with a '.'"),
+ }
}
if strings.HasPrefix(name, "_") {
- return
+ return false, nil, &excludeError{
+ errors.Newf(token.NoPos, "filename starts with a '_"),
+ }
}
f, err := cfg.fileSystem.openFile(file.Filename)
if err != nil {
- return
+ return false, nil, err
}
data, err = readImports(f, false, nil)
f.Close()
if err != nil {
- err = errors.Newf(token.NoPos, "read %s: %v", file.Filename, err)
- return
+ return false, nil,
+ errors.Newf(token.NoPos, "read %s: %v", file.Filename, err)
}
- match = true
- return
+ return true, data, nil
}
diff --git a/cue/load/tags.go b/cue/load/tags.go
index a9ab269..65ed142 100644
--- a/cue/load/tags.go
+++ b/cue/load/tags.go
@@ -305,22 +305,22 @@
// shouldBuildFile determines whether a File should be included based on its
// attributes.
-func shouldBuildFile(f *ast.File, fp *fileProcessor) (bool, errors.Error) {
+func shouldBuildFile(f *ast.File, fp *fileProcessor) errors.Error {
tags := fp.c.Tags
a, errs := getBuildAttr(f)
if errs != nil {
- return false, errs
+ return errs
}
if a == nil {
- return true, nil
+ return nil
}
_, body := a.Split()
expr, err := parser.ParseExpr("", body)
if err != nil {
- return false, errors.Promote(err, "")
+ return errors.Promote(err, "")
}
tagMap := map[string]bool{}
@@ -331,9 +331,12 @@
c := checker{tags: tagMap, loader: fp.c.loader}
include := c.shouldInclude(expr)
if c.err != nil {
- return false, c.err
+ return c.err
}
- return include, nil
+ if !include {
+ return excludeError{errors.Newf(a.Pos(), "@if(%s) did not match", body)}
+ }
+ return nil
}
func getBuildAttr(f *ast.File) (*ast.Attribute, errors.Error) {