| @@ -1,747 +1,747 @@ | | | @@ -1,747 +1,747 @@ |
1 | package main | | 1 | package main |
2 | | | 2 | |
3 | import ( | | 3 | import ( |
4 | "path" | | 4 | "path" |
5 | "strings" | | 5 | "strings" |
6 | ) | | 6 | ) |
7 | | | 7 | |
8 | type VartypeCheck struct { | | 8 | type VartypeCheck struct { |
9 | line *Line | | 9 | line *Line |
10 | varname string | | 10 | varname string |
11 | op string | | 11 | op string |
12 | value string | | 12 | value string |
13 | valueNovar string | | 13 | valueNovar string |
14 | comment string | | 14 | comment string |
15 | listContext bool | | 15 | listContext bool |
16 | guessed Guessed | | 16 | guessed Guessed |
17 | } | | 17 | } |
18 | | | 18 | |
19 | func (cv *VartypeCheck) AwkCommand() { | | 19 | func (cv *VartypeCheck) AwkCommand() { |
20 | _ = G.opts.DebugUnchecked && cv.line.debugf("Unchecked AWK command: %q", cv.value) | | 20 | _ = G.opts.DebugUnchecked && cv.line.debugf("Unchecked AWK command: %q", cv.value) |
21 | } | | 21 | } |
22 | | | 22 | |
23 | func (cv *VartypeCheck) BasicRegularExpression() { | | 23 | func (cv *VartypeCheck) BasicRegularExpression() { |
24 | _ = G.opts.DebugUnchecked && cv.line.debugf("Unchecked basic regular expression: %q", cv.value) | | 24 | _ = G.opts.DebugUnchecked && cv.line.debugf("Unchecked basic regular expression: %q", cv.value) |
25 | } | | 25 | } |
26 | | | 26 | |
27 | func (cv *VartypeCheck) BuildlinkDepmethod() { | | 27 | func (cv *VartypeCheck) BuildlinkDepmethod() { |
28 | if !containsVarRef(cv.value) && cv.value != "build" && cv.value != "full" { | | 28 | if !containsVarRef(cv.value) && cv.value != "build" && cv.value != "full" { |
29 | cv.line.warnf("Invalid dependency method %q. Valid methods are \"build\" or \"full\".", cv.value) | | 29 | cv.line.warnf("Invalid dependency method %q. Valid methods are \"build\" or \"full\".", cv.value) |
30 | } | | 30 | } |
31 | } | | 31 | } |
32 | | | 32 | |
33 | func (cv *VartypeCheck) Category() { | | 33 | func (cv *VartypeCheck) Category() { |
34 | if fileExists(G.currentDir + "/" + G.curPkgsrcdir + "/" + cv.value + "/Makefile") { | | 34 | if fileExists(G.currentDir + "/" + G.curPkgsrcdir + "/" + cv.value + "/Makefile") { |
35 | return | | 35 | return |
36 | } | | 36 | } |
37 | switch cv.value { | | 37 | switch cv.value { |
38 | case | | 38 | case |
39 | "chinese", "crosspkgtools", | | 39 | "chinese", "crosspkgtools", |
40 | "gnome", "gnustep", | | 40 | "gnome", "gnustep", |
41 | "japanese", "java", | | 41 | "japanese", "java", |
42 | "kde", "korean", | | 42 | "kde", "korean", |
43 | "linux", "local", | | 43 | "linux", "local", |
44 | "packages", "perl5", "plan9", "python", | | 44 | "packages", "perl5", "plan9", "python", |
45 | "ruby", | | 45 | "ruby", |
46 | "scm", | | 46 | "scm", |
47 | "tcl", "tk", | | 47 | "tcl", "tk", |
48 | "windowmaker", | | 48 | "windowmaker", |
49 | "xmms": | | 49 | "xmms": |
50 | default: | | 50 | default: |
51 | cv.line.errorf("Invalid category %q.", cv.value) | | 51 | cv.line.errorf("Invalid category %q.", cv.value) |
52 | } | | 52 | } |
53 | } | | 53 | } |
54 | | | 54 | |
55 | // A single option to the C/C++ compiler. | | 55 | // A single option to the C/C++ compiler. |
56 | func (cv *VartypeCheck) CFlag() { | | 56 | func (cv *VartypeCheck) CFlag() { |
57 | line, value := cv.line, cv.value | | 57 | line, value := cv.line, cv.value |
58 | | | 58 | |
59 | switch { | | 59 | switch { |
60 | case matches(value, `^-[DILOUWfgm]`), | | 60 | case matches(value, `^-[DILOUWfgm]`), |
61 | hasPrefix(value, "-std="), | | 61 | hasPrefix(value, "-std="), |
62 | value == "-c99": | | 62 | value == "-c99": |
63 | case hasPrefix(value, "-"): | | 63 | case hasPrefix(value, "-"): |
64 | line.warnf("Unknown compiler flag %q.", value) | | 64 | line.warnf("Unknown compiler flag %q.", value) |
65 | case !containsVarRef(value): | | 65 | case !containsVarRef(value): |
66 | line.warnf("Compiler flag %q should start with a hyphen.", value) | | 66 | line.warnf("Compiler flag %q should start with a hyphen.", value) |
67 | } | | 67 | } |
68 | } | | 68 | } |
69 | | | 69 | |
70 | // The single-line description of the package. | | 70 | // The single-line description of the package. |
71 | func (cv *VartypeCheck) Comment() { | | 71 | func (cv *VartypeCheck) Comment() { |
72 | line, value := cv.line, cv.value | | 72 | line, value := cv.line, cv.value |
73 | | | 73 | |
74 | if value == "SHORT_DESCRIPTION_OF_THE_PACKAGE" { | | 74 | if value == "SHORT_DESCRIPTION_OF_THE_PACKAGE" { |
75 | line.errorf("COMMENT must be set.") | | 75 | line.errorf("COMMENT must be set.") |
76 | } | | 76 | } |
77 | if m, first := match1(value, `^(?i)(a|an)\s`); m { | | 77 | if m, first := match1(value, `^(?i)(a|an)\s`); m { |
78 | line.warnf("COMMENT should not begin with %q.", first) | | 78 | line.warnf("COMMENT should not begin with %q.", first) |
79 | } | | 79 | } |
80 | if matches(value, `^[a-z]`) { | | 80 | if matches(value, `^[a-z]`) { |
81 | line.warnf("COMMENT should start with a capital letter.") | | 81 | line.warnf("COMMENT should start with a capital letter.") |
82 | } | | 82 | } |
83 | if hasSuffix(value, ".") { | | 83 | if hasSuffix(value, ".") { |
84 | line.warnf("COMMENT should not end with a period.") | | 84 | line.warnf("COMMENT should not end with a period.") |
85 | } | | 85 | } |
86 | if len(value) > 70 { | | 86 | if len(value) > 70 { |
87 | line.warnf("COMMENT should not be longer than 70 characters.") | | 87 | line.warnf("COMMENT should not be longer than 70 characters.") |
88 | } | | 88 | } |
89 | } | | 89 | } |
90 | | | 90 | |
91 | func (cv *VartypeCheck) Dependency() { | | 91 | func (cv *VartypeCheck) Dependency() { |
92 | line, value := cv.line, cv.value | | 92 | line, value := cv.line, cv.value |
93 | | | 93 | |
94 | if m, depbase, depop, depversion := match3(value, `^(`+rePkgbase+`)(<|=|>|<=|>=|!=|-)(`+rePkgversion+`)$`); m { | | 94 | if m, depbase, depop, depversion := match3(value, `^(`+rePkgbase+`)(<|=|>|<=|>=|!=|-)(`+rePkgversion+`)$`); m { |
95 | _, _, _ = depbase, depop, depversion | | 95 | _, _, _ = depbase, depop, depversion |
96 | return | | 96 | return |
97 | } | | 97 | } |
98 | | | 98 | |
99 | if m, depbase, bracket, version, versionWildcard, other := match5(value, `^(`+rePkgbase+`)-(?:\[(.*)\]\*|(\d+(?:\.\d+)*(?:\.\*)?)(\{,nb\*\}|\*|)|(.*))?$`); m { | | 99 | if m, depbase, bracket, version, versionWildcard, other := match5(value, `^(`+rePkgbase+`)-(?:\[(.*)\]\*|(\d+(?:\.\d+)*(?:\.\*)?)(\{,nb\*\}|\*|)|(.*))?$`); m { |
100 | switch { | | 100 | switch { |
101 | case bracket != "": | | 101 | case bracket != "": |
102 | if bracket != "0-9" { | | 102 | if bracket != "0-9" { |
103 | line.warnf("Only [0-9]* is allowed in the numeric part of a dependency.") | | 103 | line.warnf("Only [0-9]* is allowed in the numeric part of a dependency.") |
104 | } | | 104 | } |
105 | | | 105 | |
106 | case version != "" && versionWildcard != "": | | 106 | case version != "" && versionWildcard != "": |
107 | // Fine. | | 107 | // Fine. |
108 | | | 108 | |
109 | case version != "": | | 109 | case version != "": |
110 | line.warnf("Please append \"{,nb*}\" to the version number of this dependency.") | | 110 | line.warnf("Please append \"{,nb*}\" to the version number of this dependency.") |
111 | line.explain( | | 111 | line.explain( |
112 | "Usually, a dependency should stay valid when the PKGREVISION is", | | 112 | "Usually, a dependency should stay valid when the PKGREVISION is", |
113 | "increased, since those changes are most often editorial. In the", | | 113 | "increased, since those changes are most often editorial. In the", |
114 | "current form, the dependency only matches if the PKGREVISION is", | | 114 | "current form, the dependency only matches if the PKGREVISION is", |
115 | "undefined.") | | 115 | "undefined.") |
116 | | | 116 | |
117 | case other == "*": | | 117 | case other == "*": |
118 | line.warnf("Please use \"%s-[0-9]*\" instead of \"%s-*\".", depbase, depbase) | | 118 | line.warnf("Please use \"%s-[0-9]*\" instead of \"%s-*\".", depbase, depbase) |
119 | line.explain( | | 119 | line.explain( |
120 | "If you use a * alone, the package specification may match other", | | 120 | "If you use a * alone, the package specification may match other", |
121 | "packages that have the same prefix, but a longer name. For example,", | | 121 | "packages that have the same prefix, but a longer name. For example,", |
122 | "foo-* matches foo-1.2, but also foo-client-1.2 and foo-server-1.2.") | | 122 | "foo-* matches foo-1.2, but also foo-client-1.2 and foo-server-1.2.") |
123 | | | 123 | |
124 | default: | | 124 | default: |
125 | line.errorf("Unknown dependency pattern %q.", value) | | 125 | line.errorf("Unknown dependency pattern %q.", value) |
126 | } | | 126 | } |
127 | return | | 127 | return |
128 | } | | 128 | } |
129 | | | 129 | |
130 | switch { | | 130 | switch { |
131 | case contains(value, "{"): | | 131 | case contains(value, "{"): |
132 | // No check yet for alternative dependency patterns. | | 132 | // No check yet for alternative dependency patterns. |
133 | _ = G.opts.DebugUnchecked && line.debugf("Unchecked alternative dependency pattern: %s", value) | | 133 | _ = G.opts.DebugUnchecked && line.debugf("Unchecked alternative dependency pattern: %s", value) |
134 | | | 134 | |
135 | case value != cv.valueNovar: | | 135 | case value != cv.valueNovar: |
136 | _ = G.opts.DebugUnchecked && line.debugf("Unchecked dependency: %s", value) | | 136 | _ = G.opts.DebugUnchecked && line.debugf("Unchecked dependency: %s", value) |
137 | | | 137 | |
138 | default: | | 138 | default: |
139 | line.warnf("Unknown dependency format: %s", value) | | 139 | line.warnf("Unknown dependency format: %s", value) |
140 | line.explain( | | 140 | line.explain( |
141 | "Typical dependencies have the following forms:", | | 141 | "Typical dependencies have the following forms:", |
142 | "", | | 142 | "", |
143 | "* package>=2.5", | | 143 | "* package>=2.5", |
144 | "* package-[0-9]*", | | 144 | "* package-[0-9]*", |
145 | "* package-3.141") | | 145 | "* package-3.141") |
146 | } | | 146 | } |
147 | } | | 147 | } |
148 | | | 148 | |
149 | func (cv *VartypeCheck) DependencyWithPath() { | | 149 | func (cv *VartypeCheck) DependencyWithPath() { |
150 | line, value := cv.line, cv.value | | 150 | line, value := cv.line, cv.value |
151 | if value != cv.valueNovar { | | 151 | if value != cv.valueNovar { |
152 | return // It's probably not worth checking this. | | 152 | return // It's probably not worth checking this. |
153 | } | | 153 | } |
154 | | | 154 | |
155 | if m, pattern, relpath, _, pkg := match4(value, `(.*):(\.\./\.\./([^/]+)/([^/]+))$`); m { | | 155 | if m, pattern, relpath, _, pkg := match4(value, `(.*):(\.\./\.\./([^/]+)/([^/]+))$`); m { |
156 | checklineRelativePkgdir(line, relpath) | | 156 | checklineRelativePkgdir(line, relpath) |
157 | | | 157 | |
158 | switch pkg { | | 158 | switch pkg { |
159 | case "msgfmt", "gettext": | | 159 | case "msgfmt", "gettext": |
160 | line.warnf("Please use USE_TOOLS+=msgfmt instead of this dependency.") | | 160 | line.warnf("Please use USE_TOOLS+=msgfmt instead of this dependency.") |
161 | case "perl5": | | 161 | case "perl5": |
162 | line.warnf("Please use USE_TOOLS+=perl:run instead of this dependency.") | | 162 | line.warnf("Please use USE_TOOLS+=perl:run instead of this dependency.") |
163 | case "gmake": | | 163 | case "gmake": |
164 | line.warnf("Please use USE_TOOLS+=gmake instead of this dependency.") | | 164 | line.warnf("Please use USE_TOOLS+=gmake instead of this dependency.") |
165 | } | | 165 | } |
166 | | | 166 | |
167 | if !matches(pattern, reDependencyCmp) && !matches(pattern, reDependencyWildcard) { | | 167 | if !matches(pattern, reDependencyCmp) && !matches(pattern, reDependencyWildcard) { |
168 | line.errorf("Unknown dependency pattern %q.", pattern) | | 168 | line.errorf("Unknown dependency pattern %q.", pattern) |
169 | } | | 169 | } |
170 | return | | 170 | return |
171 | } | | 171 | } |
172 | | | 172 | |
173 | if matches(value, `:\.\./[^/]+$`) { | | 173 | if matches(value, `:\.\./[^/]+$`) { |
174 | line.warnf("Dependencies should have the form \"../../category/package\".") | | 174 | line.warnf("Dependencies should have the form \"../../category/package\".") |
175 | explainRelativeDirs(line) | | 175 | explainRelativeDirs(line) |
176 | return | | 176 | return |
177 | } | | 177 | } |
178 | | | 178 | |
179 | line.warnf("Unknown dependency format.") | | 179 | line.warnf("Unknown dependency format.") |
180 | line.explain( | | 180 | line.explain( |
181 | "Examples for valid dependencies are:", | | 181 | "Examples for valid dependencies are:", |
182 | " package-[0-9]*:../../category/package", | | 182 | " package-[0-9]*:../../category/package", |
183 | " package>=3.41:../../category/package", | | 183 | " package>=3.41:../../category/package", |
184 | " package-2.718:../../category/package") | | 184 | " package-2.718:../../category/package") |
185 | } | | 185 | } |
186 | | | 186 | |
187 | func (cv *VartypeCheck) DistSuffix() { | | 187 | func (cv *VartypeCheck) DistSuffix() { |
188 | if cv.value == ".tar.gz" { | | 188 | if cv.value == ".tar.gz" { |
189 | cv.line.notef("%s is \".tar.gz\" by default, so this definition may be redundant.", cv.varname) | | 189 | cv.line.notef("%s is \".tar.gz\" by default, so this definition may be redundant.", cv.varname) |
190 | } | | 190 | } |
191 | } | | 191 | } |
192 | | | 192 | |
193 | func (cv *VartypeCheck) EmulPlatform() { | | 193 | func (cv *VartypeCheck) EmulPlatform() { |
194 | | | 194 | |
195 | if m, opsys, arch := match2(cv.value, `^(\w+)-(\w+)$`); m { | | 195 | if m, opsys, arch := match2(cv.value, `^(\w+)-(\w+)$`); m { |
196 | if !matches(opsys, `^(?:bsdos|cygwin|darwin|dragonfly|freebsd|haiku|hpux|interix|irix|linux|netbsd|openbsd|osf1|sunos|solaris)$`) { | | 196 | if !matches(opsys, `^(?:bitrig|bsdos|cygwin|darwin|dragonfly|freebsd|haiku|hpux|interix|irix|linux|mirbsd|netbsd|openbsd|osf1|sunos|solaris)$`) { |
197 | cv.line.warnf("Unknown operating system: %s", opsys) | | 197 | cv.line.warnf("Unknown operating system: %s", opsys) |
198 | } | | 198 | } |
199 | // no check for os_version | | 199 | // no check for os_version |
200 | if !matches(arch, `^(?:i386|alpha|amd64|arc|arm|arm32|cobalt|convex|dreamcast|hpcmips|hpcsh|hppa|ia64|m68k|m88k|mips|mips64|mipsel|mipseb|mipsn32|ns32k|pc532|pmax|powerpc|rs6000|s390|sparc|sparc64|vax|x86_64)$`) { | | 200 | if !matches(arch, `^(?:i386|alpha|amd64|arc|arm|arm32|cobalt|convex|dreamcast|hpcmips|hpcsh|hppa|ia64|m68k|m88k|mips|mips64|mipsel|mipseb|mipsn32|ns32k|pc532|pmax|powerpc|rs6000|s390|sparc|sparc64|vax|x86_64)$`) { |
201 | cv.line.warnf("Unknown hardware architecture: %s", arch) | | 201 | cv.line.warnf("Unknown hardware architecture: %s", arch) |
202 | } | | 202 | } |
203 | | | 203 | |
204 | } else { | | 204 | } else { |
205 | cv.line.warnf("%q is not a valid emulation platform.", cv.value) | | 205 | cv.line.warnf("%q is not a valid emulation platform.", cv.value) |
206 | cv.line.explain( | | 206 | cv.line.explain( |
207 | "An emulation platform has the form <OPSYS>-<MACHINE_ARCH>.", | | 207 | "An emulation platform has the form <OPSYS>-<MACHINE_ARCH>.", |
208 | "OPSYS is the lower-case name of the operating system, and MACHINE_ARCH", | | 208 | "OPSYS is the lower-case name of the operating system, and MACHINE_ARCH", |
209 | "is the hardware architecture.", | | 209 | "is the hardware architecture.", |
210 | "", | | 210 | "", |
211 | "Examples: linux-i386, irix-mipsel.") | | 211 | "Examples: linux-i386, irix-mipsel.") |
212 | } | | 212 | } |
213 | } | | 213 | } |
214 | | | 214 | |
215 | func (cv *VartypeCheck) FetchURL() { | | 215 | func (cv *VartypeCheck) FetchURL() { |
216 | NewMkLine(cv.line).checkVartypePrimitive(cv.varname, CheckvarURL, cv.op, cv.value, cv.comment, cv.listContext, cv.guessed) | | 216 | NewMkLine(cv.line).checkVartypePrimitive(cv.varname, CheckvarURL, cv.op, cv.value, cv.comment, cv.listContext, cv.guessed) |
217 | | | 217 | |
218 | for siteUrl, siteName := range G.globalData.masterSiteUrls { | | 218 | for siteUrl, siteName := range G.globalData.masterSiteUrls { |
219 | if hasPrefix(cv.value, siteUrl) { | | 219 | if hasPrefix(cv.value, siteUrl) { |
220 | subdir := cv.value[len(siteUrl):] | | 220 | subdir := cv.value[len(siteUrl):] |
221 | isGithub := hasPrefix(cv.value, "https://github.com/") | | 221 | isGithub := hasPrefix(cv.value, "https://github.com/") |
222 | if isGithub { | | 222 | if isGithub { |
223 | subdir = strings.SplitAfter(subdir, "/")[0] | | 223 | subdir = strings.SplitAfter(subdir, "/")[0] |
224 | } | | 224 | } |
225 | cv.line.warnf("Please use ${%s:=%s} instead of %q.", siteName, subdir, cv.value) | | 225 | cv.line.warnf("Please use ${%s:=%s} instead of %q.", siteName, subdir, cv.value) |
226 | if isGithub { | | 226 | if isGithub { |
227 | cv.line.warnf("Run \"%s help topic=github\" for further tips.", confMake) | | 227 | cv.line.warnf("Run \"%s help topic=github\" for further tips.", confMake) |
228 | } | | 228 | } |
229 | return | | 229 | return |
230 | } | | 230 | } |
231 | } | | 231 | } |
232 | } | | 232 | } |
233 | | | 233 | |
234 | // See Pathname | | 234 | // See Pathname |
235 | // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_169 | | 235 | // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_169 |
236 | func (cv *VartypeCheck) Filename() { | | 236 | func (cv *VartypeCheck) Filename() { |
237 | switch { | | 237 | switch { |
238 | case contains(cv.valueNovar, "/"): | | 238 | case contains(cv.valueNovar, "/"): |
239 | cv.line.warnf("A filename should not contain a slash.") | | 239 | cv.line.warnf("A filename should not contain a slash.") |
240 | case !matches(cv.valueNovar, `^[-0-9@A-Za-z.,_~+%]*$`): | | 240 | case !matches(cv.valueNovar, `^[-0-9@A-Za-z.,_~+%]*$`): |
241 | cv.line.warnf("%q is not a valid filename.", cv.value) | | 241 | cv.line.warnf("%q is not a valid filename.", cv.value) |
242 | } | | 242 | } |
243 | } | | 243 | } |
244 | | | 244 | |
245 | func (cv *VartypeCheck) Filemask() { | | 245 | func (cv *VartypeCheck) Filemask() { |
246 | if !matches(cv.valueNovar, `^[-0-9A-Za-z._~+%*?]*$`) { | | 246 | if !matches(cv.valueNovar, `^[-0-9A-Za-z._~+%*?]*$`) { |
247 | cv.line.warnf("%q is not a valid filename mask.", cv.value) | | 247 | cv.line.warnf("%q is not a valid filename mask.", cv.value) |
248 | } | | 248 | } |
249 | } | | 249 | } |
250 | | | 250 | |
251 | func (cv *VartypeCheck) FileMode() { | | 251 | func (cv *VartypeCheck) FileMode() { |
252 | switch { | | 252 | switch { |
253 | case cv.value != "" && cv.valueNovar == "": | | 253 | case cv.value != "" && cv.valueNovar == "": |
254 | // Fine. | | 254 | // Fine. |
255 | case matches(cv.value, `^[0-7]{3,4}`): | | 255 | case matches(cv.value, `^[0-7]{3,4}`): |
256 | // Fine. | | 256 | // Fine. |
257 | default: | | 257 | default: |
258 | cv.line.warnf("Invalid file mode %q.", cv.value) | | 258 | cv.line.warnf("Invalid file mode %q.", cv.value) |
259 | } | | 259 | } |
260 | } | | 260 | } |
261 | | | 261 | |
262 | func (cv *VartypeCheck) Identifier() { | | 262 | func (cv *VartypeCheck) Identifier() { |
263 | if cv.value != cv.valueNovar { | | 263 | if cv.value != cv.valueNovar { |
264 | //line.logWarning("Identifiers should be given directly.") | | 264 | //line.logWarning("Identifiers should be given directly.") |
265 | } | | 265 | } |
266 | switch { | | 266 | switch { |
267 | case matches(cv.valueNovar, `^[+\-.0-9A-Z_a-z]+$`): | | 267 | case matches(cv.valueNovar, `^[+\-.0-9A-Z_a-z]+$`): |
268 | // Fine. | | 268 | // Fine. |
269 | case cv.value != "" && cv.valueNovar == "": | | 269 | case cv.value != "" && cv.valueNovar == "": |
270 | // Don't warn here. | | 270 | // Don't warn here. |
271 | default: | | 271 | default: |
272 | cv.line.warnf("Invalid identifier %q.", cv.value) | | 272 | cv.line.warnf("Invalid identifier %q.", cv.value) |
273 | } | | 273 | } |
274 | } | | 274 | } |
275 | | | 275 | |
276 | func (cv *VartypeCheck) Integer() { | | 276 | func (cv *VartypeCheck) Integer() { |
277 | if !matches(cv.value, `^\d+$`) { | | 277 | if !matches(cv.value, `^\d+$`) { |
278 | cv.line.warnf("Invalid integer %q.", cv.value) | | 278 | cv.line.warnf("Invalid integer %q.", cv.value) |
279 | } | | 279 | } |
280 | } | | 280 | } |
281 | | | 281 | |
282 | func (cv *VartypeCheck) LdFlag() { | | 282 | func (cv *VartypeCheck) LdFlag() { |
283 | if matches(cv.value, `^-[Ll]`) || cv.value == "-static" { | | 283 | if matches(cv.value, `^-[Ll]`) || cv.value == "-static" { |
284 | return | | 284 | return |
285 | } else if m, rpathFlag := match1(cv.value, `^(-Wl,(?:-R|-rpath|--rpath))`); m { | | 285 | } else if m, rpathFlag := match1(cv.value, `^(-Wl,(?:-R|-rpath|--rpath))`); m { |
286 | cv.line.warnf("Please use ${COMPILER_RPATH_FLAG} instead of %s.", rpathFlag) | | 286 | cv.line.warnf("Please use ${COMPILER_RPATH_FLAG} instead of %s.", rpathFlag) |
287 | | | 287 | |
288 | } else if hasPrefix(cv.value, "-") { | | 288 | } else if hasPrefix(cv.value, "-") { |
289 | cv.line.warnf("Unknown linker flag %q.", cv.value) | | 289 | cv.line.warnf("Unknown linker flag %q.", cv.value) |
290 | | | 290 | |
291 | } else if cv.value == cv.valueNovar { | | 291 | } else if cv.value == cv.valueNovar { |
292 | cv.line.warnf("Linker flag %q does not start with a dash.", cv.value) | | 292 | cv.line.warnf("Linker flag %q does not start with a dash.", cv.value) |
293 | } | | 293 | } |
294 | } | | 294 | } |
295 | | | 295 | |
296 | func (cv *VartypeCheck) License() { | | 296 | func (cv *VartypeCheck) License() { |
297 | checklineLicense(cv.line, cv.value) | | 297 | checklineLicense(cv.line, cv.value) |
298 | } | | 298 | } |
299 | | | 299 | |
300 | func (cv *VartypeCheck) MailAddress() { | | 300 | func (cv *VartypeCheck) MailAddress() { |
301 | line, value := cv.line, cv.value | | 301 | line, value := cv.line, cv.value |
302 | | | 302 | |
303 | if m, _, domain := match2(value, `^([+\-.0-9A-Z_a-z]+)@([-\w\d.]+)$`); m { | | 303 | if m, _, domain := match2(value, `^([+\-.0-9A-Z_a-z]+)@([-\w\d.]+)$`); m { |
304 | if strings.EqualFold(domain, "NetBSD.org") && domain != "NetBSD.org" { | | 304 | if strings.EqualFold(domain, "NetBSD.org") && domain != "NetBSD.org" { |
305 | line.warnf("Please write \"NetBSD.org\" instead of %q.", domain) | | 305 | line.warnf("Please write \"NetBSD.org\" instead of %q.", domain) |
306 | } | | 306 | } |
307 | if matches(value, `(?i)^(tech-pkg|packages)@NetBSD\.org$`) { | | 307 | if matches(value, `(?i)^(tech-pkg|packages)@NetBSD\.org$`) { |
308 | line.errorf("This mailing list address is obsolete. Use pkgsrc-users@NetBSD.org instead.") | | 308 | line.errorf("This mailing list address is obsolete. Use pkgsrc-users@NetBSD.org instead.") |
309 | } | | 309 | } |
310 | | | 310 | |
311 | } else { | | 311 | } else { |
312 | line.warnf("\"%s\" is not a valid mail address.", value) | | 312 | line.warnf("\"%s\" is not a valid mail address.", value) |
313 | } | | 313 | } |
314 | } | | 314 | } |
315 | | | 315 | |
316 | // See ${STEP_MSG}, ${PKG_FAIL_REASON} | | 316 | // See ${STEP_MSG}, ${PKG_FAIL_REASON} |
317 | func (cv *VartypeCheck) Message() { | | 317 | func (cv *VartypeCheck) Message() { |
318 | line, varname, value := cv.line, cv.varname, cv.value | | 318 | line, varname, value := cv.line, cv.varname, cv.value |
319 | | | 319 | |
320 | if matches(value, `^[\"'].*[\"']$`) { | | 320 | if matches(value, `^[\"'].*[\"']$`) { |
321 | line.warnf("%s should not be quoted.", varname) | | 321 | line.warnf("%s should not be quoted.", varname) |
322 | line.explain( | | 322 | line.explain( |
323 | "The quoting is only needed for variables which are interpreted as", | | 323 | "The quoting is only needed for variables which are interpreted as", |
324 | "multiple words (or, generally speaking, a list of something). A single", | | 324 | "multiple words (or, generally speaking, a list of something). A single", |
325 | "text message does not belong to this class, since it is only printed", | | 325 | "text message does not belong to this class, since it is only printed", |
326 | "as a whole.", | | 326 | "as a whole.", |
327 | "", | | 327 | "", |
328 | "On the other hand, PKG_FAIL_REASON is a _list_ of text messages, so in", | | 328 | "On the other hand, PKG_FAIL_REASON is a _list_ of text messages, so in", |
329 | "that case, the quoting has to be done.`") | | 329 | "that case, the quoting has to be done.`") |
330 | } | | 330 | } |
331 | } | | 331 | } |
332 | | | 332 | |
333 | // A package option from options.mk | | 333 | // A package option from options.mk |
334 | func (cv *VartypeCheck) Option() { | | 334 | func (cv *VartypeCheck) Option() { |
335 | line, value, valueNovar := cv.line, cv.value, cv.valueNovar | | 335 | line, value, valueNovar := cv.line, cv.value, cv.valueNovar |
336 | | | 336 | |
337 | if value != valueNovar { | | 337 | if value != valueNovar { |
338 | _ = G.opts.DebugUnchecked && line.debugf("Unchecked option name: %q", value) | | 338 | _ = G.opts.DebugUnchecked && line.debugf("Unchecked option name: %q", value) |
339 | return | | 339 | return |
340 | } | | 340 | } |
341 | | | 341 | |
342 | if m, optname := match1(value, `^-?([a-z][-0-9a-z\+]*)$`); m { | | 342 | if m, optname := match1(value, `^-?([a-z][-0-9a-z\+]*)$`); m { |
343 | if _, found := G.globalData.pkgOptions[optname]; !found { // There’s a difference between empty and absent here. | | 343 | if _, found := G.globalData.pkgOptions[optname]; !found { // There’s a difference between empty and absent here. |
344 | line.warnf("Unknown option \"%s\".", optname) | | 344 | line.warnf("Unknown option \"%s\".", optname) |
345 | line.explain( | | 345 | line.explain( |
346 | "This option is not documented in the mk/defaults/options.description", | | 346 | "This option is not documented in the mk/defaults/options.description", |
347 | "file. If this is not a typo, please think of a brief but precise", | | 347 | "file. If this is not a typo, please think of a brief but precise", |
348 | "description and either update that file yourself or ask on the", | | 348 | "description and either update that file yourself or ask on the", |
349 | "tech-pkg@NetBSD.org mailing list.") | | 349 | "tech-pkg@NetBSD.org mailing list.") |
350 | } | | 350 | } |
351 | return | | 351 | return |
352 | } | | 352 | } |
353 | | | 353 | |
354 | if matches(value, `^-?([a-z][-0-9a-z_\+]*)$`) { | | 354 | if matches(value, `^-?([a-z][-0-9a-z_\+]*)$`) { |
355 | line.warnf("Use of the underscore character in option names is deprecated.") | | 355 | line.warnf("Use of the underscore character in option names is deprecated.") |
356 | return | | 356 | return |
357 | } | | 357 | } |
358 | | | 358 | |
359 | line.errorf("Invalid option name.") | | 359 | line.errorf("Invalid option name.") |
360 | } | | 360 | } |
361 | | | 361 | |
362 | // The PATH environment variable | | 362 | // The PATH environment variable |
363 | func (cv *VartypeCheck) Pathlist() { | | 363 | func (cv *VartypeCheck) Pathlist() { |
364 | if !contains(cv.value, ":") && cv.guessed == guGuessed { | | 364 | if !contains(cv.value, ":") && cv.guessed == guGuessed { |
365 | NewMkLine(cv.line).checkVartypePrimitive(cv.varname, CheckvarPathname, cv.op, cv.value, cv.comment, cv.listContext, cv.guessed) | | 365 | NewMkLine(cv.line).checkVartypePrimitive(cv.varname, CheckvarPathname, cv.op, cv.value, cv.comment, cv.listContext, cv.guessed) |
366 | return | | 366 | return |
367 | } | | 367 | } |
368 | | | 368 | |
369 | for _, path := range strings.Split(cv.value, ":") { | | 369 | for _, path := range strings.Split(cv.value, ":") { |
370 | if contains(path, "${") { | | 370 | if contains(path, "${") { |
371 | continue | | 371 | continue |
372 | } | | 372 | } |
373 | | | 373 | |
374 | if !matches(path, `^[-0-9A-Za-z._~+%/]*$`) { | | 374 | if !matches(path, `^[-0-9A-Za-z._~+%/]*$`) { |
375 | cv.line.warnf("%q is not a valid pathname.", path) | | 375 | cv.line.warnf("%q is not a valid pathname.", path) |
376 | } | | 376 | } |
377 | | | 377 | |
378 | if !hasPrefix(path, "/") { | | 378 | if !hasPrefix(path, "/") { |
379 | cv.line.warnf("All components of %s (in this case %q) should be absolute paths.", cv.varname, path) | | 379 | cv.line.warnf("All components of %s (in this case %q) should be absolute paths.", cv.varname, path) |
380 | } | | 380 | } |
381 | } | | 381 | } |
382 | } | | 382 | } |
383 | | | 383 | |
384 | // Shell globbing including slashes. | | 384 | // Shell globbing including slashes. |
385 | // See Filemask | | 385 | // See Filemask |
386 | func (cv *VartypeCheck) Pathmask() { | | 386 | func (cv *VartypeCheck) Pathmask() { |
387 | if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%*?/\[\]]*`) { | | 387 | if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%*?/\[\]]*`) { |
388 | cv.line.warnf("%q is not a valid pathname mask.", cv.value) | | 388 | cv.line.warnf("%q is not a valid pathname mask.", cv.value) |
389 | } | | 389 | } |
390 | cv.line.checkAbsolutePathname(cv.value) | | 390 | cv.line.checkAbsolutePathname(cv.value) |
391 | } | | 391 | } |
392 | | | 392 | |
393 | // Like Filename, but including slashes | | 393 | // Like Filename, but including slashes |
394 | // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266 | | 394 | // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266 |
395 | func (cv *VartypeCheck) Pathname() { | | 395 | func (cv *VartypeCheck) Pathname() { |
396 | if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%/]*$`) { | | 396 | if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%/]*$`) { |
397 | cv.line.warnf("%q is not a valid pathname.", cv.value) | | 397 | cv.line.warnf("%q is not a valid pathname.", cv.value) |
398 | } | | 398 | } |
399 | cv.line.checkAbsolutePathname(cv.value) | | 399 | cv.line.checkAbsolutePathname(cv.value) |
400 | } | | 400 | } |
401 | | | 401 | |
402 | func (cv *VartypeCheck) Perl5Packlist() { | | 402 | func (cv *VartypeCheck) Perl5Packlist() { |
403 | if cv.value != cv.valueNovar { | | 403 | if cv.value != cv.valueNovar { |
404 | cv.line.warnf("%s should not depend on other variables.", cv.varname) | | 404 | cv.line.warnf("%s should not depend on other variables.", cv.varname) |
405 | } | | 405 | } |
406 | } | | 406 | } |
407 | | | 407 | |
408 | func (cv *VartypeCheck) PkgName() { | | 408 | func (cv *VartypeCheck) PkgName() { |
409 | if cv.value == cv.valueNovar && !matches(cv.value, rePkgname) { | | 409 | if cv.value == cv.valueNovar && !matches(cv.value, rePkgname) { |
410 | cv.line.warnf("%q is not a valid package name. A valid package name has the form packagename-version, where version consists only of digits, letters and dots.", cv.value) | | 410 | cv.line.warnf("%q is not a valid package name. A valid package name has the form packagename-version, where version consists only of digits, letters and dots.", cv.value) |
411 | } | | 411 | } |
412 | } | | 412 | } |
413 | | | 413 | |
414 | func (cv *VartypeCheck) PkgOptionsVar() { | | 414 | func (cv *VartypeCheck) PkgOptionsVar() { |
415 | NewMkLine(cv.line).checkVartypePrimitive(cv.varname, CheckvarVarname, cv.op, cv.value, cv.comment, false, cv.guessed) | | 415 | NewMkLine(cv.line).checkVartypePrimitive(cv.varname, CheckvarVarname, cv.op, cv.value, cv.comment, false, cv.guessed) |
416 | if matches(cv.value, `\$\{PKGBASE[:\}]`) { | | 416 | if matches(cv.value, `\$\{PKGBASE[:\}]`) { |
417 | cv.line.errorf("PKGBASE must not be used in PKG_OPTIONS_VAR.") | | 417 | cv.line.errorf("PKGBASE must not be used in PKG_OPTIONS_VAR.") |
418 | cv.line.explain( | | 418 | cv.line.explain( |
419 | "PKGBASE is defined in bsd.pkg.mk, which is included as the", | | 419 | "PKGBASE is defined in bsd.pkg.mk, which is included as the", |
420 | "very last file, but PKG_OPTIONS_VAR is evaluated earlier.", | | 420 | "very last file, but PKG_OPTIONS_VAR is evaluated earlier.", |
421 | "Use ${PKGNAME:C/-[0-9].*//} instead.") | | 421 | "Use ${PKGNAME:C/-[0-9].*//} instead.") |
422 | } | | 422 | } |
423 | } | | 423 | } |
424 | | | 424 | |
425 | // A directory name relative to the top-level pkgsrc directory. | | 425 | // A directory name relative to the top-level pkgsrc directory. |
426 | // Despite its name, it is more similar to RelativePkgDir than to RelativePkgPath. | | 426 | // Despite its name, it is more similar to RelativePkgDir than to RelativePkgPath. |
427 | func (cv *VartypeCheck) PkgPath() { | | 427 | func (cv *VartypeCheck) PkgPath() { |
428 | checklineRelativePkgdir(cv.line, G.curPkgsrcdir+"/"+cv.value) | | 428 | checklineRelativePkgdir(cv.line, G.curPkgsrcdir+"/"+cv.value) |
429 | } | | 429 | } |
430 | | | 430 | |
431 | func (cv *VartypeCheck) PkgRevision() { | | 431 | func (cv *VartypeCheck) PkgRevision() { |
432 | if !matches(cv.value, `^[1-9]\d*$`) { | | 432 | if !matches(cv.value, `^[1-9]\d*$`) { |
433 | cv.line.warnf("%s must be a positive integer number.", cv.varname) | | 433 | cv.line.warnf("%s must be a positive integer number.", cv.varname) |
434 | } | | 434 | } |
435 | if path.Base(cv.line.fname) != "Makefile" { | | 435 | if path.Base(cv.line.fname) != "Makefile" { |
436 | cv.line.errorf("%s only makes sense directly in the package Makefile.", cv.varname) | | 436 | cv.line.errorf("%s only makes sense directly in the package Makefile.", cv.varname) |
437 | cv.line.explain( | | 437 | cv.line.explain( |
438 | "Usually, different packages using the same Makefile.common have", | | 438 | "Usually, different packages using the same Makefile.common have", |
439 | "different dependencies and will be bumped at different times (e.g. for", | | 439 | "different dependencies and will be bumped at different times (e.g. for", |
440 | "shlib major bumps) and thus the PKGREVISIONs must be in the separate", | | 440 | "shlib major bumps) and thus the PKGREVISIONs must be in the separate", |
441 | "Makefiles. There is no practical way of having this information in a", | | 441 | "Makefiles. There is no practical way of having this information in a", |
442 | "commonly used Makefile.") | | 442 | "commonly used Makefile.") |
443 | } | | 443 | } |
444 | } | | 444 | } |
445 | | | 445 | |
446 | func (cv *VartypeCheck) PlatformTriple() { | | 446 | func (cv *VartypeCheck) PlatformTriple() { |
447 | if cv.value != cv.valueNovar { | | 447 | if cv.value != cv.valueNovar { |
448 | return | | 448 | return |
449 | } | | 449 | } |
450 | | | 450 | |
451 | rePart := `(?:\[[^\]]+\]|[^-\[])+` | | 451 | rePart := `(?:\[[^\]]+\]|[^-\[])+` |
452 | reTriple := `^(` + rePart + `)-(` + rePart + `)-(` + rePart + `)$` | | 452 | reTriple := `^(` + rePart + `)-(` + rePart + `)-(` + rePart + `)$` |
453 | if m, opsys, _, arch := match3(cv.value, reTriple); m { | | 453 | if m, opsys, _, arch := match3(cv.value, reTriple); m { |
454 | if !matches(opsys, `^(?:\*|BSDOS|Cygwin|Darwin|DragonFly|FreeBSD|Haiku|HPUX|Interix|IRIX|Linux|NetBSD|OpenBSD|OSF1|QNX|SunOS)$`) { | | 454 | if !matches(opsys, `^(?:\*|Bitrig|BSDOS|Cygwin|Darwin|DragonFly|FreeBSD|Haiku|HPUX|Interix|IRIX|Linux|MirBSD|NetBSD|OpenBSD|OSF1|QNX|SunOS)$`) { |
455 | cv.line.warnf("Unknown operating system: %s", opsys) | | 455 | cv.line.warnf("Unknown operating system: %s", opsys) |
456 | } | | 456 | } |
457 | // no check for os_version | | 457 | // no check for os_version |
458 | if !matches(arch, `^(?:\*|i386|alpha|amd64|arc|arm|arm32|cobalt|convex|dreamcast|hpcmips|hpcsh|hppa|ia64|m68k|m88k|mips|mips64|mipsel|mipseb|mipsn32|ns32k|pc532|pmax|powerpc|rs6000|s390|sparc|sparc64|vax|x86_64)$`) { | | 458 | if !matches(arch, `^(?:\*|i386|alpha|amd64|arc|arm|arm32|cobalt|convex|dreamcast|hpcmips|hpcsh|hppa|ia64|m68k|m88k|mips|mips64|mipsel|mipseb|mipsn32|ns32k|pc532|pmax|powerpc|rs6000|s390|sparc|sparc64|vax|x86_64)$`) { |
459 | cv.line.warnf("Unknown hardware architecture: %s", arch) | | 459 | cv.line.warnf("Unknown hardware architecture: %s", arch) |
460 | } | | 460 | } |
461 | | | 461 | |
462 | } else { | | 462 | } else { |
463 | cv.line.warnf("%q is not a valid platform triple.", cv.value) | | 463 | cv.line.warnf("%q is not a valid platform triple.", cv.value) |
464 | cv.line.explain( | | 464 | cv.line.explain( |
465 | "A platform triple has the form <OPSYS>-<OS_VERSION>-<MACHINE_ARCH>.", | | 465 | "A platform triple has the form <OPSYS>-<OS_VERSION>-<MACHINE_ARCH>.", |
466 | "Each of these components may be a shell globbing expression.", | | 466 | "Each of these components may be a shell globbing expression.", |
467 | "Examples: NetBSD-*-i386, *-*-*, Linux-*-*.") | | 467 | "Examples: NetBSD-*-i386, *-*-*, Linux-*-*.") |
468 | } | | 468 | } |
469 | } | | 469 | } |
470 | | | 470 | |
471 | func (cv *VartypeCheck) PrefixPathname() { | | 471 | func (cv *VartypeCheck) PrefixPathname() { |
472 | if m, mansubdir := match1(cv.value, `^man/(.+)`); m { | | 472 | if m, mansubdir := match1(cv.value, `^man/(.+)`); m { |
473 | cv.line.warnf("Please use \"${PKGMANDIR}/%s\" instead of %q.", mansubdir, cv.value) | | 473 | cv.line.warnf("Please use \"${PKGMANDIR}/%s\" instead of %q.", mansubdir, cv.value) |
474 | } | | 474 | } |
475 | } | | 475 | } |
476 | | | 476 | |
477 | func (cv *VartypeCheck) PythonDependency() { | | 477 | func (cv *VartypeCheck) PythonDependency() { |
478 | if cv.value != cv.valueNovar { | | 478 | if cv.value != cv.valueNovar { |
479 | cv.line.warnf("Python dependencies should not contain variables.") | | 479 | cv.line.warnf("Python dependencies should not contain variables.") |
480 | } | | 480 | } |
481 | if !matches(cv.valueNovar, `^[+\-.0-9A-Z_a-z]+(?:|:link|:build)$`) { | | 481 | if !matches(cv.valueNovar, `^[+\-.0-9A-Z_a-z]+(?:|:link|:build)$`) { |
482 | cv.line.warnf("Invalid Python dependency %q.", cv.value) | | 482 | cv.line.warnf("Invalid Python dependency %q.", cv.value) |
483 | cv.line.explain( | | 483 | cv.line.explain( |
484 | "Python dependencies must be an identifier for a package, as specified", | | 484 | "Python dependencies must be an identifier for a package, as specified", |
485 | "in lang/python/versioned_dependencies.mk. This identifier may be", | | 485 | "in lang/python/versioned_dependencies.mk. This identifier may be", |
486 | "followed by :build for a build-time only dependency, or by :link for", | | 486 | "followed by :build for a build-time only dependency, or by :link for", |
487 | "a run-time only dependency.") | | 487 | "a run-time only dependency.") |
488 | } | | 488 | } |
489 | } | | 489 | } |
490 | | | 490 | |
491 | // Refers to a package directory. | | 491 | // Refers to a package directory. |
492 | func (cv *VartypeCheck) RelativePkgDir() { | | 492 | func (cv *VartypeCheck) RelativePkgDir() { |
493 | checklineRelativePkgdir(cv.line, cv.value) | | 493 | checklineRelativePkgdir(cv.line, cv.value) |
494 | } | | 494 | } |
495 | | | 495 | |
496 | // Refers to a file or directory. | | 496 | // Refers to a file or directory. |
497 | func (cv *VartypeCheck) RelativePkgPath() { | | 497 | func (cv *VartypeCheck) RelativePkgPath() { |
498 | checklineRelativePath(cv.line, cv.value, true) | | 498 | checklineRelativePath(cv.line, cv.value, true) |
499 | } | | 499 | } |
500 | | | 500 | |
501 | func (cv *VartypeCheck) Restricted() { | | 501 | func (cv *VartypeCheck) Restricted() { |
502 | if cv.value != "${RESTRICTED}" { | | 502 | if cv.value != "${RESTRICTED}" { |
503 | cv.line.warnf("The only valid value for %s is ${RESTRICTED}.", cv.varname) | | 503 | cv.line.warnf("The only valid value for %s is ${RESTRICTED}.", cv.varname) |
504 | cv.line.explain( | | 504 | cv.line.explain( |
505 | "These variables are used to control which files may be mirrored on FTP", | | 505 | "These variables are used to control which files may be mirrored on FTP", |
506 | "servers or CD-ROM collections. They are not intended to mark packages", | | 506 | "servers or CD-ROM collections. They are not intended to mark packages", |
507 | "whose only MASTER_SITES are on ftp.NetBSD.org.") | | 507 | "whose only MASTER_SITES are on ftp.NetBSD.org.") |
508 | } | | 508 | } |
509 | } | | 509 | } |
510 | | | 510 | |
511 | func (cv *VartypeCheck) SedCommand() { | | 511 | func (cv *VartypeCheck) SedCommand() { |
512 | } | | 512 | } |
513 | | | 513 | |
514 | func (cv *VartypeCheck) SedCommands() { | | 514 | func (cv *VartypeCheck) SedCommands() { |
515 | line := cv.line | | 515 | line := cv.line |
516 | | | 516 | |
517 | words, rest := splitIntoShellwords(line, cv.value) | | 517 | words, rest := splitIntoShellwords(line, cv.value) |
518 | if rest != "" { | | 518 | if rest != "" { |
519 | if contains(cv.value, "#") { | | 519 | if contains(cv.value, "#") { |
520 | line.errorf("Invalid shell words in sed commands.") | | 520 | line.errorf("Invalid shell words in sed commands.") |
521 | line.explain( | | 521 | line.explain( |
522 | "When sed commands have embedded \"#\" characters, they need to be", | | 522 | "When sed commands have embedded \"#\" characters, they need to be", |
523 | "escaped with a backslash, otherwise make(1) will interpret them as a", | | 523 | "escaped with a backslash, otherwise make(1) will interpret them as a", |
524 | "comment, no matter if they occur in single or double quotes or", | | 524 | "comment, no matter if they occur in single or double quotes or", |
525 | "whatever.") | | 525 | "whatever.") |
526 | } | | 526 | } |
527 | return | | 527 | return |
528 | } | | 528 | } |
529 | | | 529 | |
530 | nwords := len(words) | | 530 | nwords := len(words) |
531 | ncommands := 0 | | 531 | ncommands := 0 |
532 | | | 532 | |
533 | for i := 0; i < nwords; i++ { | | 533 | for i := 0; i < nwords; i++ { |
534 | word := words[i] | | 534 | word := words[i] |
535 | NewMkShellLine(cv.line).checkShellword(word, true) | | 535 | NewMkShellLine(cv.line).checkShellword(word, true) |
536 | | | 536 | |
537 | switch { | | 537 | switch { |
538 | case word == "-e": | | 538 | case word == "-e": |
539 | if i+1 < nwords { | | 539 | if i+1 < nwords { |
540 | // Check the real sed command here. | | 540 | // Check the real sed command here. |
541 | i++ | | 541 | i++ |
542 | ncommands++ | | 542 | ncommands++ |
543 | if ncommands > 1 { | | 543 | if ncommands > 1 { |
544 | line.notef("Each sed command should appear in an assignment of its own.") | | 544 | line.notef("Each sed command should appear in an assignment of its own.") |
545 | line.explain( | | 545 | line.explain( |
546 | "For example, instead of", | | 546 | "For example, instead of", |
547 | " SUBST_SED.foo+= -e s,command1,, -e s,command2,,", | | 547 | " SUBST_SED.foo+= -e s,command1,, -e s,command2,,", |
548 | "use", | | 548 | "use", |
549 | " SUBST_SED.foo+= -e s,command1,,", | | 549 | " SUBST_SED.foo+= -e s,command1,,", |
550 | " SUBST_SED.foo+= -e s,command2,,", | | 550 | " SUBST_SED.foo+= -e s,command2,,", |
551 | "", | | 551 | "", |
552 | "This way, short sed commands cannot be hidden at the end of a line.") | | 552 | "This way, short sed commands cannot be hidden at the end of a line.") |
553 | } | | 553 | } |
554 | NewMkShellLine(line).checkShellword(words[i-1], true) | | 554 | NewMkShellLine(line).checkShellword(words[i-1], true) |
555 | NewMkShellLine(line).checkShellword(words[i], true) | | 555 | NewMkShellLine(line).checkShellword(words[i], true) |
556 | NewMkLine(line).checkVartypePrimitive(cv.varname, CheckvarSedCommand, cv.op, words[i], cv.comment, cv.listContext, cv.guessed) | | 556 | NewMkLine(line).checkVartypePrimitive(cv.varname, CheckvarSedCommand, cv.op, words[i], cv.comment, cv.listContext, cv.guessed) |
557 | } else { | | 557 | } else { |
558 | line.errorf("The -e option to sed requires an argument.") | | 558 | line.errorf("The -e option to sed requires an argument.") |
559 | } | | 559 | } |
560 | case word == "-E": | | 560 | case word == "-E": |
561 | // Switch to extended regular expressions mode. | | 561 | // Switch to extended regular expressions mode. |
562 | | | 562 | |
563 | case word == "-n": | | 563 | case word == "-n": |
564 | // Don't print lines per default. | | 564 | // Don't print lines per default. |
565 | | | 565 | |
566 | case i == 0 && matches(word, `^(["']?)(?:\d*|/.*/)s.+["']?$`): | | 566 | case i == 0 && matches(word, `^(["']?)(?:\d*|/.*/)s.+["']?$`): |
567 | line.notef("Please always use \"-e\" in sed commands, even if there is only one substitution.") | | 567 | line.notef("Please always use \"-e\" in sed commands, even if there is only one substitution.") |
568 | | | 568 | |
569 | default: | | 569 | default: |
570 | line.warnf("Unknown sed command %q.", word) | | 570 | line.warnf("Unknown sed command %q.", word) |
571 | } | | 571 | } |
572 | } | | 572 | } |
573 | } | | 573 | } |
574 | | | 574 | |
575 | func (cv *VartypeCheck) ShellCommand() { | | 575 | func (cv *VartypeCheck) ShellCommand() { |
576 | NewMkShellLine(cv.line).checkShelltext(cv.value) | | 576 | NewMkShellLine(cv.line).checkShelltext(cv.value) |
577 | } | | 577 | } |
578 | | | 578 | |
579 | func (cv *VartypeCheck) ShellWord() { | | 579 | func (cv *VartypeCheck) ShellWord() { |
580 | if !cv.listContext { | | 580 | if !cv.listContext { |
581 | NewMkShellLine(cv.line).checkShellword(cv.value, true) | | 581 | NewMkShellLine(cv.line).checkShellword(cv.value, true) |
582 | } | | 582 | } |
583 | } | | 583 | } |
584 | | | 584 | |
585 | func (cv *VartypeCheck) Stage() { | | 585 | func (cv *VartypeCheck) Stage() { |
586 | if !matches(cv.value, `^(?:pre|do|post)-(?:extract|patch|configure|build|test|install)`) { | | 586 | if !matches(cv.value, `^(?:pre|do|post)-(?:extract|patch|configure|build|test|install)`) { |
587 | cv.line.warnf("Invalid stage name %q. Use one of {pre,do,post}-{extract,patch,configure,build,test,install}.", cv.value) | | 587 | cv.line.warnf("Invalid stage name %q. Use one of {pre,do,post}-{extract,patch,configure,build,test,install}.", cv.value) |
588 | } | | 588 | } |
589 | } | | 589 | } |
590 | | | 590 | |
591 | func (cv *VartypeCheck) String() { | | 591 | func (cv *VartypeCheck) String() { |
592 | // No further checks possible. | | 592 | // No further checks possible. |
593 | } | | 593 | } |
594 | | | 594 | |
595 | func (cv *VartypeCheck) Tool() { | | 595 | func (cv *VartypeCheck) Tool() { |
596 | if cv.varname == "TOOLS_NOOP" && cv.op == "+=" { | | 596 | if cv.varname == "TOOLS_NOOP" && cv.op == "+=" { |
597 | // no warning for package-defined tool definitions | | 597 | // no warning for package-defined tool definitions |
598 | | | 598 | |
599 | } else if m, toolname, tooldep := match2(cv.value, `^([-\w]+|\[)(?::(\w+))?$`); m { | | 599 | } else if m, toolname, tooldep := match2(cv.value, `^([-\w]+|\[)(?::(\w+))?$`); m { |
600 | if !G.globalData.tools[toolname] { | | 600 | if !G.globalData.tools[toolname] { |
601 | cv.line.errorf("Unknown tool %q.", toolname) | | 601 | cv.line.errorf("Unknown tool %q.", toolname) |
602 | } | | 602 | } |
603 | switch tooldep { | | 603 | switch tooldep { |
604 | case "", "bootstrap", "build", "pkgsrc", "run": | | 604 | case "", "bootstrap", "build", "pkgsrc", "run": |
605 | default: | | 605 | default: |
606 | cv.line.errorf("Unknown tool dependency %q. Use one of \"build\", \"pkgsrc\" or \"run\".", tooldep) | | 606 | cv.line.errorf("Unknown tool dependency %q. Use one of \"build\", \"pkgsrc\" or \"run\".", tooldep) |
607 | } | | 607 | } |
608 | } else { | | 608 | } else { |
609 | cv.line.errorf("Invalid tool syntax: %q.", cv.value) | | 609 | cv.line.errorf("Invalid tool syntax: %q.", cv.value) |
610 | } | | 610 | } |
611 | } | | 611 | } |
612 | | | 612 | |
613 | func (cv *VartypeCheck) Unchecked() { | | 613 | func (cv *VartypeCheck) Unchecked() { |
614 | // Do nothing, as the name says. | | 614 | // Do nothing, as the name says. |
615 | } | | 615 | } |
616 | | | 616 | |
617 | func (cv *VartypeCheck) URL() { | | 617 | func (cv *VartypeCheck) URL() { |
618 | line, value := cv.line, cv.value | | 618 | line, value := cv.line, cv.value |
619 | | | 619 | |
620 | if value == "" && hasPrefix(cv.comment, "#") { | | 620 | if value == "" && hasPrefix(cv.comment, "#") { |
621 | // Ok | | 621 | // Ok |
622 | | | 622 | |
623 | } else if m, name, subdir := match2(value, `\$\{(MASTER_SITE_[^:]*).*:=(.*)\}$`); m { | | 623 | } else if m, name, subdir := match2(value, `\$\{(MASTER_SITE_[^:]*).*:=(.*)\}$`); m { |
624 | if !G.globalData.masterSiteVars[name] { | | 624 | if !G.globalData.masterSiteVars[name] { |
625 | line.errorf("%s does not exist.", name) | | 625 | line.errorf("%s does not exist.", name) |
626 | } | | 626 | } |
627 | if !hasSuffix(subdir, "/") { | | 627 | if !hasSuffix(subdir, "/") { |
628 | line.errorf("The subdirectory in %s must end with a slash.", name) | | 628 | line.errorf("The subdirectory in %s must end with a slash.", name) |
629 | } | | 629 | } |
630 | | | 630 | |
631 | } else if containsVarRef(value) { | | 631 | } else if containsVarRef(value) { |
632 | // No further checks | | 632 | // No further checks |
633 | | | 633 | |
634 | } else if m, _, host, _, _ := match4(value, `^(https?|ftp|gopher)://([-0-9A-Za-z.]+)(?::(\d+))?/([-%&+,./0-9:=?@A-Z_a-z~]|#)*$`); m { | | 634 | } else if m, _, host, _, _ := match4(value, `^(https?|ftp|gopher)://([-0-9A-Za-z.]+)(?::(\d+))?/([-%&+,./0-9:=?@A-Z_a-z~]|#)*$`); m { |
635 | if matches(host, `(?i)\.NetBSD\.org$`) && !matches(host, `\.NetBSD\.org$`) { | | 635 | if matches(host, `(?i)\.NetBSD\.org$`) && !matches(host, `\.NetBSD\.org$`) { |
636 | line.warnf("Please write NetBSD.org instead of %s.", host) | | 636 | line.warnf("Please write NetBSD.org instead of %s.", host) |
637 | } | | 637 | } |
638 | | | 638 | |
639 | } else if m, scheme, _, absPath := match3(value, `^([0-9A-Za-z]+)://([^/]+)(.*)$`); m { | | 639 | } else if m, scheme, _, absPath := match3(value, `^([0-9A-Za-z]+)://([^/]+)(.*)$`); m { |
640 | switch { | | 640 | switch { |
641 | case scheme != "ftp" && scheme != "http" && scheme != "https" && scheme != "gopher": | | 641 | case scheme != "ftp" && scheme != "http" && scheme != "https" && scheme != "gopher": |
642 | line.warnf("%q is not a valid URL. Only ftp, gopher, http, and https URLs are allowed here.", value) | | 642 | line.warnf("%q is not a valid URL. Only ftp, gopher, http, and https URLs are allowed here.", value) |
643 | | | 643 | |
644 | case absPath == "": | | 644 | case absPath == "": |
645 | line.notef("For consistency, please add a trailing slash to %q.", value) | | 645 | line.notef("For consistency, please add a trailing slash to %q.", value) |
646 | | | 646 | |
647 | default: | | 647 | default: |
648 | line.warnf("%q is not a valid URL.", value) | | 648 | line.warnf("%q is not a valid URL.", value) |
649 | } | | 649 | } |
650 | | | 650 | |
651 | } else { | | 651 | } else { |
652 | line.warnf("%q is not a valid URL.", value) | | 652 | line.warnf("%q is not a valid URL.", value) |
653 | } | | 653 | } |
654 | } | | 654 | } |
655 | | | 655 | |
656 | func (cv *VartypeCheck) UserGroupName() { | | 656 | func (cv *VartypeCheck) UserGroupName() { |
657 | if cv.value == cv.valueNovar && !matches(cv.value, `^[0-9_a-z]+$`) { | | 657 | if cv.value == cv.valueNovar && !matches(cv.value, `^[0-9_a-z]+$`) { |
658 | cv.line.warnf("Invalid user or group name %q.", cv.value) | | 658 | cv.line.warnf("Invalid user or group name %q.", cv.value) |
659 | } | | 659 | } |
660 | } | | 660 | } |
661 | | | 661 | |
662 | func (cv *VartypeCheck) Varname() { | | 662 | func (cv *VartypeCheck) Varname() { |
663 | if cv.value == cv.valueNovar && !matches(cv.value, `^[A-Z_][0-9A-Z_]*(?:[.].*)?$`) { | | 663 | if cv.value == cv.valueNovar && !matches(cv.value, `^[A-Z_][0-9A-Z_]*(?:[.].*)?$`) { |
664 | cv.line.warnf("%q is not a valid variable name.", cv.value) | | 664 | cv.line.warnf("%q is not a valid variable name.", cv.value) |
665 | cv.line.explain( | | 665 | cv.line.explain( |
666 | "Variable names are restricted to only uppercase letters and the", | | 666 | "Variable names are restricted to only uppercase letters and the", |
667 | "underscore in the basename, and arbitrary characters in the", | | 667 | "underscore in the basename, and arbitrary characters in the", |
668 | "parameterized part, following the dot.", | | 668 | "parameterized part, following the dot.", |
669 | "", | | 669 | "", |
670 | "Examples:", | | 670 | "Examples:", |
671 | "\t* PKGNAME", | | 671 | "\t* PKGNAME", |
672 | "\t* PKG_OPTIONS.gnuchess") | | 672 | "\t* PKG_OPTIONS.gnuchess") |
673 | } | | 673 | } |
674 | } | | 674 | } |
675 | | | 675 | |
676 | func (cv *VartypeCheck) Version() { | | 676 | func (cv *VartypeCheck) Version() { |
677 | if !matches(cv.value, `^([\d.])+$`) { | | 677 | if !matches(cv.value, `^([\d.])+$`) { |
678 | cv.line.warnf("Invalid version number %q.", cv.value) | | 678 | cv.line.warnf("Invalid version number %q.", cv.value) |
679 | } | | 679 | } |
680 | } | | 680 | } |
681 | | | 681 | |
682 | func (cv *VartypeCheck) WrapperReorder() { | | 682 | func (cv *VartypeCheck) WrapperReorder() { |
683 | if !matches(cv.value, `^reorder:l:([\w\-]+):([\w\-]+)$`) { | | 683 | if !matches(cv.value, `^reorder:l:([\w\-]+):([\w\-]+)$`) { |
684 | cv.line.warnf("Unknown wrapper reorder command %q.", cv.value) | | 684 | cv.line.warnf("Unknown wrapper reorder command %q.", cv.value) |
685 | } | | 685 | } |
686 | } | | 686 | } |
687 | | | 687 | |
688 | func (cv *VartypeCheck) WrapperTransform() { | | 688 | func (cv *VartypeCheck) WrapperTransform() { |
689 | switch { | | 689 | switch { |
690 | case matches(cv.value, `^rm:(?:-[DILOUWflm].*|-std=.*)$`): | | 690 | case matches(cv.value, `^rm:(?:-[DILOUWflm].*|-std=.*)$`): |
691 | case matches(cv.value, `^l:([^:]+):(.+)$`): | | 691 | case matches(cv.value, `^l:([^:]+):(.+)$`): |
692 | case matches(cv.value, `^'?(?:opt|rename|rm-optarg|rmdir):.*$`): | | 692 | case matches(cv.value, `^'?(?:opt|rename|rm-optarg|rmdir):.*$`): |
693 | case cv.value == "-e": | | 693 | case cv.value == "-e": |
694 | case matches(cv.value, `^\"?'?s[|:,]`): | | 694 | case matches(cv.value, `^\"?'?s[|:,]`): |
695 | default: | | 695 | default: |
696 | cv.line.warnf("Unknown wrapper transform command %q.", cv.value) | | 696 | cv.line.warnf("Unknown wrapper transform command %q.", cv.value) |
697 | } | | 697 | } |
698 | } | | 698 | } |
699 | | | 699 | |
700 | func (cv *VartypeCheck) WrkdirSubdirectory() { | | 700 | func (cv *VartypeCheck) WrkdirSubdirectory() { |
701 | NewMkLine(cv.line).checkVartypePrimitive(cv.varname, CheckvarPathname, cv.op, cv.value, cv.comment, cv.listContext, cv.guessed) | | 701 | NewMkLine(cv.line).checkVartypePrimitive(cv.varname, CheckvarPathname, cv.op, cv.value, cv.comment, cv.listContext, cv.guessed) |
702 | } | | 702 | } |
703 | | | 703 | |
704 | // A directory relative to ${WRKSRC}, for use in CONFIGURE_DIRS and similar variables. | | 704 | // A directory relative to ${WRKSRC}, for use in CONFIGURE_DIRS and similar variables. |
705 | func (cv *VartypeCheck) WrksrcSubdirectory() { | | 705 | func (cv *VartypeCheck) WrksrcSubdirectory() { |
706 | if m, _, rest := match2(cv.value, `^(\$\{WRKSRC\})(?:/(.*))?`); m { | | 706 | if m, _, rest := match2(cv.value, `^(\$\{WRKSRC\})(?:/(.*))?`); m { |
707 | if rest == "" { | | 707 | if rest == "" { |
708 | rest = "." | | 708 | rest = "." |
709 | } | | 709 | } |
710 | cv.line.notef("You can use %q instead of %q.", rest, cv.value) | | 710 | cv.line.notef("You can use %q instead of %q.", rest, cv.value) |
711 | | | 711 | |
712 | } else if cv.value != "" && cv.valueNovar == "" { | | 712 | } else if cv.value != "" && cv.valueNovar == "" { |
713 | // The value of another variable | | 713 | // The value of another variable |
714 | | | 714 | |
715 | } else if !matches(cv.valueNovar, `^(?:\.|[0-9A-Za-z_@][-0-9A-Za-z_@./+]*)$`) { | | 715 | } else if !matches(cv.valueNovar, `^(?:\.|[0-9A-Za-z_@][-0-9A-Za-z_@./+]*)$`) { |
716 | cv.line.warnf("%q is not a valid subdirectory of ${WRKSRC}.", cv.value) | | 716 | cv.line.warnf("%q is not a valid subdirectory of ${WRKSRC}.", cv.value) |
717 | } | | 717 | } |
718 | } | | 718 | } |
719 | | | 719 | |
720 | // Used for variables that are checked using `.if defined(VAR)`. | | 720 | // Used for variables that are checked using `.if defined(VAR)`. |
721 | func (cv *VartypeCheck) Yes() { | | 721 | func (cv *VartypeCheck) Yes() { |
722 | if !matches(cv.value, `^(?:YES|yes)(?:\s+#.*)?$`) { | | 722 | if !matches(cv.value, `^(?:YES|yes)(?:\s+#.*)?$`) { |
723 | cv.line.warnf("%s should be set to YES or yes.", cv.varname) | | 723 | cv.line.warnf("%s should be set to YES or yes.", cv.varname) |
724 | cv.line.explain( | | 724 | cv.line.explain( |
725 | "This variable means \"yes\" if it is defined, and \"no\" if it is", | | 725 | "This variable means \"yes\" if it is defined, and \"no\" if it is", |
726 | "undefined. Even when it has the value \"no\", this means \"yes\".", | | 726 | "undefined. Even when it has the value \"no\", this means \"yes\".", |
727 | "Therefore when it is defined, its value should correspond to its", | | 727 | "Therefore when it is defined, its value should correspond to its", |
728 | "meaning.") | | 728 | "meaning.") |
729 | } | | 729 | } |
730 | } | | 730 | } |
731 | | | 731 | |
732 | // The type YesNo is used for variables that are checked using | | 732 | // The type YesNo is used for variables that are checked using |
733 | // .if defined(VAR) && !empty(VAR:M[Yy][Ee][Ss]) | | 733 | // .if defined(VAR) && !empty(VAR:M[Yy][Ee][Ss]) |
734 | // | | 734 | // |
735 | func (cv *VartypeCheck) YesNo() { | | 735 | func (cv *VartypeCheck) YesNo() { |
736 | if !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) { | | 736 | if !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) { |
737 | cv.line.warnf("%s should be set to YES, yes, NO, or no.", cv.varname) | | 737 | cv.line.warnf("%s should be set to YES, yes, NO, or no.", cv.varname) |
738 | } | | 738 | } |
739 | } | | 739 | } |
740 | | | 740 | |
741 | // Like YesNo, but the value may be produced by a shell command using the | | 741 | // Like YesNo, but the value may be produced by a shell command using the |
742 | // != operator. | | 742 | // != operator. |
743 | func (cv *VartypeCheck) YesNoIndirectly() { | | 743 | func (cv *VartypeCheck) YesNoIndirectly() { |
744 | if cv.valueNovar != "" && !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) { | | 744 | if cv.valueNovar != "" && !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) { |
745 | cv.line.warnf("%s should be set to YES, yes, NO, or no.", cv.varname) | | 745 | cv.line.warnf("%s should be set to YES, yes, NO, or no.", cv.varname) |
746 | } | | 746 | } |
747 | } | | 747 | } |