blob: 6f77495f0ed612d76cfd364b835593f156967cba [file] [log] [blame]
Marcel van Lohuizenbc4d65d2018-12-10 15:40:02 +01001// 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
15package load
16
17import (
18 "fmt"
19 "path/filepath"
20 "strings"
21
22 build "cuelang.org/go/cue/build"
23 "cuelang.org/go/cue/token"
24)
25
26func lastError(p *build.Instance) *packageError {
27 if p == nil {
28 return nil
29 }
30 switch v := p.Err.(type) {
31 case *packageError:
32 return v
33 }
34 return nil
35}
36
37func report(p *build.Instance, err *packageError) {
38 if err != nil {
39 p.ReportError(err)
40 }
41}
42
43// shortPath returns an absolute or relative name for path, whatever is shorter.
44func shortPath(cwd, path string) string {
45 if cwd == "" {
46 return path
47 }
48 if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {
49 return rel
50 }
51 return path
52}
53
54// A packageError describes an error loading information about a package.
55type packageError struct {
56 ImportStack []string // shortest path from package named on command line to this one
57 Pos string // position of error
58 Err string // the error itself
59 IsImportCycle bool `json:"-"` // the error is an import cycle
60 Hard bool `json:"-"` // whether the error is soft or hard; soft errors are ignored in some places
61}
62
63func (l *loader) errPkgf(importPos []token.Position, format string, args ...interface{}) *packageError {
64 err := &packageError{
65 ImportStack: l.stk.Copy(),
66 Err: fmt.Sprintf(format, args...),
67 }
68 err.fillPos(l.cfg.Dir, importPos)
69 return err
70}
71
72func (p *packageError) fillPos(cwd string, positions []token.Position) {
73 if len(positions) > 0 && p.Pos == "" {
74 pos := positions[0]
75 pos.Filename = shortPath(cwd, pos.Filename)
76 p.Pos = pos.String()
77 }
78}
79
80func (p *packageError) Error() string {
81 // Import cycles deserve special treatment.
82 if p.IsImportCycle {
83 return fmt.Sprintf("%s\npackage %s\n", p.Err, strings.Join(p.ImportStack, "\n\timports "))
84 }
85 if p.Pos != "" {
86 // Omit import stack. The full path to the file where the error
87 // is the most important thing.
88 return p.Pos + ": " + p.Err
89 }
90 if len(p.ImportStack) == 0 {
91 return p.Err
92 }
93 return "package " + strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err
94}
95
96// noCUEError is the error used by Import to describe a directory
97// containing no buildable Go source files. (It may still contain
98// test files, files hidden by build tags, and so on.)
99type noCUEError struct {
100 Package *build.Instance
101
102 Dir string
103 Ignored bool // whether any Go files were ignored due to build tags
104}
105
106// func (e *noCUEError) Error() string {
107// msg := "no buildable CUE config files in " + e.Dir
108// if e.Ignored {
109// msg += " (.cue files ignored due to build tags)"
110// }
111// return msg
112// }
113
114func (e *noCUEError) Error() string {
115 // Count files beginning with _ and ., which we will pretend don't exist at all.
116 dummy := 0
117 for _, name := range e.Package.IgnoredCUEFiles {
118 if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") {
119 dummy++
120 }
121 }
122
123 // path := shortPath(e.Package.Root, e.Package.Dir)
124 path := e.Package.DisplayPath
125
126 if len(e.Package.IgnoredCUEFiles) > dummy {
127 // CUE files exist, but they were ignored due to build constraints.
128 return "build constraints exclude all CUE files in " + path
129 }
130 // if len(e.Package.TestCUEFiles) > 0 {
131 // // Test CUE files exist, but we're not interested in them.
132 // // The double-negative is unfortunate but we want e.Package.Dir
133 // // to appear at the end of error message.
134 // return "no non-test CUE files in " + e.Package.Dir
135 // }
136 return "no CUE files in " + path
137}
138
139// multiplePackageError describes a directory containing
140// multiple buildable Go source files for multiple packages.
141type multiplePackageError struct {
142 Dir string // directory containing files
143 Packages []string // package names found
144 Files []string // corresponding files: Files[i] declares package Packages[i]
145}
146
147func (e *multiplePackageError) Error() string {
148 // Error string limited to two entries for compatibility.
149 return fmt.Sprintf("found packages %s (%s) and %s (%s) in %s", e.Packages[0], e.Files[0], e.Packages[1], e.Files[1], e.Dir)
150}