pkgtools/pkglint: update to 5.6.8 Changes since 5.6.7: In pkgsrc-wip, if the first line of a file contains an expanded CVS Id, it is not an error but only a note that it should be an unexpanded CVS Id. The autofix for this no longer inserts a new line but replaces the existing line. Several refactorings and small improvements to the existing diagnostics.diff -r1.559 -r1.560 pkgsrc/pkgtools/pkglint/Makefile
(rillig)
@@ -1,16 +1,16 @@ | @@ -1,16 +1,16 @@ | |||
1 | # $NetBSD: Makefile,v 1.559 2018/12/02 01:57:48 rillig Exp $ | 1 | # $NetBSD: Makefile,v 1.560 2018/12/02 23:12:43 rillig Exp $ | |
2 | 2 | |||
3 | PKGNAME= pkglint-5.6.7 | 3 | PKGNAME= pkglint-5.6.8 | |
4 | CATEGORIES= pkgtools | 4 | CATEGORIES= pkgtools | |
5 | DISTNAME= tools | 5 | DISTNAME= tools | |
6 | MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/} | 6 | MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/} | |
7 | GITHUB_PROJECT= tools | 7 | GITHUB_PROJECT= tools | |
8 | GITHUB_TAG= 92d8274bd7b8a4c65f24bafe401a029e58392704 | 8 | GITHUB_TAG= 92d8274bd7b8a4c65f24bafe401a029e58392704 | |
9 | 9 | |||
10 | MAINTAINER= rillig@NetBSD.org | 10 | MAINTAINER= rillig@NetBSD.org | |
11 | HOMEPAGE= https://github.com/rillig/pkglint | 11 | HOMEPAGE= https://github.com/rillig/pkglint | |
12 | COMMENT= Verifier for NetBSD packages | 12 | COMMENT= Verifier for NetBSD packages | |
13 | LICENSE= 2-clause-bsd | 13 | LICENSE= 2-clause-bsd | |
14 | CONFLICTS+= pkglint4-[0-9]* | 14 | CONFLICTS+= pkglint4-[0-9]* | |
15 | 15 | |||
16 | USE_TOOLS+= pax | 16 | USE_TOOLS+= pax |
@@ -293,40 +293,43 @@ func (fix *Autofix) Apply() { | @@ -293,40 +293,43 @@ func (fix *Autofix) Apply() { | |||
293 | G.Explain(fix.explanation...) | 293 | G.Explain(fix.explanation...) | |
294 | } | 294 | } | |
295 | if G.Logger.Opts.ShowSource { | 295 | if G.Logger.Opts.ShowSource { | |
296 | if !G.Logger.Opts.Explain || !logDiagnostic || len(fix.explanation) == 0 { | 296 | if !G.Logger.Opts.Explain || !logDiagnostic || len(fix.explanation) == 0 { | |
297 | G.out.Separate() | 297 | G.out.Separate() | |
298 | } | 298 | } | |
299 | } | 299 | } | |
300 | } | 300 | } | |
301 | 301 | |||
302 | reset() | 302 | reset() | |
303 | } | 303 | } | |
304 | 304 | |||
305 | func (fix *Autofix) Realign(mkline MkLine, newWidth int) { | 305 | func (fix *Autofix) Realign(mkline MkLine, newWidth int) { | |
306 | ||||
307 | // XXX: Check whether this method can be implemented as Custom fix. | |||
308 | // This complicated code should not be in the Autofix type. | |||
309 | ||||
306 | fix.assertRealLine() | 310 | fix.assertRealLine() | |
307 | G.Assertf(mkline.IsMultiline(), "Line must be a multiline.") | 311 | G.Assertf(mkline.IsMultiline(), "Line must be a multiline.") | |
308 | G.Assertf(mkline.IsVarassign() || mkline.IsCommentedVarassign(), "Line must be a variable assignment.") | 312 | G.Assertf(mkline.IsVarassign() || mkline.IsCommentedVarassign(), "Line must be a variable assignment.") | |
309 | 313 | |||
310 | if fix.skip() { | 314 | if fix.skip() { | |
311 | return | 315 | return | |
312 | } | 316 | } | |
313 | 317 | |||
314 | normalized := true // Whether all indentation is tabs, followed by spaces. | 318 | normalized := true // Whether all indentation is tabs, followed by spaces. | |
315 | oldWidth := 0 // The minimum required indentation in the original lines. | 319 | oldWidth := 0 // The minimum required indentation in the original lines. | |
316 | 320 | |||
317 | { | 321 | { | |
318 | // Parsing the continuation marker as variable value | 322 | // Parsing the continuation marker as variable value is cheating but works well. | |
319 | // is cheating but works well. | |||
320 | text := strings.TrimSuffix(mkline.raw[0].orignl, "\n") | 323 | text := strings.TrimSuffix(mkline.raw[0].orignl, "\n") | |
321 | m, _, _, _, _, valueAlign, value, _, _ := MatchVarassign(text) | 324 | m, _, _, _, _, valueAlign, value, _, _ := MatchVarassign(text) | |
322 | if m && value != "\\" { | 325 | if m && value != "\\" { | |
323 | oldWidth = tabWidth(valueAlign) | 326 | oldWidth = tabWidth(valueAlign) | |
324 | } | 327 | } | |
325 | } | 328 | } | |
326 | 329 | |||
327 | for _, rawLine := range fix.line.raw[1:] { | 330 | for _, rawLine := range fix.line.raw[1:] { | |
328 | _, comment, space := match2(rawLine.textnl, `^(#?)([ \t]*)`) | 331 | _, comment, space := match2(rawLine.textnl, `^(#?)([ \t]*)`) | |
329 | width := tabWidth(comment + space) | 332 | width := tabWidth(comment + space) | |
330 | if (oldWidth == 0 || width < oldWidth) && width >= 8 && rawLine.textnl != "\n" { | 333 | if (oldWidth == 0 || width < oldWidth) && width >= 8 && rawLine.textnl != "\n" { | |
331 | oldWidth = width | 334 | oldWidth = width | |
332 | } | 335 | } |
@@ -313,28 +313,28 @@ func (src *Pkgsrc) loadTools() { | @@ -313,28 +313,28 @@ func (src *Pkgsrc) loadTools() { | |||
313 | } | 313 | } | |
314 | 314 | |||
315 | for _, relativeName := range [...]string{"mk/bsd.prefs.mk", "mk/bsd.pkg.mk"} { | 315 | for _, relativeName := range [...]string{"mk/bsd.prefs.mk", "mk/bsd.pkg.mk"} { | |
316 | 316 | |||
317 | mklines := G.Pkgsrc.LoadMk(relativeName, MustSucceed|NotEmpty) | 317 | mklines := G.Pkgsrc.LoadMk(relativeName, MustSucceed|NotEmpty) | |
318 | mklines.ForEach(func(mkline MkLine) { | 318 | mklines.ForEach(func(mkline MkLine) { | |
319 | if mkline.IsVarassign() { | 319 | if mkline.IsVarassign() { | |
320 | varname := mkline.Varname() | 320 | varname := mkline.Varname() | |
321 | switch varname { | 321 | switch varname { | |
322 | case "USE_TOOLS": | 322 | case "USE_TOOLS": | |
323 | tools.ParseToolLine(mkline, true, !mklines.indentation.IsConditional()) | 323 | tools.ParseToolLine(mkline, true, !mklines.indentation.IsConditional()) | |
324 | 324 | |||
325 | case "_BUILD_DEFS": | 325 | case "_BUILD_DEFS": | |
326 | for _, bdvar := range mkline.ValueFields(mkline.Value()) { | 326 | for _, buildDefsVar := range mkline.Fields() { | |
327 | src.AddBuildDefs(bdvar) | 327 | src.AddBuildDefs(buildDefsVar) | |
328 | } | 328 | } | |
329 | } | 329 | } | |
330 | } | 330 | } | |
331 | }) | 331 | }) | |
332 | } | 332 | } | |
333 | 333 | |||
334 | if trace.Tracing { | 334 | if trace.Tracing { | |
335 | tools.Trace() | 335 | tools.Trace() | |
336 | } | 336 | } | |
337 | } | 337 | } | |
338 | 338 | |||
339 | // loadUntypedVars scans all pkgsrc infrastructure files in mk/ | 339 | // loadUntypedVars scans all pkgsrc infrastructure files in mk/ | |
340 | // to find variable definitions that are not yet covered in | 340 | // to find variable definitions that are not yet covered in |
@@ -1,46 +1,46 @@ | @@ -1,46 +1,46 @@ | |||
1 | package main | 1 | package main | |
2 | 2 | |||
3 | import "gopkg.in/check.v1" | 3 | import "gopkg.in/check.v1" | |
4 | 4 | |||
5 | func (s *Suite) Test_ChecklinesBuildlink3Mk__unfinished_url2pkg(c *check.C) { | 5 | func (s *Suite) Test_ChecklinesBuildlink3Mk__unfinished_url2pkg(c *check.C) { | |
6 | t := s.Init(c) | 6 | t := s.Init(c) | |
7 | 7 | |||
8 | t.SetupVartypes() | 8 | t.SetupVartypes() | |
9 | t.CreateFileLines("x11/Xbae/Makefile") | 9 | t.CreateFileLines("x11/Xbae/Makefile") | |
10 | t.CreateFileLines("mk/motif.buildlink3.mk") | 10 | t.CreateFileLines("mk/motif.buildlink3.mk") | |
11 | mklines := t.SetupFileMkLines("buildlink3.mk", | 11 | mklines := t.SetupFileMkLines("category/package/buildlink3.mk", | |
12 | MkRcsID, | 12 | MkRcsID, | |
13 | "# XXX This file was created automatically using createbuildlink-@PKGVERSION@", | 13 | "# XXX This file was created automatically using createbuildlink-@PKGVERSION@", | |
14 | "", | 14 | "", | |
15 | "BUILDLINK_TREE+=\tXbae", | 15 | "BUILDLINK_TREE+=\tXbae", | |
16 | "", | 16 | "", | |
17 | "BUILDLINK_DEPMETHOD.Xbae?=\tfull", | 17 | "BUILDLINK_DEPMETHOD.Xbae?=\tfull", | |
18 | ".if !defined(XBAE_BUILDLINK3_MK)", | 18 | ".if !defined(XBAE_BUILDLINK3_MK)", | |
19 | "XBAE_BUILDLINK3_MK:=", | 19 | "XBAE_BUILDLINK3_MK:=", | |
20 | "", | 20 | "", | |
21 | "BUILDLINK_API_DEPENDS.Xbae+=\tXbae>=4.8.4", | 21 | "BUILDLINK_API_DEPENDS.Xbae+=\tXbae>=4.8.4", | |
22 | "BUILDLINK_ABI_DEPENDS.Xbae+=\tXbae>=4.51.01nb2", | 22 | "BUILDLINK_ABI_DEPENDS.Xbae+=\tXbae>=4.51.01nb2", | |
23 | "BUILDLINK_PKGSRCDIR.Xbae?=\t../../x11/Xbae", | 23 | "BUILDLINK_PKGSRCDIR.Xbae?=\t../../x11/Xbae", | |
24 | "", | 24 | "", | |
25 | ".include \"../../mk/motif.buildlink3.mk\"", | 25 | ".include \"../../mk/motif.buildlink3.mk\"", | |
26 | ".endif # XBAE_BUILDLINK3_MK", | 26 | ".endif # XBAE_BUILDLINK3_MK", | |
27 | "", | 27 | "", | |
28 | "BUILDLINK_TREE+=\t-Xbae") | 28 | "BUILDLINK_TREE+=\t-Xbae") | |
29 | 29 | |||
30 | ChecklinesBuildlink3Mk(mklines) | 30 | ChecklinesBuildlink3Mk(mklines) | |
31 | 31 | |||
32 | t.CheckOutputLines( | 32 | t.CheckOutputLines( | |
33 | "ERROR: ~/buildlink3.mk:2: This comment indicates unfinished work (url2pkg).") | 33 | "ERROR: ~/category/package/buildlink3.mk:2: This comment indicates unfinished work (url2pkg).") | |
34 | } | 34 | } | |
35 | 35 | |||
36 | // Before version 5.3, pkglint wrongly warned here. | 36 | // Before version 5.3, pkglint wrongly warned here. | |
37 | // The mk/haskell.mk file takes care of constructing the correct PKGNAME, | 37 | // The mk/haskell.mk file takes care of constructing the correct PKGNAME, | |
38 | // but pkglint had not looked at that file. | 38 | // but pkglint had not looked at that file. | |
39 | // | 39 | // | |
40 | // Since then, pkglint also looks at files from mk/ when they are directly | 40 | // Since then, pkglint also looks at files from mk/ when they are directly | |
41 | // included, and therefore finds the default definition for PKGNAME. | 41 | // included, and therefore finds the default definition for PKGNAME. | |
42 | func (s *Suite) Test_ChecklinesBuildlink3Mk__name_mismatch_Haskell_incomplete(c *check.C) { | 42 | func (s *Suite) Test_ChecklinesBuildlink3Mk__name_mismatch_Haskell_incomplete(c *check.C) { | |
43 | t := s.Init(c) | 43 | t := s.Init(c) | |
44 | 44 | |||
45 | t.SetupPackage("x11/hs-X11", | 45 | t.SetupPackage("x11/hs-X11", | |
46 | "DISTNAME=\tX11-1.0") | 46 | "DISTNAME=\tX11-1.0") |
@@ -6,26 +6,27 @@ import ( | @@ -6,26 +6,27 @@ import ( | |||
6 | ) | 6 | ) | |
7 | 7 | |||
8 | // Vartype is a combination of a data type and a permission specification. | 8 | // Vartype is a combination of a data type and a permission specification. | |
9 | // See vardefs.go for examples, and vartypecheck.go for the implementation. | 9 | // See vardefs.go for examples, and vartypecheck.go for the implementation. | |
10 | type Vartype struct { | 10 | type Vartype struct { | |
11 | kindOfList KindOfList | 11 | kindOfList KindOfList | |
12 | basicType *BasicType | 12 | basicType *BasicType | |
13 | aclEntries []ACLEntry | 13 | aclEntries []ACLEntry | |
14 | guessed bool | 14 | guessed bool | |
15 | } | 15 | } | |
16 | 16 | |||
17 | type KindOfList uint8 | 17 | type KindOfList uint8 | |
18 | 18 | |||
19 | // TODO: Remove lkSpace. Since 2015 the .for variables are split on shell words, like everywhere else. | |||
19 | const ( | 20 | const ( | |
20 | lkNone KindOfList = iota // Plain data type | 21 | lkNone KindOfList = iota // Plain data type | |
21 | lkSpace // List entries are separated by whitespace; used in .for loops. | 22 | lkSpace // List entries are separated by whitespace; used in .for loops. | |
22 | lkShell // List entries are shell words; used in the :M, :S modifiers. | 23 | lkShell // List entries are shell words; used in the :M, :S modifiers. | |
23 | ) | 24 | ) | |
24 | 25 | |||
25 | type ACLEntry struct { | 26 | type ACLEntry struct { | |
26 | glob string // Examples: "Makefile", "*.mk" | 27 | glob string // Examples: "Makefile", "*.mk" | |
27 | permissions ACLPermissions | 28 | permissions ACLPermissions | |
28 | } | 29 | } | |
29 | 30 | |||
30 | type ACLPermissions uint8 | 31 | type ACLPermissions uint8 | |
31 | 32 | |||
@@ -193,26 +194,30 @@ type BasicType struct { | @@ -193,26 +194,30 @@ type BasicType struct { | |||
193 | checker func(*VartypeCheck) | 194 | checker func(*VartypeCheck) | |
194 | } | 195 | } | |
195 | 196 | |||
196 | func (bt *BasicType) IsEnum() bool { | 197 | func (bt *BasicType) IsEnum() bool { | |
197 | return hasPrefix(bt.name, "enum: ") | 198 | return hasPrefix(bt.name, "enum: ") | |
198 | } | 199 | } | |
199 | func (bt *BasicType) HasEnum(value string) bool { | 200 | func (bt *BasicType) HasEnum(value string) bool { | |
200 | return !contains(value, " ") && contains(bt.name, " "+value+" ") | 201 | return !contains(value, " ") && contains(bt.name, " "+value+" ") | |
201 | } | 202 | } | |
202 | func (bt *BasicType) AllowedEnums() string { | 203 | func (bt *BasicType) AllowedEnums() string { | |
203 | return bt.name[6 : len(bt.name)-1] | 204 | return bt.name[6 : len(bt.name)-1] | |
204 | } | 205 | } | |
205 | 206 | |||
207 | // TODO: Try to implement BasicType.PossibleChars() | |||
208 | // TODO: Try to implement BasicType.CanBeEmpty() | |||
209 | // TODO: Try to implement BasicType.PossibleWords() / PossibleValues() | |||
210 | ||||
206 | var ( | 211 | var ( | |
207 | BtAwkCommand = &BasicType{"AwkCommand", (*VartypeCheck).AwkCommand} | 212 | BtAwkCommand = &BasicType{"AwkCommand", (*VartypeCheck).AwkCommand} | |
208 | BtBasicRegularExpression = &BasicType{"BasicRegularExpression", (*VartypeCheck).BasicRegularExpression} | 213 | BtBasicRegularExpression = &BasicType{"BasicRegularExpression", (*VartypeCheck).BasicRegularExpression} | |
209 | BtBuildlinkDepmethod = &BasicType{"BuildlinkDepmethod", (*VartypeCheck).BuildlinkDepmethod} | 214 | BtBuildlinkDepmethod = &BasicType{"BuildlinkDepmethod", (*VartypeCheck).BuildlinkDepmethod} | |
210 | BtCategory = &BasicType{"Category", (*VartypeCheck).Category} | 215 | BtCategory = &BasicType{"Category", (*VartypeCheck).Category} | |
211 | BtCFlag = &BasicType{"CFlag", (*VartypeCheck).CFlag} | 216 | BtCFlag = &BasicType{"CFlag", (*VartypeCheck).CFlag} | |
212 | BtComment = &BasicType{"Comment", (*VartypeCheck).Comment} | 217 | BtComment = &BasicType{"Comment", (*VartypeCheck).Comment} | |
213 | BtConfFiles = &BasicType{"ConfFiles", (*VartypeCheck).ConfFiles} | 218 | BtConfFiles = &BasicType{"ConfFiles", (*VartypeCheck).ConfFiles} | |
214 | BtDependency = &BasicType{"Dependency", (*VartypeCheck).Dependency} | 219 | BtDependency = &BasicType{"Dependency", (*VartypeCheck).Dependency} | |
215 | BtDependencyWithPath = &BasicType{"DependencyWithPath", (*VartypeCheck).DependencyWithPath} | 220 | BtDependencyWithPath = &BasicType{"DependencyWithPath", (*VartypeCheck).DependencyWithPath} | |
216 | BtDistSuffix = &BasicType{"DistSuffix", (*VartypeCheck).DistSuffix} | 221 | BtDistSuffix = &BasicType{"DistSuffix", (*VartypeCheck).DistSuffix} | |
217 | BtEmulPlatform = &BasicType{"EmulPlatform", (*VartypeCheck).EmulPlatform} | 222 | BtEmulPlatform = &BasicType{"EmulPlatform", (*VartypeCheck).EmulPlatform} | |
218 | BtFetchURL = &BasicType{"FetchURL", (*VartypeCheck).FetchURL} | 223 | BtFetchURL = &BasicType{"FetchURL", (*VartypeCheck).FetchURL} |
@@ -13,27 +13,27 @@ func (s *Suite) Test_CheckdirCategory__t | @@ -13,27 +13,27 @@ func (s *Suite) Test_CheckdirCategory__t | |||
13 | "SUBDIR-=unknown #doesn\u2019t work", | 13 | "SUBDIR-=unknown #doesn\u2019t work", | |
14 | "", | 14 | "", | |
15 | ".include \"../mk/category.mk\"") | 15 | ".include \"../mk/category.mk\"") | |
16 | 16 | |||
17 | CheckdirCategory(t.File("archivers")) | 17 | CheckdirCategory(t.File("archivers")) | |
18 | 18 | |||
19 | t.CheckOutputLines( | 19 | t.CheckOutputLines( | |
20 | "ERROR: ~/archivers/Makefile:1: Expected \"# $"+"NetBSD$\".", | 20 | "ERROR: ~/archivers/Makefile:1: Expected \"# $"+"NetBSD$\".", | |
21 | "WARN: ~/archivers/Makefile:4: Line contains invalid characters (U+2019).", | 21 | "WARN: ~/archivers/Makefile:4: Line contains invalid characters (U+2019).", | |
22 | "WARN: ~/archivers/Makefile:4: SUBDIR- is defined but not used.", | 22 | "WARN: ~/archivers/Makefile:4: SUBDIR- is defined but not used.", | |
23 | "NOTE: ~/archivers/Makefile:2: This variable value should be aligned to column 17.", | 23 | "NOTE: ~/archivers/Makefile:2: This variable value should be aligned to column 17.", | |
24 | "NOTE: ~/archivers/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.", | 24 | "NOTE: ~/archivers/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.", | |
25 | "NOTE: ~/archivers/Makefile:4: This variable value should be aligned to column 17.", | 25 | "NOTE: ~/archivers/Makefile:4: This variable value should be aligned to column 17.", | |
26 | "ERROR: ~/archivers/Makefile:6: \"../mk/category.mk\" does not exist.", | 26 | "ERROR: ~/archivers/Makefile:6: Relative path \"../mk/category.mk\" does not exist.", | |
27 | "NOTE: ~/archivers/Makefile:1: Empty line expected after this line.", | 27 | "NOTE: ~/archivers/Makefile:1: Empty line expected after this line.", | |
28 | "ERROR: ~/archivers/Makefile:2: COMMENT= line expected.", | 28 | "ERROR: ~/archivers/Makefile:2: COMMENT= line expected.", | |
29 | "NOTE: ~/archivers/Makefile:1: Empty line expected after this line.", // XXX: Duplicate. | 29 | "NOTE: ~/archivers/Makefile:1: Empty line expected after this line.", // XXX: Duplicate. | |
30 | "WARN: ~/archivers/Makefile:3: \"aaaaa\" should come before \"pkg1\".", | 30 | "WARN: ~/archivers/Makefile:3: \"aaaaa\" should come before \"pkg1\".", | |
31 | "ERROR: ~/archivers/Makefile:4: SUBDIR+= line or empty line expected.", | 31 | "ERROR: ~/archivers/Makefile:4: SUBDIR+= line or empty line expected.", | |
32 | "ERROR: ~/archivers/Makefile:2: \"pkg1\" exists in the Makefile but not in the file system.", | 32 | "ERROR: ~/archivers/Makefile:2: \"pkg1\" exists in the Makefile but not in the file system.", | |
33 | "ERROR: ~/archivers/Makefile:3: \"aaaaa\" exists in the Makefile but not in the file system.", | 33 | "ERROR: ~/archivers/Makefile:3: \"aaaaa\" exists in the Makefile but not in the file system.", | |
34 | "NOTE: ~/archivers/Makefile:3: Empty line expected after this line.", | 34 | "NOTE: ~/archivers/Makefile:3: Empty line expected after this line.", | |
35 | "WARN: ~/archivers/Makefile:4: This line should contain the following text: .include \"../mk/misc/category.mk\"", | 35 | "WARN: ~/archivers/Makefile:4: This line should contain the following text: .include \"../mk/misc/category.mk\"", | |
36 | "ERROR: ~/archivers/Makefile:4: The file should end here.") | 36 | "ERROR: ~/archivers/Makefile:4: The file should end here.") | |
37 | } | 37 | } | |
38 | 38 | |||
39 | func (s *Suite) Test_CheckdirCategory__invalid_comment(c *check.C) { | 39 | func (s *Suite) Test_CheckdirCategory__invalid_comment(c *check.C) { |
@@ -16,27 +16,27 @@ func (lc *LicenseChecker) Check(value st | @@ -16,27 +16,27 @@ func (lc *LicenseChecker) Check(value st | |||
16 | } else { | 16 | } else { | |
17 | lc.MkLine.Errorf("Parse error for appended license condition %q.", value) | 17 | lc.MkLine.Errorf("Parse error for appended license condition %q.", value) | |
18 | } | 18 | } | |
19 | return | 19 | return | |
20 | } | 20 | } | |
21 | 21 | |||
22 | cond.Walk(lc.checkNode) | 22 | cond.Walk(lc.checkNode) | |
23 | } | 23 | } | |
24 | 24 | |||
25 | func (lc *LicenseChecker) checkName(license string) { | 25 | func (lc *LicenseChecker) checkName(license string) { | |
26 | licenseFile := "" | 26 | licenseFile := "" | |
27 | if G.Pkg != nil { | 27 | if G.Pkg != nil { | |
28 | if mkline := G.Pkg.vars.FirstDefinition("LICENSE_FILE"); mkline != nil { | 28 | if mkline := G.Pkg.vars.FirstDefinition("LICENSE_FILE"); mkline != nil { | |
29 | licenseFile = G.Pkg.File(mkline.ResolveVarsInRelativePath(mkline.Value(), false)) | 29 | licenseFile = G.Pkg.File(mkline.ResolveVarsInRelativePath(mkline.Value())) | |
30 | } | 30 | } | |
31 | } | 31 | } | |
32 | if licenseFile == "" { | 32 | if licenseFile == "" { | |
33 | licenseFile = G.Pkgsrc.File("licenses/" + license) | 33 | licenseFile = G.Pkgsrc.File("licenses/" + license) | |
34 | if G.Pkgsrc.UsedLicenses != nil { | 34 | if G.Pkgsrc.UsedLicenses != nil { | |
35 | G.Pkgsrc.UsedLicenses[license] = true | 35 | G.Pkgsrc.UsedLicenses[license] = true | |
36 | } | 36 | } | |
37 | } | 37 | } | |
38 | 38 | |||
39 | if !fileExists(licenseFile) { | 39 | if !fileExists(licenseFile) { | |
40 | lc.MkLine.Warnf("License file %s does not exist.", cleanpath(licenseFile)) | 40 | lc.MkLine.Warnf("License file %s does not exist.", cleanpath(licenseFile)) | |
41 | } | 41 | } | |
42 | 42 |
@@ -25,26 +25,28 @@ type RawLine struct { | @@ -25,26 +25,28 @@ type RawLine struct { | |||
25 | 25 | |||
26 | // XXX: Since only Autofix needs to distinguish between orignl and textnl, | 26 | // XXX: Since only Autofix needs to distinguish between orignl and textnl, | |
27 | // one of these fields should probably be moved there. | 27 | // one of these fields should probably be moved there. | |
28 | } | 28 | } | |
29 | 29 | |||
30 | func (rline *RawLine) String() string { return sprintf("%d:%s", rline.Lineno, rline.textnl) } | 30 | func (rline *RawLine) String() string { return sprintf("%d:%s", rline.Lineno, rline.textnl) } | |
31 | 31 | |||
32 | // Line represents a line of text from a file. | 32 | // Line represents a line of text from a file. | |
33 | // It aliases a pointer type to reduces the number of *Line occurrences in the code. | 33 | // It aliases a pointer type to reduces the number of *Line occurrences in the code. | |
34 | // Using a type alias is more efficient than an interface type, I guess. | 34 | // Using a type alias is more efficient than an interface type, I guess. | |
35 | type Line = *LineImpl | 35 | type Line = *LineImpl | |
36 | 36 | |||
37 | type LineImpl struct { | 37 | type LineImpl struct { | |
38 | // TODO: Consider storing pointers to the Filename and Basename instead of strings to save memory. | |||
39 | // But first find out where and why pkglint needs so much memory (200 MB for a full recursive run over pkgsrc + wip). | |||
38 | Filename string // uses / as directory separator on all platforms | 40 | Filename string // uses / as directory separator on all platforms | |
39 | Basename string // the last component of the Filename | 41 | Basename string // the last component of the Filename | |
40 | firstLine int32 // zero means the whole file, -1 means EOF | 42 | firstLine int32 // zero means the whole file, -1 means EOF | |
41 | lastLine int32 // usually the same as firstLine, may differ in Makefiles | 43 | lastLine int32 // usually the same as firstLine, may differ in Makefiles | |
42 | // without the trailing newline character; | 44 | // without the trailing newline character; | |
43 | // in Makefiles, also contains the text from the continuation lines | 45 | // in Makefiles, also contains the text from the continuation lines | |
44 | Text string | 46 | Text string | |
45 | raw []*RawLine // contains the original text including trailing newline | 47 | raw []*RawLine // contains the original text including trailing newline | |
46 | autofix *Autofix // any changes that pkglint would like to apply to the line | 48 | autofix *Autofix // any changes that pkglint would like to apply to the line | |
47 | Once | 49 | Once | |
48 | 50 | |||
49 | // XXX: Filename and Basename could be replaced with a pointer to a Lines object. | 51 | // XXX: Filename and Basename could be replaced with a pointer to a Lines object. | |
50 | } | 52 | } |
@@ -35,41 +35,41 @@ func (ls *LinesImpl) SaveAutofixChanges( | @@ -35,41 +35,41 @@ func (ls *LinesImpl) SaveAutofixChanges( | |||
35 | SaveAutofixChanges(ls) | 35 | SaveAutofixChanges(ls) | |
36 | } | 36 | } | |
37 | 37 | |||
38 | func (ls *LinesImpl) CheckRcsID(index int, prefixRe regex.Pattern, suggestedPrefix string) bool { | 38 | func (ls *LinesImpl) CheckRcsID(index int, prefixRe regex.Pattern, suggestedPrefix string) bool { | |
39 | if trace.Tracing { | 39 | if trace.Tracing { | |
40 | defer trace.Call(prefixRe, suggestedPrefix)() | 40 | defer trace.Call(prefixRe, suggestedPrefix)() | |
41 | } | 41 | } | |
42 | 42 | |||
43 | line := ls.Lines[index] | 43 | line := ls.Lines[index] | |
44 | if m, expanded := match1(line.Text, `^`+prefixRe+`\$`+`NetBSD(:[^\$]+)?\$$`); m { | 44 | if m, expanded := match1(line.Text, `^`+prefixRe+`\$`+`NetBSD(:[^\$]+)?\$$`); m { | |
45 | 45 | |||
46 | if G.Wip && expanded != "" { | 46 | if G.Wip && expanded != "" { | |
47 | fix := line.Autofix() | 47 | fix := line.Autofix() | |
48 | fix.Errorf("Expected exactly %q.", suggestedPrefix+"$"+"NetBSD$") | 48 | fix.Notef("Expected exactly %q.", suggestedPrefix+"$"+"NetBSD$") | |
49 | fix.Explain( | 49 | fix.Explain( | |
50 | "Several files in pkgsrc must contain the CVS Id, so that their", | 50 | "Several files in pkgsrc must contain the CVS Id, so that their", | |
51 | "current version can be traced back later from a binary package.", | 51 | "current version can be traced back later from a binary package.", | |
52 | "This is to ensure reproducible builds, for example for finding bugs.", | 52 | "This is to ensure reproducible builds, for example for finding bugs.", | |
53 | "", | 53 | "", | |
54 | "These CVS Ids are specific to the CVS version control system, and", | 54 | "These CVS Ids are specific to the CVS version control system, and", | |
55 | "pkgsrc-wip uses Git instead. Therefore, having the expanded CVS Ids", | 55 | "pkgsrc-wip uses Git instead. Therefore, having the expanded CVS Ids", | |
56 | "in those files represents the file from which they were originally", | 56 | "in those files represents the file from which they were originally", | |
57 | "copied but not their current state. Because of that, these markers", | 57 | "copied but not their current state. Because of that, these markers", | |
58 | "should be replaced with the plain, unexpanded string $"+"NetBSD$.", | 58 | "should be replaced with the plain, unexpanded string $"+"NetBSD$.", | |
59 | "", | 59 | "", | |
60 | "To preserve the history of the CVS Id, should that ever be needed,", | 60 | "To preserve the history of the CVS Id, should that ever be needed,", | |
61 | "remove the leading $.") | 61 | "remove the leading $.") | |
62 | fix.InsertBefore(suggestedPrefix + "$" + "NetBSD$") | 62 | fix.ReplaceRegex(`.*`, suggestedPrefix+"$"+"NetBSD$", 1) | |
63 | fix.Apply() | 63 | fix.Apply() | |
64 | } | 64 | } | |
65 | 65 | |||
66 | return true | 66 | return true | |
67 | } | 67 | } | |
68 | 68 | |||
69 | fix := line.Autofix() | 69 | fix := line.Autofix() | |
70 | fix.Errorf("Expected %q.", suggestedPrefix+"$"+"NetBSD$") | 70 | fix.Errorf("Expected %q.", suggestedPrefix+"$"+"NetBSD$") | |
71 | fix.Explain( | 71 | fix.Explain( | |
72 | "Most files in pkgsrc contain the CVS Id, so that their current", | 72 | "Most files in pkgsrc contain the CVS Id, so that their current", | |
73 | "version can be traced back later from a binary package.", | 73 | "version can be traced back later from a binary package.", | |
74 | "This is to ensure reproducible builds, for example for finding bugs.") | 74 | "This is to ensure reproducible builds, for example for finding bugs.") | |
75 | fix.InsertBefore(suggestedPrefix + "$" + "NetBSD$") | 75 | fix.InsertBefore(suggestedPrefix + "$" + "NetBSD$") |
@@ -37,18 +37,28 @@ func (s *Suite) Test_Lines_CheckRcsID__w | @@ -37,18 +37,28 @@ func (s *Suite) Test_Lines_CheckRcsID__w | |||
37 | "# $"+"NetBSD: dummy $") | 37 | "# $"+"NetBSD: dummy $") | |
38 | t.CreateFileLines("wip/package/file2.mk", | 38 | t.CreateFileLines("wip/package/file2.mk", | |
39 | "# $"+"NetBSD$") | 39 | "# $"+"NetBSD$") | |
40 | t.CreateFileLines("wip/package/file3.mk", | 40 | t.CreateFileLines("wip/package/file3.mk", | |
41 | "# $"+"Id: dummy $") | 41 | "# $"+"Id: dummy $") | |
42 | t.CreateFileLines("wip/package/file4.mk", | 42 | t.CreateFileLines("wip/package/file4.mk", | |
43 | "# $"+"Id$") | 43 | "# $"+"Id$") | |
44 | t.CreateFileLines("wip/package/file5.mk", | 44 | t.CreateFileLines("wip/package/file5.mk", | |
45 | "# $"+"FreeBSD$") | 45 | "# $"+"FreeBSD$") | |
46 | 46 | |||
47 | G.CheckDirent(t.File("wip/package")) | 47 | G.CheckDirent(t.File("wip/package")) | |
48 | 48 | |||
49 | t.CheckOutputLines( | 49 | t.CheckOutputLines( | |
50 | "ERROR: ~/wip/package/file1.mk:1: Expected exactly \"# $"+"NetBSD$\".", | 50 | "NOTE: ~/wip/package/file1.mk:1: Expected exactly \"# $"+"NetBSD$\".", | |
51 | "ERROR: ~/wip/package/file3.mk:1: Expected \"# $"+"NetBSD$\".", | 51 | "ERROR: ~/wip/package/file3.mk:1: Expected \"# $"+"NetBSD$\".", | |
52 | "ERROR: ~/wip/package/file4.mk:1: Expected \"# $"+"NetBSD$\".", | 52 | "ERROR: ~/wip/package/file4.mk:1: Expected \"# $"+"NetBSD$\".", | |
53 | "ERROR: ~/wip/package/file5.mk:1: Expected \"# $"+"NetBSD$\".") | 53 | "ERROR: ~/wip/package/file5.mk:1: Expected \"# $"+"NetBSD$\".") | |
54 | ||||
55 | G.Logger.Opts.Autofix = true | |||
56 | ||||
57 | G.CheckDirent(t.File("wip/package")) | |||
58 | ||||
59 | t.CheckOutputLines( | |||
60 | "AUTOFIX: ~/wip/package/file1.mk:1: Replacing \"# $NetBSD: lines_test.go,v 1.2 2018/12/02 23:12:43 rillig Exp $\" with \"# $NetBSD: lines_test.go,v 1.2 2018/12/02 23:12:43 rillig Exp $\".", | |||
61 | "AUTOFIX: ~/wip/package/file3.mk:1: Inserting a line \"# $NetBSD: lines_test.go,v 1.2 2018/12/02 23:12:43 rillig Exp $\" before this line.", | |||
62 | "AUTOFIX: ~/wip/package/file4.mk:1: Inserting a line \"# $NetBSD: lines_test.go,v 1.2 2018/12/02 23:12:43 rillig Exp $\" before this line.", | |||
63 | "AUTOFIX: ~/wip/package/file5.mk:1: Inserting a line \"# $NetBSD: lines_test.go,v 1.2 2018/12/02 23:12:43 rillig Exp $\" before this line.") | |||
54 | } | 64 | } |
@@ -21,41 +21,43 @@ type MkLineImpl struct { | @@ -21,41 +21,43 @@ type MkLineImpl struct { | |||
21 | data interface{} // One of the following mkLine* types | 21 | data interface{} // One of the following mkLine* types | |
22 | } | 22 | } | |
23 | type mkLineAssign = *mkLineAssignImpl // See https://github.com/golang/go/issues/28045 | 23 | type mkLineAssign = *mkLineAssignImpl // See https://github.com/golang/go/issues/28045 | |
24 | type mkLineAssignImpl struct { | 24 | type mkLineAssignImpl struct { | |
25 | commented bool // Whether the whole variable assignment is commented out | 25 | commented bool // Whether the whole variable assignment is commented out | |
26 | varname string // e.g. "HOMEPAGE", "SUBST_SED.perl" | 26 | varname string // e.g. "HOMEPAGE", "SUBST_SED.perl" | |
27 | varcanon string // e.g. "HOMEPAGE", "SUBST_SED.*" | 27 | varcanon string // e.g. "HOMEPAGE", "SUBST_SED.*" | |
28 | varparam string // e.g. "", "perl" | 28 | varparam string // e.g. "", "perl" | |
29 | op MkOperator // | 29 | op MkOperator // | |
30 | valueAlign string // The text up to and including the assignment operator, e.g. VARNAME+=\t | 30 | valueAlign string // The text up to and including the assignment operator, e.g. VARNAME+=\t | |
31 | value string // The trimmed value | 31 | value string // The trimmed value | |
32 | valueMk []*MkToken // The value, sent through splitIntoMkWords | 32 | valueMk []*MkToken // The value, sent through splitIntoMkWords | |
33 | valueMkRest string // nonempty in case of parse errors | 33 | valueMkRest string // nonempty in case of parse errors | |
34 | fields []string // The value, space-separated according to shell quoting rules | |||
34 | comment string | 35 | comment string | |
35 | } | 36 | } | |
36 | type mkLineShell struct { | 37 | type mkLineShell struct { | |
37 | command string | 38 | command string | |
38 | } | 39 | } | |
39 | type mkLineComment struct{} // See mkLineAssignImpl.commented for another type of comment line | 40 | type mkLineComment struct{} // See mkLineAssignImpl.commented for another type of comment line | |
40 | type mkLineEmpty struct{} | 41 | type mkLineEmpty struct{} | |
41 | type mkLineDirective = *mkLineDirectiveImpl // See https://github.com/golang/go/issues/28045 | 42 | type mkLineDirective = *mkLineDirectiveImpl // See https://github.com/golang/go/issues/28045 | |
42 | type mkLineDirectiveImpl struct { | 43 | type mkLineDirectiveImpl struct { | |
43 | indent string // the space between the leading "." and the directive | 44 | indent string // the space between the leading "." and the directive | |
44 | directive string // "if", "else", "for", etc. | 45 | directive string // "if", "else", "for", etc. | |
45 | args string | 46 | args string | |
46 | comment string // mainly interesting for .endif and .endfor | 47 | comment string // mainly interesting for .endif and .endfor | |
47 | elseLine MkLine // for .if (filled in later) | 48 | elseLine MkLine // for .if (filled in later) | |
48 | cond MkCond // for .if and .elif (filled in later as needed) | 49 | cond MkCond // for .if and .elif (filled in on first access) | |
50 | fields []string // the arguments for the .for loop (filled in on first access) | |||
49 | } | 51 | } | |
50 | type mkLineInclude = *mkLineIncludeImpl // See https://github.com/golang/go/issues/28045 | 52 | type mkLineInclude = *mkLineIncludeImpl // See https://github.com/golang/go/issues/28045 | |
51 | type mkLineIncludeImpl struct { | 53 | type mkLineIncludeImpl struct { | |
52 | mustExist bool // for .sinclude, nonexistent files are ignored | 54 | mustExist bool // for .sinclude, nonexistent files are ignored | |
53 | sys bool // whether the include uses <file.mk> (very rare) instead of "file.mk" | 55 | sys bool // whether the include uses <file.mk> (very rare) instead of "file.mk" | |
54 | indent string // the space between the leading "." and the directive | 56 | indent string // the space between the leading "." and the directive | |
55 | includedFile string // the text between the <brackets> or "quotes" | 57 | includedFile string // the text between the <brackets> or "quotes" | |
56 | conditionalVars []string // variables on which this inclusion depends (filled in later, as needed) | 58 | conditionalVars []string // variables on which this inclusion depends (filled in later, as needed) | |
57 | } | 59 | } | |
58 | type mkLineDependency struct { | 60 | type mkLineDependency struct { | |
59 | targets string | 61 | targets string | |
60 | sources string | 62 | sources string | |
61 | } | 63 | } | |
@@ -100,45 +102,46 @@ func NewMkLine(line Line) *MkLineImpl { | @@ -100,45 +102,46 @@ func NewMkLine(line Line) *MkLineImpl { | |||
100 | "continues until the end of the line. To escape the #, write \\#.") | 102 | "continues until the end of the line. To escape the #, write \\#.") | |
101 | } | 103 | } | |
102 | 104 | |||
103 | return &MkLineImpl{line, &mkLineAssignImpl{ | 105 | return &MkLineImpl{line, &mkLineAssignImpl{ | |
104 | commented, | 106 | commented, | |
105 | varname, | 107 | varname, | |
106 | varnameCanon(varname), | 108 | varnameCanon(varname), | |
107 | varnameParam(varname), | 109 | varnameParam(varname), | |
108 | NewMkOperator(op), | 110 | NewMkOperator(op), | |
109 | valueAlign, | 111 | valueAlign, | |
110 | strings.Replace(value, "\\#", "#", -1), | 112 | strings.Replace(value, "\\#", "#", -1), | |
111 | nil, | 113 | nil, | |
112 | "", | 114 | "", | |
115 | nil, | |||
113 | comment}} | 116 | comment}} | |
114 | } | 117 | } | |
115 | 118 | |||
116 | if hasPrefix(text, "\t") { | 119 | if hasPrefix(text, "\t") { | |
117 | shellcmd := text[1:] | 120 | shellcmd := text[1:] | |
118 | return &MkLineImpl{line, mkLineShell{shellcmd}} | 121 | return &MkLineImpl{line, mkLineShell{shellcmd}} | |
119 | } | 122 | } | |
120 | 123 | |||
121 | trimmedText := trimHspace(text) | 124 | trimmedText := trimHspace(text) | |
122 | if strings.HasPrefix(trimmedText, "#") { | 125 | if strings.HasPrefix(trimmedText, "#") { | |
123 | return &MkLineImpl{line, mkLineComment{}} | 126 | return &MkLineImpl{line, mkLineComment{}} | |
124 | } | 127 | } | |
125 | 128 | |||
126 | if trimmedText == "" { | 129 | if trimmedText == "" { | |
127 | return &MkLineImpl{line, mkLineEmpty{}} | 130 | return &MkLineImpl{line, mkLineEmpty{}} | |
128 | } | 131 | } | |
129 | 132 | |||
130 | if m, indent, directive, args, comment := matchMkDirective(text); m { | 133 | if m, indent, directive, args, comment := matchMkDirective(text); m { | |
131 | return &MkLineImpl{line, &mkLineDirectiveImpl{indent, directive, args, comment, nil, nil}} | 134 | return &MkLineImpl{line, &mkLineDirectiveImpl{indent, directive, args, comment, nil, nil, nil}} | |
132 | } | 135 | } | |
133 | 136 | |||
134 | if m, indent, directive, includedFile := MatchMkInclude(text); m { | 137 | if m, indent, directive, includedFile := MatchMkInclude(text); m { | |
135 | return &MkLineImpl{line, &mkLineIncludeImpl{directive == "include", false, indent, includedFile, nil}} | 138 | return &MkLineImpl{line, &mkLineIncludeImpl{directive == "include", false, indent, includedFile, nil}} | |
136 | } | 139 | } | |
137 | 140 | |||
138 | if m, indent, directive, includedFile := match3(text, `^\.([\t ]*)(s?include)[\t ]+<([^>]+)>[\t ]*(?:#.*)?$`); m { | 141 | if m, indent, directive, includedFile := match3(text, `^\.([\t ]*)(s?include)[\t ]+<([^>]+)>[\t ]*(?:#.*)?$`); m { | |
139 | return &MkLineImpl{line, &mkLineIncludeImpl{directive == "include", true, indent, includedFile, nil}} | 142 | return &MkLineImpl{line, &mkLineIncludeImpl{directive == "include", true, indent, includedFile, nil}} | |
140 | } | 143 | } | |
141 | 144 | |||
142 | // XXX: Replace this regular expression with proper parsing. | 145 | // XXX: Replace this regular expression with proper parsing. | |
143 | // There might be a ${VAR:M*.c} in these variables, which currently confuses the "parser". | 146 | // There might be a ${VAR:M*.c} in these variables, which currently confuses the "parser". | |
144 | if m, targets, whitespace, sources := match3(text, `^([^\t :]+(?:[\t ]*[^\t :]+)*)([\t ]*):[\t ]*([^#]*?)(?:[\t ]*#.*)?$`); m { | 147 | if m, targets, whitespace, sources := match3(text, `^([^\t :]+(?:[\t ]*[^\t :]+)*)([\t ]*):[\t ]*([^#]*?)(?:[\t ]*#.*)?$`); m { | |
@@ -462,37 +465,72 @@ func (mkline *MkLineImpl) ValueTokens() | @@ -462,37 +465,72 @@ func (mkline *MkLineImpl) ValueTokens() | |||
462 | } | 465 | } | |
463 | 466 | |||
464 | assign := mkline.data.(mkLineAssign) | 467 | assign := mkline.data.(mkLineAssign) | |
465 | if assign.valueMk != nil || assign.valueMkRest != "" { | 468 | if assign.valueMk != nil || assign.valueMkRest != "" { | |
466 | return assign.valueMk | 469 | return assign.valueMk | |
467 | } | 470 | } | |
468 | 471 | |||
469 | p := NewMkParser(mkline.Line, value, true) | 472 | p := NewMkParser(mkline.Line, value, true) | |
470 | assign.valueMk = p.MkTokens() | 473 | assign.valueMk = p.MkTokens() | |
471 | assign.valueMkRest = p.Rest() | 474 | assign.valueMkRest = p.Rest() | |
472 | return assign.valueMk | 475 | return assign.valueMk | |
473 | } | 476 | } | |
474 | 477 | |||
478 | // Fields applies to variable assignments and .for loops. | |||
479 | // For variable assignments, it returns the right-hand side, properly split into words. | |||
480 | // For .for loops, it returns all arguments (including variable names), properly split into words. | |||
481 | func (mkline *MkLineImpl) Fields() []string { | |||
482 | if mkline.IsVarassign() { | |||
483 | value := mkline.Value() | |||
484 | if value == "" { | |||
485 | return nil | |||
486 | } | |||
487 | ||||
488 | assign := mkline.data.(mkLineAssign) | |||
489 | if assign.fields != nil { | |||
490 | return assign.fields | |||
491 | } | |||
492 | ||||
493 | assign.fields = mkline.ValueFields(value) | |||
494 | return assign.fields | |||
495 | } | |||
496 | ||||
497 | // For .for loops. | |||
498 | args := mkline.Args() | |||
499 | if args == "" { | |||
500 | return nil | |||
501 | } | |||
502 | ||||
503 | directive := mkline.data.(mkLineDirective) | |||
504 | if directive.fields != nil { | |||
505 | return directive.fields | |||
506 | } | |||
507 | ||||
508 | directive.fields = mkline.ValueFields(args) | |||
509 | return directive.fields | |||
510 | ||||
511 | } | |||
512 | ||||
475 | func (mkline *MkLineImpl) WithoutMakeVariables(value string) string { | 513 | func (mkline *MkLineImpl) WithoutMakeVariables(value string) string { | |
476 | valueNovar := "" | 514 | valueNovar := "" | |
477 | for _, token := range NewMkParser(nil, value, false).MkTokens() { | 515 | for _, token := range NewMkParser(nil, value, false).MkTokens() { | |
478 | if token.Varuse == nil { | 516 | if token.Varuse == nil { | |
479 | valueNovar += token.Text | 517 | valueNovar += token.Text | |
480 | } | 518 | } | |
481 | } | 519 | } | |
482 | return valueNovar | 520 | return valueNovar | |
483 | } | 521 | } | |
484 | 522 | |||
485 | func (mkline *MkLineImpl) ResolveVarsInRelativePath(relativePath string, adjustDepth bool) string { | 523 | func (mkline *MkLineImpl) ResolveVarsInRelativePath(relativePath string) string { | |
486 | 524 | |||
487 | var basedir string | 525 | var basedir string | |
488 | if G.Pkg != nil { | 526 | if G.Pkg != nil { | |
489 | basedir = G.Pkg.File(".") | 527 | basedir = G.Pkg.File(".") | |
490 | } else { | 528 | } else { | |
491 | basedir = path.Dir(mkline.Filename) | 529 | basedir = path.Dir(mkline.Filename) | |
492 | } | 530 | } | |
493 | pkgsrcdir := relpath(basedir, G.Pkgsrc.File(".")) | 531 | pkgsrcdir := relpath(basedir, G.Pkgsrc.File(".")) | |
494 | 532 | |||
495 | if G.Testing { | 533 | if G.Testing { | |
496 | // Relative pkgsrc paths usually only contain two or three levels. | 534 | // Relative pkgsrc paths usually only contain two or three levels. | |
497 | // A possible reason for reaching this assertion is: | 535 | // A possible reason for reaching this assertion is: | |
498 | // Tests that access the file system must create their lines | 536 | // Tests that access the file system must create their lines | |
@@ -516,33 +554,26 @@ func (mkline *MkLineImpl) ResolveVarsInR | @@ -516,33 +554,26 @@ func (mkline *MkLineImpl) ResolveVarsInR | |||
516 | replaceLatest("${LUA_PKGSRCDIR}", "lang", `^lua[0-9]+$`, "../../lang/$0") | 554 | replaceLatest("${LUA_PKGSRCDIR}", "lang", `^lua[0-9]+$`, "../../lang/$0") | |
517 | replaceLatest("${PHPPKGSRCDIR}", "lang", `^php[0-9]+$`, "../../lang/$0") | 555 | replaceLatest("${PHPPKGSRCDIR}", "lang", `^php[0-9]+$`, "../../lang/$0") | |
518 | replaceLatest("${SUSE_DIR_PREFIX}", "emulators", `^(suse[0-9]+)_base$`, "$1") | 556 | replaceLatest("${SUSE_DIR_PREFIX}", "emulators", `^(suse[0-9]+)_base$`, "$1") | |
519 | replaceLatest("${PYPKGSRCDIR}", "lang", `^python[0-9]+$`, "../../lang/$0") | 557 | replaceLatest("${PYPKGSRCDIR}", "lang", `^python[0-9]+$`, "../../lang/$0") | |
520 | replaceLatest("${PYPACKAGE}", "lang", `^python[0-9]+$`, "$0") | 558 | replaceLatest("${PYPACKAGE}", "lang", `^python[0-9]+$`, "$0") | |
521 | if G.Pkg != nil { | 559 | if G.Pkg != nil { | |
522 | // XXX: Even if these variables are defined indirectly, | 560 | // XXX: Even if these variables are defined indirectly, | |
523 | // pkglint should be able to resolve them properly. | 561 | // pkglint should be able to resolve them properly. | |
524 | // There is already G.Pkg.Value, maybe that can be used here. | 562 | // There is already G.Pkg.Value, maybe that can be used here. | |
525 | tmp = strings.Replace(tmp, "${FILESDIR}", G.Pkg.Filesdir, -1) | 563 | tmp = strings.Replace(tmp, "${FILESDIR}", G.Pkg.Filesdir, -1) | |
526 | tmp = strings.Replace(tmp, "${PKGDIR}", G.Pkg.Pkgdir, -1) | 564 | tmp = strings.Replace(tmp, "${PKGDIR}", G.Pkg.Pkgdir, -1) | |
527 | } | 565 | } | |
528 | 566 | |||
529 | // TODO: What is this good for, and in which cases does it make a difference? | |||
530 | if adjustDepth { | |||
531 | if hasPrefix(tmp, "../../") && !hasPrefix(tmp[6:], ".") { | |||
532 | tmp = pkgsrcdir + "/" + tmp[6:] | |||
533 | } | |||
534 | } | |||
535 | ||||
536 | tmp = cleanpath(tmp) | 567 | tmp = cleanpath(tmp) | |
537 | 568 | |||
538 | if trace.Tracing && relativePath != tmp { | 569 | if trace.Tracing && relativePath != tmp { | |
539 | trace.Step2("resolveVarsInRelativePath: %q => %q", relativePath, tmp) | 570 | trace.Step2("resolveVarsInRelativePath: %q => %q", relativePath, tmp) | |
540 | } | 571 | } | |
541 | return tmp | 572 | return tmp | |
542 | } | 573 | } | |
543 | 574 | |||
544 | func (mkline *MkLineImpl) ExplainRelativeDirs() { | 575 | func (mkline *MkLineImpl) ExplainRelativeDirs() { | |
545 | G.Explain( | 576 | G.Explain( | |
546 | "Directories in the form \"../../category/package\" make it easier to", | 577 | "Directories in the form \"../../category/package\" make it easier to", | |
547 | "move a package around in pkgsrc, for example from pkgsrc-wip to the", | 578 | "move a package around in pkgsrc, for example from pkgsrc-wip to the", | |
548 | "main pkgsrc repository.") | 579 | "main pkgsrc repository.") |
@@ -349,27 +349,27 @@ func (pkglint *Pkglint) checkdirPackage( | @@ -349,27 +349,27 @@ func (pkglint *Pkglint) checkdirPackage( | |||
349 | } | 349 | } | |
350 | 350 | |||
351 | haveDistinfo := false | 351 | haveDistinfo := false | |
352 | havePatches := false | 352 | havePatches := false | |
353 | 353 | |||
354 | // Determine the used variables and PLIST directories before checking any of the Makefile fragments. | 354 | // Determine the used variables and PLIST directories before checking any of the Makefile fragments. | |
355 | for _, filename := range files { | 355 | for _, filename := range files { | |
356 | basename := path.Base(filename) | 356 | basename := path.Base(filename) | |
357 | if (hasPrefix(basename, "Makefile.") || hasSuffix(filename, ".mk")) && | 357 | if (hasPrefix(basename, "Makefile.") || hasSuffix(filename, ".mk")) && | |
358 | !matches(filename, `patch-`) && | 358 | !matches(filename, `patch-`) && | |
359 | !contains(filename, pkg.Pkgdir+"/") && | 359 | !contains(filename, pkg.Pkgdir+"/") && | |
360 | !contains(filename, pkg.Filesdir+"/") { | 360 | !contains(filename, pkg.Filesdir+"/") { | |
361 | if fragmentMklines := LoadMk(filename, MustSucceed); fragmentMklines != nil { | 361 | if fragmentMklines := LoadMk(filename, MustSucceed); fragmentMklines != nil { | |
362 | fragmentMklines.DetermineUsedVariables() | 362 | fragmentMklines.collectUsedVariables() | |
363 | } | 363 | } | |
364 | } | 364 | } | |
365 | if hasPrefix(basename, "PLIST") { | 365 | if hasPrefix(basename, "PLIST") { | |
366 | pkg.loadPlistDirs(filename) | 366 | pkg.loadPlistDirs(filename) | |
367 | } | 367 | } | |
368 | } | 368 | } | |
369 | 369 | |||
370 | for _, filename := range files { | 370 | for _, filename := range files { | |
371 | if containsVarRef(filename) { | 371 | if containsVarRef(filename) { | |
372 | if trace.Tracing { | 372 | if trace.Tracing { | |
373 | trace.Stepf("Skipping file %q because the name contains an unresolved variable.", filename) | 373 | trace.Stepf("Skipping file %q because the name contains an unresolved variable.", filename) | |
374 | } | 374 | } | |
375 | continue | 375 | continue | |
@@ -412,26 +412,28 @@ func (pkglint *Pkglint) Assertf(cond boo | @@ -412,26 +412,28 @@ func (pkglint *Pkglint) Assertf(cond boo | |||
412 | } | 412 | } | |
413 | 413 | |||
414 | // Returns the pkgsrc top-level directory, relative to the given file or directory. | 414 | // Returns the pkgsrc top-level directory, relative to the given file or directory. | |
415 | func findPkgsrcTopdir(filename string) string { | 415 | func findPkgsrcTopdir(filename string) string { | |
416 | for _, dir := range [...]string{".", "..", "../..", "../../.."} { | 416 | for _, dir := range [...]string{".", "..", "../..", "../../.."} { | |
417 | if fileExists(filename + "/" + dir + "/mk/bsd.pkg.mk") { | 417 | if fileExists(filename + "/" + dir + "/mk/bsd.pkg.mk") { | |
418 | return dir | 418 | return dir | |
419 | } | 419 | } | |
420 | } | 420 | } | |
421 | return "" | 421 | return "" | |
422 | } | 422 | } | |
423 | 423 | |||
424 | func resolveVariableRefs(text string) (resolved string) { | 424 | func resolveVariableRefs(text string) (resolved string) { | |
425 | // TODO: How does this fit into the Scope type, which is newer than this function? | |||
426 | ||||
425 | if !contains(text, "${") { | 427 | if !contains(text, "${") { | |
426 | return text | 428 | return text | |
427 | } | 429 | } | |
428 | 430 | |||
429 | visited := make(map[string]bool) // To prevent endless loops | 431 | visited := make(map[string]bool) // To prevent endless loops | |
430 | 432 | |||
431 | replacer := func(m string) string { | 433 | replacer := func(m string) string { | |
432 | varname := m[2 : len(m)-1] | 434 | varname := m[2 : len(m)-1] | |
433 | if !visited[varname] { | 435 | if !visited[varname] { | |
434 | visited[varname] = true | 436 | visited[varname] = true | |
435 | if G.Pkg != nil { | 437 | if G.Pkg != nil { | |
436 | if value, ok := G.Pkg.vars.Value(varname); ok { | 438 | if value, ok := G.Pkg.vars.Value(varname); ok { | |
437 | return value | 439 | return value |
@@ -385,27 +385,27 @@ func (s *Suite) Test_MkLine_VariableNeed | @@ -385,27 +385,27 @@ func (s *Suite) Test_MkLine_VariableNeed | |||
385 | } | 385 | } | |
386 | 386 | |||
387 | func (s *Suite) Test_MkLine_VariableNeedsQuoting__command_in_command(c *check.C) { | 387 | func (s *Suite) Test_MkLine_VariableNeedsQuoting__command_in_command(c *check.C) { | |
388 | t := s.Init(c) | 388 | t := s.Init(c) | |
389 | 389 | |||
390 | t.SetupVartypes() | 390 | t.SetupVartypes() | |
391 | t.SetupTool("find", "FIND", AtRunTime) | 391 | t.SetupTool("find", "FIND", AtRunTime) | |
392 | t.SetupTool("sort", "SORT", AtRunTime) | 392 | t.SetupTool("sort", "SORT", AtRunTime) | |
393 | G.Pkg = NewPackage(t.File("category/pkgbase")) | 393 | G.Pkg = NewPackage(t.File("category/pkgbase")) | |
394 | G.Mk = t.NewMkLines("Makefile", | 394 | G.Mk = t.NewMkLines("Makefile", | |
395 | MkRcsID, | 395 | MkRcsID, | |
396 | "GENERATE_PLIST= cd ${DESTDIR}${PREFIX}; ${FIND} * \\( -type f -or -type l \\) | ${SORT};") | 396 | "GENERATE_PLIST= cd ${DESTDIR}${PREFIX}; ${FIND} * \\( -type f -or -type l \\) | ${SORT};") | |
397 | 397 | |||
398 | G.Mk.DetermineDefinedVariables() | 398 | G.Mk.collectDefinedVariables() | |
399 | MkLineChecker{G.Mk.mklines[1]}.Check() | 399 | MkLineChecker{G.Mk.mklines[1]}.Check() | |
400 | 400 | |||
401 | t.CheckOutputLines( | 401 | t.CheckOutputLines( | |
402 | "WARN: Makefile:2: The exitcode of \"${FIND}\" at the left of the | operator is ignored.") | 402 | "WARN: Makefile:2: The exitcode of \"${FIND}\" at the left of the | operator is ignored.") | |
403 | } | 403 | } | |
404 | 404 | |||
405 | func (s *Suite) Test_MkLine_VariableNeedsQuoting__word_as_part_of_word(c *check.C) { | 405 | func (s *Suite) Test_MkLine_VariableNeedsQuoting__word_as_part_of_word(c *check.C) { | |
406 | t := s.Init(c) | 406 | t := s.Init(c) | |
407 | 407 | |||
408 | t.SetupVartypes() | 408 | t.SetupVartypes() | |
409 | G.Mk = t.NewMkLines("Makefile", | 409 | G.Mk = t.NewMkLines("Makefile", | |
410 | MkRcsID, | 410 | MkRcsID, | |
411 | "EGDIR=\t${EGDIR}/${MACHINE_GNU_PLATFORM}") | 411 | "EGDIR=\t${EGDIR}/${MACHINE_GNU_PLATFORM}") | |
@@ -906,27 +906,27 @@ func (s *Suite) Test_MkLine_ValueTokens_ | @@ -906,27 +906,27 @@ func (s *Suite) Test_MkLine_ValueTokens_ | |||
906 | 906 | |||
907 | func (s *Suite) Test_MkLine_ResolveVarsInRelativePath(c *check.C) { | 907 | func (s *Suite) Test_MkLine_ResolveVarsInRelativePath(c *check.C) { | |
908 | t := s.Init(c) | 908 | t := s.Init(c) | |
909 | 909 | |||
910 | t.CreateFileLines("lang/lua53/Makefile") | 910 | t.CreateFileLines("lang/lua53/Makefile") | |
911 | t.CreateFileLines("lang/php72/Makefile") | 911 | t.CreateFileLines("lang/php72/Makefile") | |
912 | t.CreateFileLines("emulators/suse100_base/Makefile") | 912 | t.CreateFileLines("emulators/suse100_base/Makefile") | |
913 | t.CreateFileLines("lang/python36/Makefile") | 913 | t.CreateFileLines("lang/python36/Makefile") | |
914 | mklines := t.SetupFileMkLines("Makefile", | 914 | mklines := t.SetupFileMkLines("Makefile", | |
915 | MkRcsID) | 915 | MkRcsID) | |
916 | mkline := mklines.mklines[0] | 916 | mkline := mklines.mklines[0] | |
917 | 917 | |||
918 | checkResolve := func(before string, after string) { | 918 | checkResolve := func(before string, after string) { | |
919 | c.Check(mkline.ResolveVarsInRelativePath(before, false), equals, after) | 919 | c.Check(mkline.ResolveVarsInRelativePath(before), equals, after) | |
920 | } | 920 | } | |
921 | 921 | |||
922 | checkResolve("", ".") | 922 | checkResolve("", ".") | |
923 | checkResolve("${LUA_PKGSRCDIR}", "../../lang/lua53") | 923 | checkResolve("${LUA_PKGSRCDIR}", "../../lang/lua53") | |
924 | checkResolve("${PHPPKGSRCDIR}", "../../lang/php72") | 924 | checkResolve("${PHPPKGSRCDIR}", "../../lang/php72") | |
925 | checkResolve("${SUSE_DIR_PREFIX}", "suse100") | 925 | checkResolve("${SUSE_DIR_PREFIX}", "suse100") | |
926 | checkResolve("${PYPKGSRCDIR}", "../../lang/python36") | 926 | checkResolve("${PYPKGSRCDIR}", "../../lang/python36") | |
927 | checkResolve("${PYPACKAGE}", "python36") | 927 | checkResolve("${PYPACKAGE}", "python36") | |
928 | checkResolve("${FILESDIR}", "${FILESDIR}") | 928 | checkResolve("${FILESDIR}", "${FILESDIR}") | |
929 | checkResolve("${PKGDIR}", "${PKGDIR}") | 929 | checkResolve("${PKGDIR}", "${PKGDIR}") | |
930 | 930 | |||
931 | G.Pkg = NewPackage(t.File("category/package")) | 931 | G.Pkg = NewPackage(t.File("category/package")) | |
932 | 932 |
@@ -152,27 +152,27 @@ func (ck MkLineChecker) checkDirective(f | @@ -152,27 +152,27 @@ func (ck MkLineChecker) checkDirective(f | |||
152 | } | 152 | } | |
153 | 153 | |||
154 | case directive == "if" || directive == "elif": | 154 | case directive == "if" || directive == "elif": | |
155 | ck.checkDirectiveCond() | 155 | ck.checkDirectiveCond() | |
156 | 156 | |||
157 | case directive == "ifdef" || directive == "ifndef": | 157 | case directive == "ifdef" || directive == "ifndef": | |
158 | mkline.Warnf("The \".%s\" directive is deprecated. Please use \".if %sdefined(%s)\" instead.", | 158 | mkline.Warnf("The \".%s\" directive is deprecated. Please use \".if %sdefined(%s)\" instead.", | |
159 | directive, ifelseStr(directive == "ifdef", "", "!"), args) | 159 | directive, ifelseStr(directive == "ifdef", "", "!"), args) | |
160 | 160 | |||
161 | case directive == "for": | 161 | case directive == "for": | |
162 | ck.checkDirectiveFor(forVars, ind) | 162 | ck.checkDirectiveFor(forVars, ind) | |
163 | 163 | |||
164 | case directive == "undef": | 164 | case directive == "undef": | |
165 | for _, varname := range fields(args) { | 165 | for _, varname := range mkline.Fields() { | |
166 | if forVars[varname] { | 166 | if forVars[varname] { | |
167 | mkline.Notef("Using \".undef\" after a \".for\" loop is unnecessary.") | 167 | mkline.Notef("Using \".undef\" after a \".for\" loop is unnecessary.") | |
168 | } | 168 | } | |
169 | } | 169 | } | |
170 | } | 170 | } | |
171 | } | 171 | } | |
172 | 172 | |||
173 | func (ck MkLineChecker) checkDirectiveEnd(ind *Indentation) { | 173 | func (ck MkLineChecker) checkDirectiveEnd(ind *Indentation) { | |
174 | mkline := ck.MkLine | 174 | mkline := ck.MkLine | |
175 | directive := mkline.Directive() | 175 | directive := mkline.Directive() | |
176 | comment := mkline.DirectiveComment() | 176 | comment := mkline.DirectiveComment() | |
177 | 177 | |||
178 | if directive == "endif" && comment != "" { | 178 | if directive == "endif" && comment != "" { | |
@@ -193,28 +193,28 @@ func (ck MkLineChecker) checkDirectiveEn | @@ -193,28 +193,28 @@ func (ck MkLineChecker) checkDirectiveEn | |||
193 | func (ck MkLineChecker) checkDirectiveFor(forVars map[string]bool, indentation *Indentation) { | 193 | func (ck MkLineChecker) checkDirectiveFor(forVars map[string]bool, indentation *Indentation) { | |
194 | mkline := ck.MkLine | 194 | mkline := ck.MkLine | |
195 | args := mkline.Args() | 195 | args := mkline.Args() | |
196 | 196 | |||
197 | if m, vars, _ := match2(args, `^([^\t ]+(?:[\t ]*[^\t ]+)*?)[\t ]+in[\t ]+(.*)$`); m { | 197 | if m, vars, _ := match2(args, `^([^\t ]+(?:[\t ]*[^\t ]+)*?)[\t ]+in[\t ]+(.*)$`); m { | |
198 | for _, forvar := range fields(vars) { | 198 | for _, forvar := range fields(vars) { | |
199 | indentation.AddVar(forvar) | 199 | indentation.AddVar(forvar) | |
200 | if !G.Infrastructure && hasPrefix(forvar, "_") { | 200 | if !G.Infrastructure && hasPrefix(forvar, "_") { | |
201 | mkline.Warnf("Variable names starting with an underscore (%s) are reserved for internal pkgsrc use.", forvar) | 201 | mkline.Warnf("Variable names starting with an underscore (%s) are reserved for internal pkgsrc use.", forvar) | |
202 | } | 202 | } | |
203 | 203 | |||
204 | if matches(forvar, `^[_a-z][_a-z0-9]*$`) { | 204 | if matches(forvar, `^[_a-z][_a-z0-9]*$`) { | |
205 | // Fine. | 205 | // Fine. | |
206 | } else if strings.IndexFunc(forvar, func(r rune) bool { return 'A' <= r && r <= 'Z' }) != -1 { | 206 | } else if matches(forvar, `^[A-Z_a-z][0-9A-Z_a-z]*$`) { | |
207 | mkline.Warnf(".for variable names should not contain uppercase letters.") | 207 | mkline.Warnf("The variable name %q in the .for loop should not contain uppercase letters.", forvar) | |
208 | } else { | 208 | } else { | |
209 | mkline.Errorf("Invalid variable name %q.", forvar) | 209 | mkline.Errorf("Invalid variable name %q.", forvar) | |
210 | } | 210 | } | |
211 | 211 | |||
212 | forVars[forvar] = true | 212 | forVars[forvar] = true | |
213 | } | 213 | } | |
214 | 214 | |||
215 | // XXX: The type BtUnknown is very unspecific here. For known variables | 215 | // XXX: The type BtUnknown is very unspecific here. For known variables | |
216 | // or constant values this could probably be improved. | 216 | // or constant values this could probably be improved. | |
217 | // | 217 | // | |
218 | // The guessed flag could also be determined more correctly. As of November 2018, | 218 | // The guessed flag could also be determined more correctly. As of November 2018, | |
219 | // running pkglint over the whole pkgsrc tree did not produce any different result | 219 | // running pkglint over the whole pkgsrc tree did not produce any different result | |
220 | // whether guessed was true or false, so currently it is not worth investing | 220 | // whether guessed was true or false, so currently it is not worth investing | |
@@ -754,46 +754,46 @@ func (ck MkLineChecker) checkVaruseDepre | @@ -754,46 +754,46 @@ func (ck MkLineChecker) checkVaruseDepre | |||
754 | return | 754 | return | |
755 | } | 755 | } | |
756 | 756 | |||
757 | varname := varuse.varname | 757 | varname := varuse.varname | |
758 | instead := G.Pkgsrc.Deprecated[varname] | 758 | instead := G.Pkgsrc.Deprecated[varname] | |
759 | if instead == "" { | 759 | if instead == "" { | |
760 | instead = G.Pkgsrc.Deprecated[varnameCanon(varname)] | 760 | instead = G.Pkgsrc.Deprecated[varnameCanon(varname)] | |
761 | } | 761 | } | |
762 | if instead != "" { | 762 | if instead != "" { | |
763 | ck.MkLine.Warnf("Use of %q is deprecated. %s", varname, instead) | 763 | ck.MkLine.Warnf("Use of %q is deprecated. %s", varname, instead) | |
764 | } | 764 | } | |
765 | } | 765 | } | |
766 | 766 | |||
767 | func (ck MkLineChecker) checkVarassignDecreasingVersions(varname, value string) { | 767 | func (ck MkLineChecker) checkVarassignDecreasingVersions() { | |
768 | if trace.Tracing { | 768 | if trace.Tracing { | |
769 | defer trace.Call2(varname, value)() | 769 | defer trace.Call0()() | |
770 | } | 770 | } | |
771 | 771 | |||
772 | mkline := ck.MkLine | 772 | mkline := ck.MkLine | |
773 | strVersions := fields(value) | 773 | strVersions := mkline.Fields() | |
774 | intVersions := make([]int, len(strVersions)) | 774 | intVersions := make([]int, len(strVersions)) | |
775 | for i, strVersion := range strVersions { | 775 | for i, strVersion := range strVersions { | |
776 | iver, err := strconv.Atoi(strVersion) | 776 | iver, err := strconv.Atoi(strVersion) | |
777 | if err != nil || !(iver > 0) { | 777 | if err != nil || !(iver > 0) { | |
778 | mkline.Errorf("All values for %s must be positive integers.", varname) | 778 | mkline.Errorf("All values for %s must be positive integers.", mkline.Varname()) | |
779 | return | 779 | return | |
780 | } | 780 | } | |
781 | intVersions[i] = iver | 781 | intVersions[i] = iver | |
782 | } | 782 | } | |
783 | 783 | |||
784 | for i, ver := range intVersions { | 784 | for i, ver := range intVersions { | |
785 | if i > 0 && ver >= intVersions[i-1] { | 785 | if i > 0 && ver >= intVersions[i-1] { | |
786 | mkline.Warnf("The values for %s should be in decreasing order.", varname) | 786 | mkline.Warnf("The values for %s should be in decreasing order.", mkline.Varname()) | |
787 | G.Explain( | 787 | G.Explain( | |
788 | "If they aren't, it may be possible that needless versions of", | 788 | "If they aren't, it may be possible that needless versions of", | |
789 | "packages are installed.") | 789 | "packages are installed.") | |
790 | } | 790 | } | |
791 | } | 791 | } | |
792 | } | 792 | } | |
793 | 793 | |||
794 | func (ck MkLineChecker) checkVarassign() { | 794 | func (ck MkLineChecker) checkVarassign() { | |
795 | mkline := ck.MkLine | 795 | mkline := ck.MkLine | |
796 | varname := mkline.Varname() | 796 | varname := mkline.Varname() | |
797 | op := mkline.Op() | 797 | op := mkline.Op() | |
798 | value := mkline.Value() | 798 | value := mkline.Value() | |
799 | comment := mkline.VarassignComment() | 799 | comment := mkline.VarassignComment() | |
@@ -935,54 +935,54 @@ func (ck MkLineChecker) checkVarassignSp | @@ -935,54 +935,54 @@ func (ck MkLineChecker) checkVarassignSp | |||
935 | mkline := ck.MkLine | 935 | mkline := ck.MkLine | |
936 | varname := mkline.Varname() | 936 | varname := mkline.Varname() | |
937 | value := mkline.Value() | 937 | value := mkline.Value() | |
938 | 938 | |||
939 | if contains(value, "/etc/rc.d") && mkline.Varname() != "RPMIGNOREPATH" { | 939 | if contains(value, "/etc/rc.d") && mkline.Varname() != "RPMIGNOREPATH" { | |
940 | mkline.Warnf("Please use the RCD_SCRIPTS mechanism to install rc.d scripts automatically to ${RCD_SCRIPTS_EXAMPLEDIR}.") | 940 | mkline.Warnf("Please use the RCD_SCRIPTS mechanism to install rc.d scripts automatically to ${RCD_SCRIPTS_EXAMPLEDIR}.") | |
941 | } | 941 | } | |
942 | 942 | |||
943 | if hasPrefix(varname, "_") && !G.Infrastructure { | 943 | if hasPrefix(varname, "_") && !G.Infrastructure { | |
944 | mkline.Warnf("Variable names starting with an underscore (%s) are reserved for internal pkgsrc use.", varname) | 944 | mkline.Warnf("Variable names starting with an underscore (%s) are reserved for internal pkgsrc use.", varname) | |
945 | } | 945 | } | |
946 | 946 | |||
947 | if varname == "PYTHON_VERSIONS_ACCEPTED" { | 947 | if varname == "PYTHON_VERSIONS_ACCEPTED" { | |
948 | ck.checkVarassignDecreasingVersions(varname, value) | 948 | ck.checkVarassignDecreasingVersions() | |
949 | } | 949 | } | |
950 | 950 | |||
951 | if mkline.VarassignComment() == "# defined" && !hasSuffix(varname, "_MK") && !hasSuffix(varname, "_COMMON") { | 951 | if mkline.VarassignComment() == "# defined" && !hasSuffix(varname, "_MK") && !hasSuffix(varname, "_COMMON") { | |
952 | mkline.Notef("Please use \"# empty\", \"# none\" or \"# yes\" instead of \"# defined\".") | 952 | mkline.Notef("Please use \"# empty\", \"# none\" or \"# yes\" instead of \"# defined\".") | |
953 | G.Explain( | 953 | G.Explain( | |
954 | "The value #defined says something about the state of the variable,", | 954 | "The value #defined says something about the state of the variable,", | |
955 | "but not what that _means_. In some cases a variable that is defined", | 955 | "but not what that _means_. In some cases a variable that is defined", | |
956 | "means \"yes\", in other cases it is an empty list (which is also", | 956 | "means \"yes\", in other cases it is an empty list (which is also", | |
957 | "only the state of the variable), whose meaning could be described", | 957 | "only the state of the variable), whose meaning could be described", | |
958 | "with \"none\". It is this meaning that should be described.") | 958 | "with \"none\". It is this meaning that should be described.") | |
959 | } | 959 | } | |
960 | 960 | |||
961 | if varname == "DIST_SUBDIR" || varname == "WRKSRC" { | 961 | if varname == "DIST_SUBDIR" || varname == "WRKSRC" { | |
962 | if m, revVarname := match1(value, `\$\{(PKGNAME|PKGVERSION)[:\}]`); m { | 962 | if m, revVarname := match1(value, `\$\{(PKGNAME|PKGVERSION)[:\}]`); m { | |
963 | mkline.Warnf("%s should not be used in %s as it includes the PKGREVISION. "+ | 963 | mkline.Warnf("%s should not be used in %s as it includes the PKGREVISION. "+ | |
964 | "Please use %[1]s_NOREV instead.", revVarname, varname) | 964 | "Please use %[1]s_NOREV instead.", revVarname, varname) | |
965 | } | 965 | } | |
966 | } | 966 | } | |
967 | 967 | |||
968 | if hasPrefix(varname, "SITES_") { | 968 | if hasPrefix(varname, "SITES_") { | |
969 | mkline.Warnf("SITES_* is deprecated. Please use SITES.* instead.") | 969 | mkline.Warnf("SITES_* is deprecated. Please use SITES.* instead.") | |
970 | // No autofix since it doesn't occur anymore. | 970 | // No autofix since it doesn't occur anymore. | |
971 | } | 971 | } | |
972 | 972 | |||
973 | if varname == "PKG_SKIP_REASON" && G.Mk.indentation.DependsOn("OPSYS") { | 973 | if varname == "PKG_SKIP_REASON" && G.Mk.indentation.DependsOn("OPSYS") { | |
974 | mkline.Notef("Consider defining NOT_FOR_PLATFORM instead of " + | 974 | mkline.Notef("Consider setting NOT_FOR_PLATFORM instead of " + | |
975 | "setting PKG_SKIP_REASON depending on ${OPSYS}.") | 975 | "PKG_SKIP_REASON depending on ${OPSYS}.") | |
976 | } | 976 | } | |
977 | } | 977 | } | |
978 | 978 | |||
979 | func (ck MkLineChecker) checkVarassignBsdPrefs() { | 979 | func (ck MkLineChecker) checkVarassignBsdPrefs() { | |
980 | mkline := ck.MkLine | 980 | mkline := ck.MkLine | |
981 | 981 | |||
982 | switch mkline.Varcanon() { | 982 | switch mkline.Varcanon() { | |
983 | case "BUILDLINK_PKGSRCDIR.*", | 983 | case "BUILDLINK_PKGSRCDIR.*", | |
984 | "BUILDLINK_DEPMETHOD.*", | 984 | "BUILDLINK_DEPMETHOD.*", | |
985 | "BUILDLINK_ABI_DEPENDS.*", | 985 | "BUILDLINK_ABI_DEPENDS.*", | |
986 | "BUILDLINK_INCDIRS.*", | 986 | "BUILDLINK_INCDIRS.*", | |
987 | "BUILDLINK_LIBDIRS.*": | 987 | "BUILDLINK_LIBDIRS.*": | |
988 | return | 988 | return | |
@@ -1141,150 +1141,179 @@ func (ck MkLineChecker) checkTextDepreca | @@ -1141,150 +1141,179 @@ func (ck MkLineChecker) checkTextDepreca | |||
1141 | func (ck MkLineChecker) checkDirectiveCond() { | 1141 | func (ck MkLineChecker) checkDirectiveCond() { | |
1142 | mkline := ck.MkLine | 1142 | mkline := ck.MkLine | |
1143 | if trace.Tracing { | 1143 | if trace.Tracing { | |
1144 | defer trace.Call1(mkline.Args())() | 1144 | defer trace.Call1(mkline.Args())() | |
1145 | } | 1145 | } | |
1146 | 1146 | |||
1147 | p := NewMkParser(nil, mkline.Args(), false) // No emitWarnings here, see the code below. | 1147 | p := NewMkParser(nil, mkline.Args(), false) // No emitWarnings here, see the code below. | |
1148 | cond := p.MkCond() | 1148 | cond := p.MkCond() | |
1149 | if !p.EOF() { | 1149 | if !p.EOF() { | |
1150 | mkline.Warnf("Invalid condition, unrecognized part: %q.", p.Rest()) | 1150 | mkline.Warnf("Invalid condition, unrecognized part: %q.", p.Rest()) | |
1151 | return | 1151 | return | |
1152 | } | 1152 | } | |
1153 | 1153 | |||
1154 | checkEmpty := func(varuse *MkVarUse) { | |||
1155 | varname := varuse.varname | |||
1156 | if matches(varname, `^\$.*:[MN]`) { | |||
1157 | mkline.Warnf("The empty() function takes a variable name as parameter, not a variable expression.") | |||
1158 | G.Explain( | |||
1159 | "Instead of empty(${VARNAME:Mpattern}), you should write either", | |||
1160 | "of the following:", | |||
1161 | "", | |||
1162 | "\tempty(VARNAME:Mpattern)", | |||
1163 | "\t${VARNAME:Mpattern} == \"\"", | |||
1164 | "", | |||
1165 | "Instead of !empty(${VARNAME:Mpattern}), you should write either", | |||
1166 | "of the following:", | |||
1167 | "", | |||
1168 | "\t!empty(VARNAME:Mpattern)", | |||
1169 | "\t${VARNAME:Mpattern}") | |||
1170 | } | |||
1171 | ||||
1172 | modifiers := varuse.modifiers | |||
1173 | for _, modifier := range modifiers { | |||
1174 | if m, positive, pattern := modifier.MatchMatch(); m && (positive || len(modifiers) == 1) { | |||
1175 | ck.checkVartype(varname, opUseMatch, pattern, "") | |||
1176 | ||||
1177 | vartype := G.Pkgsrc.VariableType(varname) | |||
1178 | if matches(pattern, `^[\w-/]+$`) && vartype != nil && !vartype.IsConsideredList() { | |||
1179 | mkline.Notef("%s should be compared using == instead of the :M or :N modifier without wildcards.", varname) | |||
1180 | G.Explain( | |||
1181 | "This variable has a single value, not a list of values. Therefore", | |||
1182 | "it feels strange to apply list operators like :M and :N onto it.", | |||
1183 | "A more direct approach is to use the == and != operators.", | |||
1184 | "", | |||
1185 | "An entirely different case is when the pattern contains wildcards", | |||
1186 | "like ^, *, $. In such a case, using the :M or :N modifiers is", | |||
1187 | "useful and preferred.") | |||
1188 | } | |||
1189 | } | |||
1190 | } | |||
1191 | } | |||
1192 | ||||
1193 | checkCompareVarStr := func(varuse *MkVarUse, op string, value string) { | 1154 | checkCompareVarStr := func(varuse *MkVarUse, op string, value string) { | |
1194 | varname := varuse.varname | 1155 | varname := varuse.varname | |
1195 | varmods := varuse.modifiers | 1156 | varmods := varuse.modifiers | |
1196 | if len(varmods) == 0 { | 1157 | switch len(varmods) { | |
1158 | case 0: | |||
1197 | ck.checkCompareVarStr(varname, op, value) | 1159 | ck.checkCompareVarStr(varname, op, value) | |
1198 | } else if len(varmods) == 1 { | 1160 | ||
1161 | case 1: | |||
1199 | if m, _, _ := varmods[0].MatchMatch(); m && value != "" { | 1162 | if m, _, _ := varmods[0].MatchMatch(); m && value != "" { | |
1200 | ck.checkVartype(varname, opUseMatch, value, "") | 1163 | ck.checkVartype(varname, opUseMatch, value, "") | |
1201 | } | 1164 | } | |
1165 | ||||
1166 | default: | |||
1167 | // This case covers ${VAR:Mfilter:O:u} or similar uses in conditions. | |||
1168 | // To check these properly, pkglint first needs to know the most common modifiers and how they interact. | |||
1169 | // As of November 2018, the modifiers are not modeled. | |||
1170 | // The following tracing statement makes it easy to discover these cases, | |||
1171 | // in order to decide whether checking them is worthwhile. | |||
1172 | if trace.Tracing { | |||
1173 | trace.Stepf("checkCompareVarStr ${%s%s} %s %s", varuse.varname, varuse.Mod(), op, value) | |||
1174 | } | |||
1202 | } | 1175 | } | |
1203 | } | 1176 | } | |
1204 | 1177 | |||
1205 | checkVarUse := func(varuse *MkVarUse) { | 1178 | checkVarUse := func(varuse *MkVarUse) { | |
1206 | var vartype *Vartype // TODO: Insert a better type guess here. | 1179 | var vartype *Vartype // TODO: Insert a better type guess here. | |
1207 | vuc := VarUseContext{vartype, vucTimeParse, vucQuotPlain, false} | 1180 | vuc := VarUseContext{vartype, vucTimeParse, vucQuotPlain, false} | |
1208 | ck.CheckVaruse(varuse, &vuc) | 1181 | ck.CheckVaruse(varuse, &vuc) | |
1209 | } | 1182 | } | |
1210 | 1183 | |||
1211 | cond.Walk(&MkCondCallback{ | 1184 | cond.Walk(&MkCondCallback{ | |
1212 | Empty: checkEmpty, | 1185 | Empty: ck.checkDirectiveCondEmpty, | |
1213 | CompareVarStr: checkCompareVarStr, | 1186 | CompareVarStr: checkCompareVarStr, | |
1214 | VarUse: checkVarUse}) | 1187 | VarUse: checkVarUse}) | |
1215 | } | 1188 | } | |
1216 | 1189 | |||
1190 | // checkDirectiveCondEmpty checks a condition of the form empty(...) in an .if directive. | |||
1191 | func (ck MkLineChecker) checkDirectiveCondEmpty(varuse *MkVarUse) { | |||
1192 | varname := varuse.varname | |||
1193 | if matches(varname, `^\$.*:[MN]`) { | |||
1194 | ck.MkLine.Warnf("The empty() function takes a variable name as parameter, not a variable expression.") | |||
1195 | G.Explain( | |||
1196 | "Instead of empty(${VARNAME:Mpattern}), you should write either", | |||
1197 | "of the following:", | |||
1198 | "", | |||
1199 | "\tempty(VARNAME:Mpattern)", | |||
1200 | "\t${VARNAME:Mpattern} == \"\"", | |||
1201 | "", | |||
1202 | "Instead of !empty(${VARNAME:Mpattern}), you should write either", | |||
1203 | "of the following:", | |||
1204 | "", | |||
1205 | "\t!empty(VARNAME:Mpattern)", | |||
1206 | "\t${VARNAME:Mpattern}") | |||
1207 | } | |||
1208 | ||||
1209 | modifiers := varuse.modifiers | |||
1210 | for _, modifier := range modifiers { | |||
1211 | if m, positive, pattern := modifier.MatchMatch(); m && (positive || len(modifiers) == 1) { | |||
1212 | ck.checkVartype(varname, opUseMatch, pattern, "") | |||
1213 | ||||
1214 | vartype := G.Pkgsrc.VariableType(varname) | |||
1215 | if matches(pattern, `^[\w-/]+$`) && vartype != nil && !vartype.IsConsideredList() { | |||
1216 | ck.MkLine.Notef("%s should be compared using %s instead of matching against %q.", | |||
1217 | varname, ifelseStr(positive, "==", "!="), ":"+modifier.Text) | |||
1218 | G.Explain( | |||
1219 | "This variable has a single value, not a list of values.", | |||
1220 | "Therefore it feels strange to apply list operators like :M and :N onto it.", | |||
1221 | "A more direct approach is to use the == and != operators.", | |||
1222 | "", | |||
1223 | "An entirely different case is when the pattern contains wildcards like ^, *, $.", | |||
1224 | "In such a case, using the :M or :N modifiers is useful and preferred.") | |||
1225 | } | |||
1226 | } | |||
1227 | } | |||
1228 | ||||
1229 | } | |||
1230 | ||||
1217 | func (ck MkLineChecker) checkCompareVarStr(varname, op, value string) { | 1231 | func (ck MkLineChecker) checkCompareVarStr(varname, op, value string) { | |
1218 | ck.checkVartype(varname, opUseCompare, value, "") | 1232 | ck.checkVartype(varname, opUseCompare, value, "") | |
1219 | 1233 | |||
1220 | if varname == "PKGSRC_COMPILER" { | 1234 | if varname == "PKGSRC_COMPILER" { | |
1221 | ck.MkLine.Warnf("Use ${PKGSRC_COMPILER:%s%s} instead of the %s operator.", ifelseStr(op == "==", "M", "N"), value, op) | 1235 | ck.MkLine.Warnf("Use ${PKGSRC_COMPILER:%s%s} instead of the %s operator.", ifelseStr(op == "==", "M", "N"), value, op) | |
1222 | G.Explain( | 1236 | G.Explain( | |
1223 | "The PKGSRC_COMPILER can be a list of chained compilers, e.g. \"ccache", | 1237 | "The PKGSRC_COMPILER can be a list of chained compilers, e.g. \"ccache", | |
1224 | "distcc clang\". Therefore, comparing it using == or != leads to", | 1238 | "distcc clang\". Therefore, comparing it using == or != leads to", | |
1225 | "wrong results in these cases.") | 1239 | "wrong results in these cases.") | |
1226 | } | 1240 | } | |
1227 | } | 1241 | } | |
1228 | 1242 | |||
1243 | // CheckRelativePkgdir checks a reference from one pkgsrc package to another. | |||
1244 | // These references should always have the form ../../category/package. | |||
1245 | // | |||
1246 | // When used in DEPENDS or similar variables, these directories could theoretically | |||
1247 | // also be relative to the pkgsrc root, which would save a few keystrokes. | |||
1248 | // This, however, is not implemented in pkgsrc and suggestions regarding this topic | |||
1249 | // have not been made in the last two decades on the public mailing lists. | |||
1250 | // While being a bit redundant, the current scheme works well. | |||
1251 | // | |||
1252 | // When used in .include directives, the relative package directories must be written | |||
1253 | // with the leading ../.. anyway, so the benefit might not be too big at all. | |||
1229 | func (ck MkLineChecker) CheckRelativePkgdir(pkgdir string) { | 1254 | func (ck MkLineChecker) CheckRelativePkgdir(pkgdir string) { | |
1230 | if trace.Tracing { | 1255 | if trace.Tracing { | |
1231 | defer trace.Call1(pkgdir)() | 1256 | defer trace.Call1(pkgdir)() | |
1232 | } | 1257 | } | |
1233 | 1258 | |||
1234 | mkline := ck.MkLine | 1259 | mkline := ck.MkLine | |
1235 | ck.CheckRelativePath(pkgdir, true) | 1260 | ck.CheckRelativePath(pkgdir, true) | |
1236 | pkgdir = mkline.ResolveVarsInRelativePath(pkgdir, false) | 1261 | pkgdir = mkline.ResolveVarsInRelativePath(pkgdir) | |
1237 | 1262 | |||
1263 | // XXX: Is the leading "./" realistic? | |||
1238 | if m, otherpkgpath := match1(pkgdir, `^(?:\./)?\.\./\.\./([^/]+/[^/]+)$`); m { | 1264 | if m, otherpkgpath := match1(pkgdir, `^(?:\./)?\.\./\.\./([^/]+/[^/]+)$`); m { | |
1239 | if !fileExists(G.Pkgsrc.File(otherpkgpath + "/Makefile")) { | 1265 | if !fileExists(G.Pkgsrc.File(otherpkgpath + "/Makefile")) { | |
1240 | mkline.Errorf("There is no package in %q.", otherpkgpath) | 1266 | mkline.Errorf("There is no package in %q.", otherpkgpath) | |
1241 | } | 1267 | } | |
1242 | 1268 | |||
1243 | } else if !containsVarRef(pkgdir) { | 1269 | } else if !containsVarRef(pkgdir) { | |
1244 | mkline.Warnf("%q is not a valid relative package directory.", pkgdir) | 1270 | mkline.Warnf("%q is not a valid relative package directory.", pkgdir) | |
1245 | G.Explain( | 1271 | G.Explain( | |
1246 | "A relative pathname always starts with \"../../\", followed", | 1272 | "A relative pathname always starts with \"../../\", followed", | |
1247 | "by a category, a slash and a the directory name of the package.", | 1273 | "by a category, a slash and a the directory name of the package.", | |
1248 | "For example, \"../../misc/screen\" is a valid relative pathname.") | 1274 | "For example, \"../../misc/screen\" is a valid relative pathname.") | |
1249 | } | 1275 | } | |
1250 | } | 1276 | } | |
1251 | 1277 | |||
1278 | // CheckRelativePath checks a relative path that leads to the directory of another package | |||
1279 | // or to a subdirectory thereof or a file within there. | |||
1252 | func (ck MkLineChecker) CheckRelativePath(relativePath string, mustExist bool) { | 1280 | func (ck MkLineChecker) CheckRelativePath(relativePath string, mustExist bool) { | |
1253 | if trace.Tracing { | 1281 | if trace.Tracing { | |
1254 | defer trace.Call(relativePath, mustExist)() | 1282 | defer trace.Call(relativePath, mustExist)() | |
1255 | } | 1283 | } | |
1256 | 1284 | |||
1257 | mkline := ck.MkLine | 1285 | mkline := ck.MkLine | |
1258 | if !G.Wip && contains(relativePath, "/wip/") { | 1286 | if !G.Wip && contains(relativePath, "/wip/") { | |
1259 | mkline.Errorf("A main pkgsrc package must not depend on a pkgsrc-wip package.") | 1287 | mkline.Errorf("A main pkgsrc package must not depend on a pkgsrc-wip package.") | |
1260 | } | 1288 | } | |
1261 | 1289 | |||
1262 | resolvedPath := mkline.ResolveVarsInRelativePath(relativePath, true) | 1290 | resolvedPath := mkline.ResolveVarsInRelativePath(relativePath) | |
1263 | if containsVarRef(resolvedPath) { | 1291 | if containsVarRef(resolvedPath) { | |
1264 | return | 1292 | return | |
1265 | } | 1293 | } | |
1266 | 1294 | |||
1267 | abs := resolvedPath | 1295 | abs := resolvedPath | |
1268 | if !hasPrefix(abs, "/") { | 1296 | if !hasPrefix(abs, "/") { | |
1269 | abs = path.Dir(mkline.Filename) + "/" + abs | 1297 | abs = path.Dir(mkline.Filename) + "/" + abs | |
1270 | } | 1298 | } | |
1271 | if _, err := os.Stat(abs); err != nil { | 1299 | if _, err := os.Stat(abs); err != nil { | |
1272 | if mustExist { | 1300 | if mustExist { | |
1273 | mkline.Errorf("%q does not exist.", resolvedPath) | 1301 | mkline.Errorf("Relative path %q does not exist.", resolvedPath) | |
1274 | } | 1302 | } | |
1275 | return | 1303 | return | |
1276 | } | 1304 | } | |
1277 | 1305 | |||
1278 | switch { | 1306 | switch { | |
1279 | case !hasPrefix(relativePath, "../"): | 1307 | case !hasPrefix(relativePath, "../"): | |
1280 | break | 1308 | break | |
1281 | case hasPrefix(relativePath, "../../mk/"): | 1309 | case hasPrefix(relativePath, "../../mk/"): | |
1282 | // From a package to the infrastructure. | 1310 | // From a package to the infrastructure. | |
1283 | case matches(relativePath, `^\.\./\.\./[^/]+/[^/]`): | 1311 | case matches(relativePath, `^\.\./\.\./[^/]+/[^/]`): | |
1284 | // From a package to another package. | 1312 | // From a package to another package. | |
1285 | case hasPrefix(relativePath, "../mk/") && relpath(path.Dir(mkline.Filename), G.Pkgsrc.File(".")) == "..": | 1313 | case hasPrefix(relativePath, "../mk/") && relpath(path.Dir(mkline.Filename), G.Pkgsrc.File(".")) == "..": | |
1286 | // For category Makefiles. | 1314 | // For category Makefiles. | |
1287 | default: | 1315 | default: | |
1288 | mkline.Warnf("Invalid relative path %q.", relativePath) | 1316 | mkline.Warnf("Invalid relative path %q.", relativePath) | |
1317 | // TODO: Explain this warning. | |||
1289 | } | 1318 | } | |
1290 | } | 1319 | } |
@@ -13,119 +13,164 @@ func (s *Suite) Test_MkLineChecker_Check | @@ -13,119 +13,164 @@ func (s *Suite) Test_MkLineChecker_Check | |||
13 | 13 | |||
14 | t.CheckOutputLines( | 14 | t.CheckOutputLines( | |
15 | "ERROR: filename.mk:1: This comment indicates unfinished work (url2pkg).") | 15 | "ERROR: filename.mk:1: This comment indicates unfinished work (url2pkg).") | |
16 | } | 16 | } | |
17 | 17 | |||
18 | func (s *Suite) Test_MkLineChecker_Check__buildlink3_include_prefs(c *check.C) { | 18 | func (s *Suite) Test_MkLineChecker_Check__buildlink3_include_prefs(c *check.C) { | |
19 | t := s.Init(c) | 19 | t := s.Init(c) | |
20 | 20 | |||
21 | t.SetupVartypes() | 21 | t.SetupVartypes() | |
22 | 22 | |||
23 | t.CreateFileLines("mk/bsd.prefs.mk") | 23 | t.CreateFileLines("mk/bsd.prefs.mk") | |
24 | mklines := t.SetupFileMkLines("category/package/buildlink3.mk", | 24 | mklines := t.SetupFileMkLines("category/package/buildlink3.mk", | |
25 | ".include \"../../mk/bsd.prefs.mk\"") | 25 | ".include \"../../mk/bsd.prefs.mk\"") | |
26 | // If the buildlink3.mk file is not actually created, resolving the | 26 | // If the buildlink3.mk file doesn't actually exist, resolving the | |
27 | // relative path fails since that depends on the actual file system, | 27 | // relative path fails since that depends on the actual file system, | |
28 | // not on syntactical paths; see os.Stat in CheckRelativePath. | 28 | // not on syntactical paths; see os.Stat in CheckRelativePath. | |
29 | // | |||
30 | // TODO: Refactor relpath to be independent of a filesystem. | |||
29 | 31 | |||
30 | MkLineChecker{mklines.mklines[0]}.Check() | 32 | MkLineChecker{mklines.mklines[0]}.Check() | |
31 | 33 | |||
32 | t.CheckOutputLines( | 34 | t.CheckOutputLines( | |
33 | "NOTE: ~/category/package/buildlink3.mk:1: For efficiency reasons, please include bsd.fast.prefs.mk instead of bsd.prefs.mk.") | 35 | "NOTE: ~/category/package/buildlink3.mk:1: For efficiency reasons, " + | |
36 | "please include bsd.fast.prefs.mk instead of bsd.prefs.mk.") | |||
34 | } | 37 | } | |
35 | 38 | |||
36 | func (s *Suite) Test_MkLineChecker_checkInclude(c *check.C) { | 39 | func (s *Suite) Test_MkLineChecker_checkInclude(c *check.C) { | |
37 | t := s.Init(c) | 40 | t := s.Init(c) | |
38 | 41 | |||
39 | t.SetupVartypes() | 42 | t.SetupVartypes() | |
40 | 43 | |||
41 | t.CreateFileLines("pkgtools/x11-links/buildlink3.mk") | 44 | t.CreateFileLines("pkgtools/x11-links/buildlink3.mk") | |
42 | t.CreateFileLines("graphics/jpeg/buildlink3.mk") | 45 | t.CreateFileLines("graphics/jpeg/buildlink3.mk") | |
43 | t.CreateFileLines("devel/intltool/buildlink3.mk") | 46 | t.CreateFileLines("devel/intltool/buildlink3.mk") | |
44 | t.CreateFileLines("devel/intltool/builtin.mk") | 47 | t.CreateFileLines("devel/intltool/builtin.mk") | |
45 | mklines := t.SetupFileMkLines("category/package/filename.mk", | 48 | mklines := t.SetupFileMkLines("category/package/filename.mk", | |
46 | MkRcsID, | 49 | MkRcsID, | |
47 | "", | 50 | "", | |
48 | ".include \"../../pkgtools/x11-links/buildlink3.mk\"", | 51 | ".include \"../../pkgtools/x11-links/buildlink3.mk\"", | |
49 | ".include \"../../graphics/jpeg/buildlink3.mk\"", | 52 | ".include \"../../graphics/jpeg/buildlink3.mk\"", | |
50 | ".include \"../../devel/intltool/buildlink3.mk\"", | 53 | ".include \"../../devel/intltool/buildlink3.mk\"", | |
51 | ".include \"../../devel/intltool/builtin.mk\"") | 54 | ".include \"../../devel/intltool/builtin.mk\"") | |
52 | 55 | |||
53 | mklines.Check() | 56 | mklines.Check() | |
54 | 57 | |||
55 | t.CheckOutputLines( | 58 | t.CheckOutputLines( | |
56 | "ERROR: ~/category/package/filename.mk:3: ../../pkgtools/x11-links/buildlink3.mk must not be included directly. "+ | 59 | "ERROR: ~/category/package/filename.mk:3: "+ | |
60 | "../../pkgtools/x11-links/buildlink3.mk must not be included directly. "+ | |||
57 | "Include \"../../mk/x11.buildlink3.mk\" instead.", | 61 | "Include \"../../mk/x11.buildlink3.mk\" instead.", | |
58 | "ERROR: ~/category/package/filename.mk:4: ../../graphics/jpeg/buildlink3.mk must not be included directly. "+ | 62 | "ERROR: ~/category/package/filename.mk:4: "+ | |
63 | "../../graphics/jpeg/buildlink3.mk must not be included directly. "+ | |||
59 | "Include \"../../mk/jpeg.buildlink3.mk\" instead.", | 64 | "Include \"../../mk/jpeg.buildlink3.mk\" instead.", | |
60 | "WARN: ~/category/package/filename.mk:5: Please write \"USE_TOOLS+= intltool\" instead of this line.", | 65 | "WARN: ~/category/package/filename.mk:5: "+ | |
61 | "ERROR: ~/category/package/filename.mk:6: ../../devel/intltool/builtin.mk must not be included directly. "+ | 66 | "Please write \"USE_TOOLS+= intltool\" instead of this line.", | |
67 | "ERROR: ~/category/package/filename.mk:6: "+ | |||
68 | "../../devel/intltool/builtin.mk must not be included directly. "+ | |||
62 | "Include \"../../devel/intltool/buildlink3.mk\" instead.") | 69 | "Include \"../../devel/intltool/buildlink3.mk\" instead.") | |
63 | } | 70 | } | |
64 | 71 | |||
65 | func (s *Suite) Test_MkLineChecker_checkInclude__Makefile(c *check.C) { | 72 | func (s *Suite) Test_MkLineChecker_checkInclude__Makefile(c *check.C) { | |
66 | t := s.Init(c) | 73 | t := s.Init(c) | |
67 | 74 | |||
68 | mkline := t.NewMkLine(t.File("Makefile"), 2, ".include \"../../other/package/Makefile\"") | 75 | mkline := t.NewMkLine(t.File("Makefile"), 2, ".include \"../../other/package/Makefile\"") | |
69 | 76 | |||
70 | MkLineChecker{mkline}.checkInclude() | 77 | MkLineChecker{mkline}.checkInclude() | |
71 | 78 | |||
72 | t.CheckOutputLines( | 79 | t.CheckOutputLines( | |
73 | "ERROR: ~/Makefile:2: \"other/package/Makefile\" does not exist.", | 80 | "ERROR: ~/Makefile:2: Relative path \"../../other/package/Makefile\" does not exist.", | |
74 | "ERROR: ~/Makefile:2: Other Makefiles must not be included directly.") | 81 | "ERROR: ~/Makefile:2: Other Makefiles must not be included directly.") | |
75 | } | 82 | } | |
76 | 83 | |||
84 | func (s *Suite) Test_MkLineChecker_checkInclude__Makefile_exists(c *check.C) { | |||
85 | t := s.Init(c) | |||
86 | ||||
87 | t.CreateFileLines("other/existing/Makefile") | |||
88 | t.SetupPackage("category/package", | |||
89 | ".include \"../../other/existing/Makefile\"", | |||
90 | ".include \"../../other/not-found/Makefile\"") | |||
91 | ||||
92 | G.checkdirPackage(t.File("category/package")) | |||
93 | ||||
94 | t.CheckOutputLines( | |||
95 | "ERROR: ~/category/package/Makefile:20: Cannot read \"../../other/existing/Makefile\".") | |||
96 | } | |||
97 | ||||
77 | func (s *Suite) Test_MkLineChecker_checkDirective(c *check.C) { | 98 | func (s *Suite) Test_MkLineChecker_checkDirective(c *check.C) { | |
78 | t := s.Init(c) | 99 | t := s.Init(c) | |
79 | 100 | |||
80 | t.SetupVartypes() | 101 | t.SetupVartypes() | |
81 | 102 | |||
82 | mklines := t.NewMkLines("category/package/filename.mk", | 103 | mklines := t.NewMkLines("category/package/filename.mk", | |
83 | MkRcsID, | 104 | MkRcsID, | |
84 | "", | 105 | "", | |
85 | ".for", | 106 | ".for", | |
86 | ".endfor", | 107 | ".endfor", | |
87 | "", | 108 | "", | |
88 | ".if", | 109 | ".if", | |
89 | ".else don't", | 110 | ".else don't", | |
90 | ".endif invalid-arg", | 111 | ".endif invalid-arg", | |
91 | "", | 112 | "", | |
92 | ".ifdef FNAME_MK", | 113 | ".ifdef FNAME_MK", | |
93 | ".endif", | 114 | ".endif", | |
94 | ".ifndef FNAME_MK", | 115 | ".ifndef FNAME_MK", | |
95 | ".endif", | 116 | ".endif", | |
96 | "", | 117 | "", | |
97 | ".for var in a b c", | 118 | ".for var in a b c", | |
98 | ".endfor", | 119 | ".endfor", | |
99 | ".undef var", | 120 | ".undef var") | |
121 | ||||
122 | mklines.Check() | |||
123 | ||||
124 | t.CheckOutputLines( | |||
125 | "ERROR: category/package/filename.mk:3: \".for\" requires arguments.", | |||
126 | "ERROR: category/package/filename.mk:6: \".if\" requires arguments.", | |||
127 | "ERROR: category/package/filename.mk:7: \".else\" does not take arguments. "+ | |||
128 | "If you meant \"else if\", use \".elif\".", | |||
129 | "ERROR: category/package/filename.mk:8: \".endif\" does not take arguments.", | |||
130 | "WARN: category/package/filename.mk:10: The \".ifdef\" directive is deprecated. "+ | |||
131 | "Please use \".if defined(FNAME_MK)\" instead.", | |||
132 | "WARN: category/package/filename.mk:12: The \".ifndef\" directive is deprecated. "+ | |||
133 | "Please use \".if !defined(FNAME_MK)\" instead.", | |||
134 | "NOTE: category/package/filename.mk:17: Using \".undef\" after a \".for\" loop is unnecessary.") | |||
135 | } | |||
136 | ||||
137 | func (s *Suite) Test_MkLineChecker_checkDirective__for_loop_varname(c *check.C) { | |||
138 | t := s.Init(c) | |||
139 | ||||
140 | t.SetupVartypes() | |||
141 | ||||
142 | mklines := t.NewMkLines("filename.mk", | |||
143 | MkRcsID, | |||
144 | "", | |||
145 | ".for VAR in a b c", // Should be lowercase. | |||
146 | ".endfor", | |||
100 | "", | 147 | "", | |
101 | ".for VAR in a b c", | 148 | ".for _var_ in a b c", // Should be written without underscores. | |
102 | ".endfor", | 149 | ".endfor", | |
103 | "", | 150 | "", | |
104 | ".for $ in a b c", | 151 | ".for .var. in a b c", // Should be written without dots. | |
152 | ".endfor", | |||
153 | "", | |||
154 | ".for ${VAR} in a b c", // The variable name really must be an identifier. | |||
105 | ".endfor") | 155 | ".endfor") | |
106 | 156 | |||
107 | mklines.Check() | 157 | mklines.Check() | |
108 | 158 | |||
109 | t.CheckOutputLines( | 159 | t.CheckOutputLines( | |
110 | "ERROR: category/package/filename.mk:3: \".for\" requires arguments.", | 160 | "WARN: filename.mk:3: The variable name \"VAR\" in the .for loop should not contain uppercase letters.", | |
111 | "ERROR: category/package/filename.mk:6: \".if\" requires arguments.", | 161 | "WARN: filename.mk:6: Variable names starting with an underscore (_var_) are reserved for internal pkgsrc use.", | |
112 | "ERROR: category/package/filename.mk:7: \".else\" does not take arguments. If you meant \"else if\", use \".elif\".", | 162 | "ERROR: filename.mk:9: Invalid variable name \".var.\".", | |
113 | "ERROR: category/package/filename.mk:8: \".endif\" does not take arguments.", | 163 | "ERROR: filename.mk:12: Invalid variable name \"${VAR}\".") | |
114 | "WARN: category/package/filename.mk:10: The \".ifdef\" directive is deprecated. Please use \".if defined(FNAME_MK)\" instead.", | |||
115 | "WARN: category/package/filename.mk:12: The \".ifndef\" directive is deprecated. Please use \".if !defined(FNAME_MK)\" instead.", | |||
116 | "NOTE: category/package/filename.mk:17: Using \".undef\" after a \".for\" loop is unnecessary.", | |||
117 | "WARN: category/package/filename.mk:19: .for variable names should not contain uppercase letters.", | |||
118 | "ERROR: category/package/filename.mk:22: Invalid variable name \"$\".") | |||
119 | } | 164 | } | |
120 | 165 | |||
121 | func (s *Suite) Test_MkLineChecker_checkDependencyRule(c *check.C) { | 166 | func (s *Suite) Test_MkLineChecker_checkDependencyRule(c *check.C) { | |
122 | t := s.Init(c) | 167 | t := s.Init(c) | |
123 | 168 | |||
124 | t.SetupVartypes() | 169 | t.SetupVartypes() | |
125 | 170 | |||
126 | mklines := t.NewMkLines("category/package/filename.mk", | 171 | mklines := t.NewMkLines("category/package/filename.mk", | |
127 | MkRcsID, | 172 | MkRcsID, | |
128 | "", | 173 | "", | |
129 | ".PHONY: target-1", | 174 | ".PHONY: target-1", | |
130 | "target-2: .PHONY", | 175 | "target-2: .PHONY", | |
131 | ".ORDER: target-1 target-2", | 176 | ".ORDER: target-1 target-2", | |
@@ -135,55 +180,55 @@ func (s *Suite) Test_MkLineChecker_check | @@ -135,55 +180,55 @@ func (s *Suite) Test_MkLineChecker_check | |||
135 | 180 | |||
136 | mklines.Check() | 181 | mklines.Check() | |
137 | 182 | |||
138 | t.CheckOutputLines( | 183 | t.CheckOutputLines( | |
139 | "WARN: category/package/filename.mk:8: Unusual target \"target-3\".") | 184 | "WARN: category/package/filename.mk:8: Unusual target \"target-3\".") | |
140 | } | 185 | } | |
141 | 186 | |||
142 | func (s *Suite) Test_MkLineChecker_checkVartype__simple_type(c *check.C) { | 187 | func (s *Suite) Test_MkLineChecker_checkVartype__simple_type(c *check.C) { | |
143 | t := s.Init(c) | 188 | t := s.Init(c) | |
144 | 189 | |||
145 | t.SetupCommandLine("-Wtypes") | 190 | t.SetupCommandLine("-Wtypes") | |
146 | t.SetupVartypes() | 191 | t.SetupVartypes() | |
147 | 192 | |||
148 | vartype1 := G.Pkgsrc.vartypes["COMMENT"] | 193 | // Since COMMENT is defined in vardefs.go its type is certain instead of guessed. | |
149 | c.Assert(vartype1, check.NotNil) | |||
150 | c.Check(vartype1.guessed, equals, false) | |||
151 | ||||
152 | vartype := G.Pkgsrc.VariableType("COMMENT") | 194 | vartype := G.Pkgsrc.VariableType("COMMENT") | |
153 | 195 | |||
154 | c.Assert(vartype, check.NotNil) | 196 | c.Assert(vartype, check.NotNil) | |
155 | c.Check(vartype.basicType.name, equals, "Comment") | 197 | c.Check(vartype.basicType.name, equals, "Comment") | |
156 | c.Check(vartype.guessed, equals, false) | 198 | c.Check(vartype.guessed, equals, false) | |
157 | c.Check(vartype.kindOfList, equals, lkNone) | 199 | c.Check(vartype.kindOfList, equals, lkNone) | |
158 | 200 | |||
159 | mkline := t.NewMkLine("Makefile", 123, "COMMENT=\tA nice package") | 201 | mkline := t.NewMkLine("Makefile", 123, "COMMENT=\tA nice package") | |
160 | MkLineChecker{mkline}.checkVartype(mkline.Varname(), mkline.Op(), mkline.Value(), mkline.VarassignComment()) | 202 | MkLineChecker{mkline}.checkVartype(mkline.Varname(), mkline.Op(), mkline.Value(), mkline.VarassignComment()) | |
161 | 203 | |||
162 | t.CheckOutputLines( | 204 | t.CheckOutputLines( | |
163 | "WARN: Makefile:123: COMMENT should not begin with \"A\".") | 205 | "WARN: Makefile:123: COMMENT should not begin with \"A\".") | |
164 | } | 206 | } | |
165 | 207 | |||
166 | func (s *Suite) Test_MkLineChecker_checkVartype(c *check.C) { | 208 | func (s *Suite) Test_MkLineChecker_checkVartype(c *check.C) { | |
167 | t := s.Init(c) | 209 | t := s.Init(c) | |
168 | 210 | |||
169 | t.SetupVartypes() | 211 | t.SetupVartypes() | |
170 | mkline := t.NewMkLine("filename", 1, "DISTNAME=gcc-${GCC_VERSION}") | 212 | mkline := t.NewMkLine("filename", 1, "DISTNAME=gcc-${GCC_VERSION}") | |
171 | 213 | |||
172 | MkLineChecker{mkline}.checkVartype("DISTNAME", opAssign, "gcc-${GCC_VERSION}", "") | 214 | MkLineChecker{mkline}.checkVartype("DISTNAME", opAssign, "gcc-${GCC_VERSION}", "") | |
173 | 215 | |||
174 | t.CheckOutputEmpty() | 216 | t.CheckOutputEmpty() | |
175 | } | 217 | } | |
176 | 218 | |||
219 | // The command line option -Wno-types can be used to suppress the type checks. | |||
220 | // Suppressing it is rarely needed and comes from Feb 12 2005 when this feature was introduced. | |||
221 | // Since then the type system has matured and proven effective. | |||
177 | func (s *Suite) Test_MkLineChecker_checkVartype__skip(c *check.C) { | 222 | func (s *Suite) Test_MkLineChecker_checkVartype__skip(c *check.C) { | |
178 | t := s.Init(c) | 223 | t := s.Init(c) | |
179 | 224 | |||
180 | t.SetupCommandLine("-Wno-types") | 225 | t.SetupCommandLine("-Wno-types") | |
181 | t.SetupVartypes() | 226 | t.SetupVartypes() | |
182 | mkline := t.NewMkLine("filename", 1, "DISTNAME=invalid:::distname") | 227 | mkline := t.NewMkLine("filename", 1, "DISTNAME=invalid:::distname") | |
183 | 228 | |||
184 | MkLineChecker{mkline}.Check() | 229 | MkLineChecker{mkline}.Check() | |
185 | 230 | |||
186 | t.CheckOutputEmpty() | 231 | t.CheckOutputEmpty() | |
187 | } | 232 | } | |
188 | 233 | |||
189 | func (s *Suite) Test_MkLineChecker_checkVartype__append_to_non_list(c *check.C) { | 234 | func (s *Suite) Test_MkLineChecker_checkVartype__append_to_non_list(c *check.C) { | |
@@ -207,96 +252,84 @@ func (s *Suite) Test_MkLineChecker_check | @@ -207,96 +252,84 @@ func (s *Suite) Test_MkLineChecker_check | |||
207 | // splitting this URL at the ampersand. | 252 | // splitting this URL at the ampersand. | |
208 | func (s *Suite) Test_MkLineChecker_checkVarassign__URL_with_shell_special_characters(c *check.C) { | 253 | func (s *Suite) Test_MkLineChecker_checkVarassign__URL_with_shell_special_characters(c *check.C) { | |
209 | t := s.Init(c) | 254 | t := s.Init(c) | |
210 | 255 | |||
211 | G.Pkg = NewPackage(t.File("graphics/gimp-fix-ca")) | 256 | G.Pkg = NewPackage(t.File("graphics/gimp-fix-ca")) | |
212 | t.SetupVartypes() | 257 | t.SetupVartypes() | |
213 | mkline := t.NewMkLine("filename", 10, "MASTER_SITES=http://registry.gimp.org/file/fix-ca.c?action=download&id=9884&file=") | 258 | mkline := t.NewMkLine("filename", 10, "MASTER_SITES=http://registry.gimp.org/file/fix-ca.c?action=download&id=9884&file=") | |
214 | 259 | |||
215 | MkLineChecker{mkline}.checkVarassign() | 260 | MkLineChecker{mkline}.checkVarassign() | |
216 | 261 | |||
217 | t.CheckOutputEmpty() | 262 | t.CheckOutputEmpty() | |
218 | } | 263 | } | |
219 | 264 | |||
220 | func (s *Suite) Test_MkLineChecker_Check__conditions(c *check.C) { | 265 | func (s *Suite) Test_MkLineChecker_checkDirectiveCond(c *check.C) { | |
221 | t := s.Init(c) | 266 | t := s.Init(c) | |
222 | 267 | |||
223 | t.SetupCommandLine("-Wtypes") | 268 | t.SetupCommandLine("-Wtypes") | |
224 | t.SetupVartypes() | 269 | t.SetupVartypes() | |
225 | 270 | |||
226 | testCond := func(cond string, output ...string) { | 271 | testCond := func(cond string, output ...string) { | |
227 | MkLineChecker{t.NewMkLine("filename", 1, cond)}.checkDirectiveCond() | 272 | MkLineChecker{t.NewMkLine("filename", 1, cond)}.checkDirectiveCond() | |
228 | if len(output) > 0 { | 273 | if len(output) > 0 { | |
229 | t.CheckOutputLines(output...) | 274 | t.CheckOutputLines(output...) | |
230 | } else { | 275 | } else { | |
231 | t.CheckOutputEmpty() | 276 | t.CheckOutputEmpty() | |
232 | } | 277 | } | |
233 | } | 278 | } | |
234 | 279 | |||
235 | testCond(".if !empty(PKGSRC_COMPILER:Mmycc)", | 280 | testCond(".if !empty(PKGSRC_COMPILER:Mmycc)", | |
236 | "WARN: filename:1: The pattern \"mycc\" cannot match any of "+ | 281 | "WARN: filename:1: The pattern \"mycc\" cannot match any of "+ | |
237 | "{ ccache ccc clang distcc f2c gcc hp icc ido "+ | 282 | "{ ccache ccc clang distcc f2c gcc hp icc ido "+ | |
238 | "mipspro mipspro-ucode pcc sunpro xlc } for PKGSRC_COMPILER.") | 283 | "mipspro mipspro-ucode pcc sunpro xlc } for PKGSRC_COMPILER.") | |
239 | 284 | |||
240 | testCond(".elif ${A} != ${B}") | 285 | testCond(".elif ${A} != ${B}") | |
241 | 286 | |||
242 | testCond(".if ${HOMEPAGE} == \"mailto:someone@example.org\"", | 287 | testCond(".if ${HOMEPAGE} == \"mailto:someone@example.org\"", | |
243 | "WARN: filename:1: \"mailto:someone@example.org\" is not a valid URL.") | 288 | "WARN: filename:1: \"mailto:someone@example.org\" is not a valid URL.") | |
244 | 289 | |||
245 | testCond(".if !empty(PKGSRC_RUN_TEST:M[Y][eE][sS])", | 290 | testCond(".if !empty(PKGSRC_RUN_TEST:M[Y][eE][sS])", | |
246 | "WARN: filename:1: PKGSRC_RUN_TEST should be matched against \"[yY][eE][sS]\" or \"[nN][oO]\", not \"[Y][eE][sS]\".") | 291 | "WARN: filename:1: PKGSRC_RUN_TEST should be matched "+ | |
292 | "against \"[yY][eE][sS]\" or \"[nN][oO]\", not \"[Y][eE][sS]\".") | |||
247 | 293 | |||
248 | testCond(".if !empty(IS_BUILTIN.Xfixes:M[yY][eE][sS])") | 294 | testCond(".if !empty(IS_BUILTIN.Xfixes:M[yY][eE][sS])") | |
249 | 295 | |||
250 | testCond(".if !empty(${IS_BUILTIN.Xfixes:M[yY][eE][sS]})", | 296 | testCond(".if !empty(${IS_BUILTIN.Xfixes:M[yY][eE][sS]})", | |
251 | "WARN: filename:1: The empty() function takes a variable name as parameter, not a variable expression.") | 297 | "WARN: filename:1: The empty() function takes a variable name as parameter, "+ | |
298 | "not a variable expression.") | |||
252 | 299 | |||
253 | testCond(".if ${EMUL_PLATFORM} == \"linux-x386\"", | 300 | testCond(".if ${PKGSRC_COMPILER} == \"msvc\"", | |
254 | "WARN: filename:1: "+ | 301 | "WARN: filename:1: \"msvc\" is not valid for PKGSRC_COMPILER. "+ | |
255 | "\"x386\" is not valid for the hardware architecture part of EMUL_PLATFORM. "+ | 302 | "Use one of { ccache ccc clang distcc f2c gcc hp icc ido mipspro mipspro-ucode pcc sunpro xlc } instead.", | |
256 | "Use one of "+ | 303 | "WARN: filename:1: Use ${PKGSRC_COMPILER:Mmsvc} instead of the == operator.") | |
257 | "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 cobalt coldfire convex "+ | |||
258 | "dreamcast earm earmeb earmhf earmhfeb earmv4 earmv4eb earmv5 earmv5eb earmv6 earmv6eb "+ | |||
259 | "earmv6hf earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+ | |||
260 | "i386 i586 i686 ia64 m68000 m68k m88k mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+ | |||
261 | "mlrisc ns32k pc532 pmax powerpc powerpc64 rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+ | |||
262 | "} instead.") | |||
263 | 304 | |||
264 | testCond(".if ${EMUL_PLATFORM:Mlinux-x386}", | 305 | testCond(".if ${PKG_LIBTOOL:Mlibtool}", | |
265 | "WARN: filename:1: "+ | 306 | "NOTE: filename:1: PKG_LIBTOOL should be compared using == instead of matching against \":Mlibtool\".") | |
266 | "The pattern \"x386\" cannot match any of { aarch64 aarch64eb alpha amd64 arc arm arm26 "+ | |||
267 | "arm32 cobalt coldfire convex dreamcast earm earmeb earmhf earmhfeb earmv4 earmv4eb "+ | |||
268 | "earmv5 earmv5eb earmv6 earmv6eb earmv6hf earmv6hfeb earmv7 earmv7eb earmv7hf "+ | |||
269 | "earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 i386 i586 i686 ia64 m68000 m68k m88k "+ | |||
270 | "mips mips64 mips64eb mips64el mipseb mipsel mipsn32 mlrisc ns32k pc532 pmax powerpc powerpc64 "+ | |||
271 | "rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 } "+ | |||
272 | "for the hardware architecture part of EMUL_PLATFORM.", | |||
273 | "NOTE: filename:1: EMUL_PLATFORM should be compared using == instead of the :M or :N modifier without wildcards.") | |||
274 | 307 | |||
275 | testCond(".if ${MACHINE_PLATFORM:MUnknownOS-*-*} || ${MACHINE_ARCH:Mx86}", | 308 | testCond(".if ${MACHINE_PLATFORM:MUnknownOS-*-*} || ${MACHINE_ARCH:Mx86}", | |
276 | "WARN: filename:1: "+ | 309 | "WARN: filename:1: "+ | |
277 | "The pattern \"UnknownOS\" cannot match any of "+ | 310 | "The pattern \"UnknownOS\" cannot match any of "+ | |
278 | "{ AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD HPUX Haiku "+ | 311 | "{ AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD HPUX Haiku "+ | |
279 | "IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare "+ | 312 | "IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare "+ | |
280 | "} for the operating system part of MACHINE_PLATFORM.", | 313 | "} for the operating system part of MACHINE_PLATFORM.", | |
281 | "WARN: filename:1: "+ | 314 | "WARN: filename:1: "+ | |
282 | "The pattern \"x86\" cannot match any of "+ | 315 | "The pattern \"x86\" cannot match any of "+ | |
283 | "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 cobalt coldfire convex dreamcast earm "+ | 316 | "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 cobalt coldfire convex dreamcast earm "+ | |
284 | "earmeb earmhf earmhfeb earmv4 earmv4eb earmv5 earmv5eb earmv6 earmv6eb earmv6hf earmv6hfeb "+ | 317 | "earmeb earmhf earmhfeb earmv4 earmv4eb earmv5 earmv5eb earmv6 earmv6eb earmv6hf earmv6hfeb "+ | |
285 | "earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 i386 i586 i686 ia64 "+ | 318 | "earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 i386 i586 i686 ia64 "+ | |
286 | "m68000 m68k m88k mips mips64 mips64eb mips64el mipseb mipsel mipsn32 mlrisc ns32k pc532 pmax "+ | 319 | "m68000 m68k m88k mips mips64 mips64eb mips64el mipseb mipsel mipsn32 mlrisc ns32k pc532 pmax "+ | |
287 | "powerpc powerpc64 rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+ | 320 | "powerpc powerpc64 rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+ | |
288 | "} for MACHINE_ARCH.", | 321 | "} for MACHINE_ARCH.", | |
289 | "NOTE: filename:1: MACHINE_ARCH should be compared using == instead of the :M or :N modifier without wildcards.") | 322 | "NOTE: filename:1: MACHINE_ARCH should be compared using == instead of matching against \":Mx86\".") | |
290 | 323 | |||
291 | testCond(".if ${MASTER_SITES:Mftp://*} == \"ftp://netbsd.org/\"") | 324 | testCond(".if ${MASTER_SITES:Mftp://*} == \"ftp://netbsd.org/\"") | |
292 | } | 325 | } | |
293 | 326 | |||
294 | func (s *Suite) Test_MkLineChecker_checkVarassign(c *check.C) { | 327 | func (s *Suite) Test_MkLineChecker_checkVarassign(c *check.C) { | |
295 | t := s.Init(c) | 328 | t := s.Init(c) | |
296 | 329 | |||
297 | t.SetupVartypes() | 330 | t.SetupVartypes() | |
298 | 331 | |||
299 | G.Mk = t.NewMkLines("Makefile", | 332 | G.Mk = t.NewMkLines("Makefile", | |
300 | MkRcsID, | 333 | MkRcsID, | |
301 | "ac_cv_libpari_libs+=\t-L${BUILDLINK_PREFIX.pari}/lib") // From math/clisp-pari/Makefile, rev. 1.8 | 334 | "ac_cv_libpari_libs+=\t-L${BUILDLINK_PREFIX.pari}/lib") // From math/clisp-pari/Makefile, rev. 1.8 | |
302 | 335 | |||
@@ -376,31 +409,30 @@ func (s *Suite) Test_MkLineChecker_check | @@ -376,31 +409,30 @@ func (s *Suite) Test_MkLineChecker_check | |||
376 | 409 | |||
377 | func (s *Suite) Test_MkLineChecker_checkVarusePermissions__load_time(c *check.C) { | 410 | func (s *Suite) Test_MkLineChecker_checkVarusePermissions__load_time(c *check.C) { | |
378 | t := s.Init(c) | 411 | t := s.Init(c) | |
379 | 412 | |||
380 | t.SetupVartypes() | 413 | t.SetupVartypes() | |
381 | mklines := t.NewMkLines("options.mk", | 414 | mklines := t.NewMkLines("options.mk", | |
382 | MkRcsID, | 415 | MkRcsID, | |
383 | "WRKSRC:=${.CURDIR}", | 416 | "WRKSRC:=${.CURDIR}", | |
384 | ".if ${PKG_SYSCONFDIR.gdm} != \"etc\"", | 417 | ".if ${PKG_SYSCONFDIR.gdm} != \"etc\"", | |
385 | ".endif") | 418 | ".endif") | |
386 | 419 | |||
387 | mklines.Check() | 420 | mklines.Check() | |
388 | 421 | |||
389 | // Evaluating PKG_SYSCONFDIR.* at load time is probably ok, though | 422 | // Evaluating PKG_SYSCONFDIR.* at load time is probably ok, | |
390 | // pkglint cannot prove anything here. | 423 | // though pkglint cannot prove anything here. | |
391 | // | 424 | // | |
392 | // Evaluating .CURDIR at load time is ok since it is defined from | 425 | // Evaluating .CURDIR at load time is definitely ok since it is defined from the beginning. | |
393 | // the beginning. | |||
394 | t.CheckOutputLines( | 426 | t.CheckOutputLines( | |
395 | "NOTE: options.mk:2: This variable value should be aligned to column 17.") | 427 | "NOTE: options.mk:2: This variable value should be aligned to column 17.") | |
396 | } | 428 | } | |
397 | 429 | |||
398 | func (s *Suite) Test_MkLineChecker_checkVarusePermissions__load_time_guessed(c *check.C) { | 430 | func (s *Suite) Test_MkLineChecker_checkVarusePermissions__load_time_guessed(c *check.C) { | |
399 | t := s.Init(c) | 431 | t := s.Init(c) | |
400 | 432 | |||
401 | t.SetupVartypes() | 433 | t.SetupVartypes() | |
402 | t.SetupTool("install", "", AtRunTime) | 434 | t.SetupTool("install", "", AtRunTime) | |
403 | mklines := t.NewMkLines("install-docfiles.mk", | 435 | mklines := t.NewMkLines("install-docfiles.mk", | |
404 | MkRcsID, | 436 | MkRcsID, | |
405 | "DOCFILES=\ta b c", | 437 | "DOCFILES=\ta b c", | |
406 | "do-install:", | 438 | "do-install:", | |
@@ -428,97 +460,155 @@ func (s *Suite) Test_MkLineChecker_check | @@ -428,97 +460,155 @@ func (s *Suite) Test_MkLineChecker_check | |||
428 | mklines := t.NewMkLines("any.mk", | 460 | mklines := t.NewMkLines("any.mk", | |
429 | MkRcsID, | 461 | MkRcsID, | |
430 | // PKGREVISION may only be set in Makefile, not used at load time; see vardefs.go. | 462 | // PKGREVISION may only be set in Makefile, not used at load time; see vardefs.go. | |
431 | ".if defined(PKGREVISION)", | 463 | ".if defined(PKGREVISION)", | |
432 | ".endif") | 464 | ".endif") | |
433 | 465 | |||
434 | mklines.Check() | 466 | mklines.Check() | |
435 | 467 | |||
436 | t.CheckOutputLines( | 468 | t.CheckOutputLines( | |
437 | "WARN: any.mk:2: PKGREVISION should not be evaluated at load time.", | 469 | "WARN: any.mk:2: PKGREVISION should not be evaluated at load time.", | |
438 | "WARN: any.mk:2: PKGREVISION may not be used in any file; it is a write-only variable.") | 470 | "WARN: any.mk:2: PKGREVISION may not be used in any file; it is a write-only variable.") | |
439 | } | 471 | } | |
440 | 472 | |||
441 | func (s *Suite) Test_MkLineChecker__warn_varuse_LOCALBASE(c *check.C) { | 473 | func (s *Suite) Test_MkLineChecker_Check__warn_varuse_LOCALBASE(c *check.C) { | |
442 | t := s.Init(c) | 474 | t := s.Init(c) | |
443 | 475 | |||
444 | t.SetupVartypes() | 476 | t.SetupVartypes() | |
445 | mkline := t.NewMkLine("options.mk", 56, "PKGNAME=${LOCALBASE}") | 477 | mkline := t.NewMkLine("options.mk", 56, "PKGNAME=${LOCALBASE}") | |
446 | 478 | |||
447 | MkLineChecker{mkline}.Check() | 479 | MkLineChecker{mkline}.Check() | |
448 | 480 | |||
449 | t.CheckOutputLines( | 481 | t.CheckOutputLines( | |
450 | "WARN: options.mk:56: Please use PREFIX instead of LOCALBASE.") | 482 | "WARN: options.mk:56: Please use PREFIX instead of LOCALBASE.") | |
451 | } | 483 | } | |
452 | 484 | |||
453 | func (s *Suite) Test_MkLineChecker_CheckRelativePkgdir(c *check.C) { | 485 | func (s *Suite) Test_MkLineChecker_CheckRelativePkgdir(c *check.C) { | |
454 | t := s.Init(c) | 486 | t := s.Init(c) | |
455 | 487 | |||
456 | mklines := t.SetupFileMkLines("Makefile", | 488 | t.CreateFileLines("other/package/Makefile") | |
489 | // Must be in the filesystem because of directory references. | |||
490 | mklines := t.SetupFileMkLines("category/package/Makefile", | |||
457 | "# dummy") | 491 | "# dummy") | |
492 | ck := MkLineChecker{mklines.mklines[0]} | |||
458 | 493 | |||
459 | MkLineChecker{mklines.mklines[0]}.CheckRelativePkgdir("../pkgbase") | 494 | ck.CheckRelativePkgdir("../pkgbase") | |
460 | 495 | ck.CheckRelativePkgdir("../../other/package") | ||
461 | t.CheckOutputLines( | 496 | ck.CheckRelativePkgdir("../../other/does-not-exist") | |
462 | "ERROR: ~/Makefile:1: \"../pkgbase\" does not exist.", | 497 | ||
463 | "WARN: ~/Makefile:1: \"../pkgbase\" is not a valid relative package directory.") | 498 | // FIXME: The diagnostics for does-not-exist are redundant. | |
499 | t.CheckOutputLines( | |||
500 | "ERROR: ~/category/package/Makefile:1: Relative path \"../pkgbase\" does not exist.", | |||
501 | "WARN: ~/category/package/Makefile:1: \"../pkgbase\" is not a valid relative package directory.", | |||
502 | "ERROR: ~/category/package/Makefile:1: Relative path \"../../other/does-not-exist\" does not exist.", | |||
503 | "ERROR: ~/category/package/Makefile:1: There is no package in \"other/does-not-exist\".") | |||
464 | } | 504 | } | |
465 | 505 | |||
466 | // PR pkg/46570, item 2 | 506 | // PR pkg/46570, item 2 | |
467 | func (s *Suite) Test_MkLineChecker__unclosed_varuse(c *check.C) { | 507 | func (s *Suite) Test_MkLineChecker__unclosed_varuse(c *check.C) { | |
468 | t := s.Init(c) | 508 | t := s.Init(c) | |
469 | 509 | |||
470 | mklines := t.NewMkLines("Makefile", | 510 | mklines := t.NewMkLines("Makefile", | |
471 | MkRcsID, | 511 | MkRcsID, | |
472 | "EGDIRS=\t${EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d") | 512 | "EGDIRS=\t${EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d") | |
473 | 513 | |||
474 | mklines.Check() | 514 | mklines.Check() | |
475 | 515 | |||
476 | t.CheckOutputLines( | 516 | t.CheckOutputLines( | |
477 | "WARN: Makefile:2: Unclosed Make variable starting at \"${EGDIR/apparmor.d $...\".", | 517 | "WARN: Makefile:2: Unclosed Make variable starting at \"${EGDIR/apparmor.d $...\".", | |
478 | "WARN: Makefile:2: EGDIRS is defined but not used.", | 518 | "WARN: Makefile:2: EGDIRS is defined but not used.", | |
519 | ||||
520 | // XXX: This warning is redundant because of the "Unclosed" warning above. | |||
479 | "WARN: Makefile:2: Pkglint parse error in MkLine.Tokenize at "+ | 521 | "WARN: Makefile:2: Pkglint parse error in MkLine.Tokenize at "+ | |
480 | "\"${EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d\".") | 522 | "\"${EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d\".") | |
481 | } | 523 | } | |
482 | 524 | |||
483 | func (s *Suite) Test_MkLineChecker__Varuse_Modifier_L(c *check.C) { | 525 | func (s *Suite) Test_MkLineChecker_Check__varuse_modifier_L(c *check.C) { | |
484 | t := s.Init(c) | 526 | t := s.Init(c) | |
485 | 527 | |||
486 | t.SetupVartypes() | 528 | t.SetupVartypes() | |
487 | G.Mk = t.NewMkLines("x11/xkeyboard-config/Makefile", | 529 | mklines := t.NewMkLines("x11/xkeyboard-config/Makefile", | |
488 | "FILES_SUBST+=XKBCOMP_SYMLINK=${${XKBBASE}/xkbcomp:L:Q}") | 530 | "FILES_SUBST+=XKBCOMP_SYMLINK=${${XKBBASE}/xkbcomp:L:Q}", | |
531 | "FILES_SUBST+=XKBCOMP_SYMLINK=${${XKBBASE}/xkbcomp:Q}") | |||
489 | 532 | |||
490 | MkLineChecker{G.Mk.mklines[0]}.Check() | 533 | MkLineChecker{mklines.mklines[0]}.Check() | |
534 | MkLineChecker{mklines.mklines[1]}.Check() | |||
491 | 535 | |||
492 | // Don't warn that ${XKBBASE}/xkbcomp is used but not defined. | 536 | // In line 1, don't warn that ${XKBBASE}/xkbcomp is used but not defined. | |
493 | t.CheckOutputEmpty() | 537 | // This is because the :L modifier interprets everything before as an expression | |
538 | // instead of a variable name. | |||
539 | // | |||
540 | // In line 2 the :L modifier is missing, therefore ${XKBBASE}/xkbcomp is the | |||
541 | // name of another variable, and that variable is not known. Only XKBBASE is known. | |||
542 | // | |||
543 | // FIXME: The below warnings are wrong because the MkParser does not recognize the | |||
544 | // slash as part of a variable name. Because of that, parsing stops before the $. | |||
545 | // The warning "Unclosed Make variable" wrongly assumes that any parse error from | |||
546 | // a variable use is because of unclosed braces, which it isn't in this case.h | |||
547 | t.CheckOutputLines( | |||
548 | "WARN: x11/xkeyboard-config/Makefile:2: Unclosed Make variable starting at \"${${XKBBASE}/xkbcomp...\".", | |||
549 | "WARN: x11/xkeyboard-config/Makefile:2: Unclosed Make variable starting at \"${${XKBBASE}/xkbcomp...\".") | |||
494 | } | 550 | } | |
495 | 551 | |||
496 | func (s *Suite) Test_MkLineChecker_checkDirectiveCond__comparison_with_shell_command(c *check.C) { | 552 | func (s *Suite) Test_MkLineChecker_checkDirectiveCond__comparison_with_shell_command(c *check.C) { | |
497 | t := s.Init(c) | 553 | t := s.Init(c) | |
498 | 554 | |||
499 | t.SetupVartypes() | 555 | t.SetupVartypes() | |
500 | G.Mk = t.NewMkLines("security/openssl/Makefile", | 556 | mklines := t.NewMkLines("security/openssl/Makefile", | |
501 | MkRcsID, | 557 | MkRcsID, | |
502 | ".if ${PKGSRC_COMPILER} == \"gcc\" && ${CC} == \"cc\"", | 558 | ".if ${PKGSRC_COMPILER} == \"gcc\" && ${CC} == \"cc\"", | |
503 | ".endif") | 559 | ".endif") | |
504 | 560 | |||
505 | G.Mk.Check() | 561 | mklines.Check() | |
506 | 562 | |||
507 | // Don't warn about unknown shell command "cc". | 563 | // Don't warn about unknown shell command "cc". | |
508 | t.CheckOutputLines( | 564 | t.CheckOutputLines( | |
509 | "WARN: security/openssl/Makefile:2: Use ${PKGSRC_COMPILER:Mgcc} instead of the == operator.") | 565 | "WARN: security/openssl/Makefile:2: Use ${PKGSRC_COMPILER:Mgcc} instead of the == operator.") | |
510 | } | 566 | } | |
511 | 567 | |||
568 | func (s *Suite) Test_MkLineChecker_checkDirectiveCondEmpty(c *check.C) { | |||
569 | t := s.Init(c) | |||
570 | ||||
571 | t.SetupVartypes() | |||
572 | mkline := t.NewMkLine("module.mk", 123, ".if ${PKGPATH} == \"category/package\"") | |||
573 | ck := MkLineChecker{mkline} | |||
574 | ||||
575 | // FIXME: checkDirectiveCondEmpty cannot know whether it is empty(...) or !empty(...). | |||
576 | // It must know that to generate the proper diagnostics. | |||
577 | ||||
578 | ck.checkDirectiveCondEmpty(NewMkVarUse("PKGPATH", "Mpattern")) | |||
579 | ||||
580 | // When the pattern contains placeholders, it cannot be converted to == or !=. | |||
581 | ck.checkDirectiveCondEmpty(NewMkVarUse("PKGPATH", "Mpa*n")) | |||
582 | ||||
583 | ck.checkDirectiveCondEmpty(NewMkVarUse("PKGPATH", "tl", "Mpattern")) | |||
584 | ||||
585 | ck.checkDirectiveCondEmpty(NewMkVarUse("PKGPATH", "Ncategory/package")) | |||
586 | ||||
587 | // ${PKGPATH:None:Ntwo} is a short variant of ${PKGPATH} != "one" && ${PKGPATH} != "two", | |||
588 | // therefore no note is logged in this case. | |||
589 | ck.checkDirectiveCondEmpty(NewMkVarUse("PKGPATH", "None", "Ntwo")) | |||
590 | ||||
591 | // Note: this combination doesn't make sense since the patterns "one" and "two" don't overlap. | |||
592 | ck.checkDirectiveCondEmpty(NewMkVarUse("PKGPATH", "Mone", "Mtwo")) | |||
593 | ||||
594 | t.CheckOutputLines( | |||
595 | "NOTE: module.mk:123: PKGPATH should be compared using == instead of matching against \":Mpattern\".", | |||
596 | "NOTE: module.mk:123: PKGPATH should be compared using == instead of matching against \":Mpattern\".", | |||
597 | "NOTE: module.mk:123: PKGPATH should be compared using != instead of matching against \":Ncategory/package\".", | |||
598 | "NOTE: module.mk:123: PKGPATH should be compared using == instead of matching against \":Mone\".", | |||
599 | "NOTE: module.mk:123: PKGPATH should be compared using == instead of matching against \":Mtwo\".") | |||
600 | } | |||
601 | ||||
512 | func (s *Suite) Test_MkLineChecker_checkDirectiveCond__comparing_PKGSRC_COMPILER_with_eqeq(c *check.C) { | 602 | func (s *Suite) Test_MkLineChecker_checkDirectiveCond__comparing_PKGSRC_COMPILER_with_eqeq(c *check.C) { | |
513 | t := s.Init(c) | 603 | t := s.Init(c) | |
514 | 604 | |||
515 | t.SetupVartypes() | 605 | t.SetupVartypes() | |
516 | G.Mk = t.NewMkLines("audio/pulseaudio/Makefile", | 606 | G.Mk = t.NewMkLines("audio/pulseaudio/Makefile", | |
517 | MkRcsID, | 607 | MkRcsID, | |
518 | ".if ${OPSYS} == \"Darwin\" && ${PKGSRC_COMPILER} == \"clang\"", | 608 | ".if ${OPSYS} == \"Darwin\" && ${PKGSRC_COMPILER} == \"clang\"", | |
519 | ".endif") | 609 | ".endif") | |
520 | 610 | |||
521 | G.Mk.Check() | 611 | G.Mk.Check() | |
522 | 612 | |||
523 | t.CheckOutputLines( | 613 | t.CheckOutputLines( | |
524 | "WARN: audio/pulseaudio/Makefile:2: Use ${PKGSRC_COMPILER:Mclang} instead of the == operator.") | 614 | "WARN: audio/pulseaudio/Makefile:2: Use ${PKGSRC_COMPILER:Mclang} instead of the == operator.") | |
@@ -528,27 +618,28 @@ func (s *Suite) Test_MkLineChecker_check | @@ -528,27 +618,28 @@ func (s *Suite) Test_MkLineChecker_check | |||
528 | t := s.Init(c) | 618 | t := s.Init(c) | |
529 | 619 | |||
530 | t.SetupVartypes() | 620 | t.SetupVartypes() | |
531 | G.Mk = t.NewMkLines("chat/pidgin-icb/Makefile", | 621 | G.Mk = t.NewMkLines("chat/pidgin-icb/Makefile", | |
532 | MkRcsID, | 622 | MkRcsID, | |
533 | "CFLAGS+=\t`pkg-config pidgin --cflags`") | 623 | "CFLAGS+=\t`pkg-config pidgin --cflags`") | |
534 | mkline := G.Mk.mklines[1] | 624 | mkline := G.Mk.mklines[1] | |
535 | 625 | |||
536 | words, rest := splitIntoMkWords(mkline.Line, mkline.Value()) | 626 | words, rest := splitIntoMkWords(mkline.Line, mkline.Value()) | |
537 | 627 | |||
538 | c.Check(words, deepEquals, []string{"`pkg-config pidgin --cflags`"}) | 628 | c.Check(words, deepEquals, []string{"`pkg-config pidgin --cflags`"}) | |
539 | c.Check(rest, equals, "") | 629 | c.Check(rest, equals, "") | |
540 | 630 | |||
541 | MkLineChecker{G.Mk.mklines[1]}.checkVartype("CFLAGS", opAssignAppend, "`pkg-config pidgin --cflags`", "") | 631 | ck := MkLineChecker{G.Mk.mklines[1]} | |
632 | ck.checkVartype("CFLAGS", opAssignAppend, "`pkg-config pidgin --cflags`", "") | |||
542 | 633 | |||
543 | // No warning about "`pkg-config" being an unknown CFlag. | 634 | // No warning about "`pkg-config" being an unknown CFlag. | |
544 | t.CheckOutputEmpty() | 635 | t.CheckOutputEmpty() | |
545 | } | 636 | } | |
546 | 637 | |||
547 | // See PR 46570, Ctrl+F "4. Shell quoting". | 638 | // See PR 46570, Ctrl+F "4. Shell quoting". | |
548 | // Pkglint is correct, since the shell sees this definition for | 639 | // Pkglint is correct, since the shell sees this definition for | |
549 | // CPPFLAGS as three words, not one word. | 640 | // CPPFLAGS as three words, not one word. | |
550 | func (s *Suite) Test_MkLineChecker_checkVartype__CFLAGS(c *check.C) { | 641 | func (s *Suite) Test_MkLineChecker_checkVartype__CFLAGS(c *check.C) { | |
551 | t := s.Init(c) | 642 | t := s.Init(c) | |
552 | 643 | |||
553 | t.SetupVartypes() | 644 | t.SetupVartypes() | |
554 | mklines := t.NewMkLines("Makefile", | 645 | mklines := t.NewMkLines("Makefile", | |
@@ -730,26 +821,30 @@ func (s *Suite) Test_MkLineChecker_Check | @@ -730,26 +821,30 @@ func (s *Suite) Test_MkLineChecker_Check | |||
730 | kindOfList: lkNone, | 821 | kindOfList: lkNone, | |
731 | basicType: BtPathname, | 822 | basicType: BtPathname, | |
732 | aclEntries: nil, | 823 | aclEntries: nil, | |
733 | guessed: true, | 824 | guessed: true, | |
734 | }, | 825 | }, | |
735 | time: vucTimeRun, | 826 | time: vucTimeRun, | |
736 | quoting: vucQuotPlain, | 827 | quoting: vucQuotPlain, | |
737 | IsWordPart: false, | 828 | IsWordPart: false, | |
738 | }) | 829 | }) | |
739 | 830 | |||
740 | t.CheckOutputEmpty() | 831 | t.CheckOutputEmpty() | |
741 | } | 832 | } | |
742 | 833 | |||
834 | // Any variable that is defined in the pkgsrc infrastructure in mk/**/*.mk is | |||
835 | // considered defined, and no "used but not defined" warning is logged for it. | |||
836 | // | |||
837 | // See Pkgsrc.loadUntypedVars. | |||
743 | func (s *Suite) Test_MkLineChecker_CheckVaruse__defined_in_infrastructure(c *check.C) { | 838 | func (s *Suite) Test_MkLineChecker_CheckVaruse__defined_in_infrastructure(c *check.C) { | |
744 | t := s.Init(c) | 839 | t := s.Init(c) | |
745 | 840 | |||
746 | t.SetupPkgsrc() | 841 | t.SetupPkgsrc() | |
747 | t.SetupVartypes() | 842 | t.SetupVartypes() | |
748 | t.CreateFileLines("mk/deeply/nested/infra.mk", | 843 | t.CreateFileLines("mk/deeply/nested/infra.mk", | |
749 | MkRcsID, | 844 | MkRcsID, | |
750 | "INFRA_VAR?=\tvalue") | 845 | "INFRA_VAR?=\tvalue") | |
751 | G.Pkgsrc.LoadInfrastructure() | 846 | G.Pkgsrc.LoadInfrastructure() | |
752 | mklines := t.SetupFileMkLines("category/package/module.mk", | 847 | mklines := t.SetupFileMkLines("category/package/module.mk", | |
753 | MkRcsID, | 848 | MkRcsID, | |
754 | "do-fetch:", | 849 | "do-fetch:", | |
755 | "\t: ${INFRA_VAR} ${UNDEFINED}") | 850 | "\t: ${INFRA_VAR} ${UNDEFINED}") | |
@@ -786,70 +881,72 @@ func (s *Suite) Test_MkLineChecker_Check | @@ -786,70 +881,72 @@ func (s *Suite) Test_MkLineChecker_Check | |||
786 | 881 | |||
787 | func (s *Suite) Test_MkLineChecker_CheckVaruse__complicated_range(c *check.C) { | 882 | func (s *Suite) Test_MkLineChecker_CheckVaruse__complicated_range(c *check.C) { | |
788 | t := s.Init(c) | 883 | t := s.Init(c) | |
789 | 884 | |||
790 | t.SetupCommandLine("--show-autofix", "--source") | 885 | t.SetupCommandLine("--show-autofix", "--source") | |
791 | t.SetupVartypes() | 886 | t.SetupVartypes() | |
792 | mkline := t.NewMkLine("mk/compiler/gcc.mk", 150, | 887 | mkline := t.NewMkLine("mk/compiler/gcc.mk", 150, | |
793 | "CC:=\t${CC:C/^/_asdf_/1:M_asdf_*:S/^_asdf_//}") | 888 | "CC:=\t${CC:C/^/_asdf_/1:M_asdf_*:S/^_asdf_//}") | |
794 | 889 | |||
795 | MkLineChecker{mkline}.Check() | 890 | MkLineChecker{mkline}.Check() | |
796 | 891 | |||
797 | // FIXME: The check is called two times, even though it only produces a single NOTE. | 892 | // FIXME: The check is called two times, even though it only produces a single NOTE. | |
798 | t.CheckOutputLines( | 893 | t.CheckOutputLines( | |
799 | "NOTE: mk/compiler/gcc.mk:150: The modifier \":C/^/_asdf_/1:M_asdf_*:S/^_asdf_//\" can be written as \":[1]\".", | 894 | "NOTE: mk/compiler/gcc.mk:150: "+ | |
800 | "AUTOFIX: mk/compiler/gcc.mk:150: Replacing \":C/^/_asdf_/1:M_asdf_*:S/^_asdf_//\" with \":[1]\".", | 895 | "The modifier \":C/^/_asdf_/1:M_asdf_*:S/^_asdf_//\" can be written as \":[1]\".", | |
896 | "AUTOFIX: mk/compiler/gcc.mk:150: "+ | |||
897 | "Replacing \":C/^/_asdf_/1:M_asdf_*:S/^_asdf_//\" with \":[1]\".", | |||
801 | "-\tCC:=\t${CC:C/^/_asdf_/1:M_asdf_*:S/^_asdf_//}", | 898 | "-\tCC:=\t${CC:C/^/_asdf_/1:M_asdf_*:S/^_asdf_//}", | |
802 | "+\tCC:=\t${CC:[1]}") | 899 | "+\tCC:=\t${CC:[1]}") | |
803 | } | 900 | } | |
804 | 901 | |||
805 | func (s *Suite) Test_MkLineChecker_CheckVaruse__deprecated_PKG_DEBUG(c *check.C) { | 902 | func (s *Suite) Test_MkLineChecker_CheckVaruse__deprecated_PKG_DEBUG(c *check.C) { | |
806 | t := s.Init(c) | 903 | t := s.Init(c) | |
807 | 904 | |||
808 | t.SetupVartypes() | 905 | t.SetupVartypes() | |
809 | G.Pkgsrc.initDeprecatedVars() | 906 | G.Pkgsrc.initDeprecatedVars() | |
810 | 907 | |||
811 | mkline := t.NewMkLine("module.mk", 123, | 908 | mkline := t.NewMkLine("module.mk", 123, | |
812 | "\t${_PKG_SILENT}${_PKG_DEBUG} :") | 909 | "\t${_PKG_SILENT}${_PKG_DEBUG} :") | |
813 | 910 | |||
814 | MkLineChecker{mkline}.Check() | 911 | MkLineChecker{mkline}.Check() | |
815 | 912 | |||
816 | t.CheckOutputLines( | 913 | t.CheckOutputLines( | |
817 | "WARN: module.mk:123: Use of \"_PKG_SILENT\" is deprecated. Use RUN (with more error checking) instead.", | 914 | "WARN: module.mk:123: Use of \"_PKG_SILENT\" is deprecated. Use RUN (with more error checking) instead.", | |
818 | "WARN: module.mk:123: Use of \"_PKG_DEBUG\" is deprecated. Use RUN (with more error checking) instead.") | 915 | "WARN: module.mk:123: Use of \"_PKG_DEBUG\" is deprecated. Use RUN (with more error checking) instead.") | |
819 | } | 916 | } | |
820 | 917 | |||
821 | func (s *Suite) Test_MkLineChecker_checkVarassignSpecific(c *check.C) { | 918 | func (s *Suite) Test_MkLineChecker_checkVarassignSpecific(c *check.C) { | |
822 | t := s.Init(c) | 919 | t := s.Init(c) | |
823 | 920 | |||
824 | t.SetupPkgsrc() | 921 | t.SetupPkgsrc() | |
825 | G.Pkgsrc.LoadInfrastructure() | 922 | G.Pkgsrc.LoadInfrastructure() | |
826 | ||||
827 | t.SetupCommandLine("-Wall,no-space") | 923 | t.SetupCommandLine("-Wall,no-space") | |
828 | t.SetupVartypes() | 924 | t.SetupVartypes() | |
829 | mklines := t.SetupFileMkLines("module.mk", | 925 | mklines := t.SetupFileMkLines("module.mk", | |
830 | MkRcsID, | 926 | MkRcsID, | |
831 | "EGDIR= ${PREFIX}/etc/rc.d", | 927 | "EGDIR= ${PREFIX}/etc/rc.d", | |
832 | "_TOOLS_VARNAME.sed= SED", | 928 | "_TOOLS_VARNAME.sed= SED", | |
833 | "DIST_SUBDIR= ${PKGNAME}", | 929 | "DIST_SUBDIR= ${PKGNAME}", | |
834 | "WRKSRC= ${PKGNAME}", | 930 | "WRKSRC= ${PKGNAME}", | |
835 | "SITES_distfile.tar.gz= ${MASTER_SITE_GITHUB:=user/}", | 931 | "SITES_distfile.tar.gz= ${MASTER_SITE_GITHUB:=user/}", | |
836 | // TODO: The first of the below assignments should be flagged as redundant by RedundantScope; | 932 | // TODO: The first of the below assignments should be flagged as redundant by RedundantScope; | |
837 | // that check is currently only implemented for package Makefiles, not for other files. | 933 | // that check is currently only implemented for package Makefiles, not for other files. | |
838 | "PYTHON_VERSIONS_ACCEPTED= -13", | 934 | "PYTHON_VERSIONS_ACCEPTED= -13", | |
839 | "PYTHON_VERSIONS_ACCEPTED= 27 36") | 935 | "PYTHON_VERSIONS_ACCEPTED= 27 36") | |
840 | 936 | |||
841 | mklines.Check() | 937 | mklines.Check() | |
842 | 938 | |||
939 | // TODO: Split this test into several, one for each topic. | |||
843 | t.CheckOutputLines( | 940 | t.CheckOutputLines( | |
844 | "WARN: ~/module.mk:2: Please use the RCD_SCRIPTS mechanism to install rc.d scripts automatically to ${RCD_SCRIPTS_EXAMPLEDIR}.", | 941 | "WARN: ~/module.mk:2: Please use the RCD_SCRIPTS mechanism to install rc.d scripts automatically to ${RCD_SCRIPTS_EXAMPLEDIR}.", | |
845 | "WARN: ~/module.mk:3: _TOOLS_VARNAME.sed is defined but not used.", | 942 | "WARN: ~/module.mk:3: _TOOLS_VARNAME.sed is defined but not used.", | |
846 | "WARN: ~/module.mk:3: Variable names starting with an underscore (_TOOLS_VARNAME.sed) are reserved for internal pkgsrc use.", | 943 | "WARN: ~/module.mk:3: Variable names starting with an underscore (_TOOLS_VARNAME.sed) are reserved for internal pkgsrc use.", | |
847 | "WARN: ~/module.mk:4: PKGNAME should not be used in DIST_SUBDIR as it includes the PKGREVISION. Please use PKGNAME_NOREV instead.", | 944 | "WARN: ~/module.mk:4: PKGNAME should not be used in DIST_SUBDIR as it includes the PKGREVISION. Please use PKGNAME_NOREV instead.", | |
848 | "WARN: ~/module.mk:5: PKGNAME should not be used in WRKSRC as it includes the PKGREVISION. Please use PKGNAME_NOREV instead.", | 945 | "WARN: ~/module.mk:5: PKGNAME should not be used in WRKSRC as it includes the PKGREVISION. Please use PKGNAME_NOREV instead.", | |
849 | "WARN: ~/module.mk:6: SITES_distfile.tar.gz is defined but not used.", | 946 | "WARN: ~/module.mk:6: SITES_distfile.tar.gz is defined but not used.", | |
850 | "WARN: ~/module.mk:6: SITES_* is deprecated. Please use SITES.* instead.", | 947 | "WARN: ~/module.mk:6: SITES_* is deprecated. Please use SITES.* instead.", | |
851 | "WARN: ~/module.mk:7: The variable PYTHON_VERSIONS_ACCEPTED may not be set "+ | 948 | "WARN: ~/module.mk:7: The variable PYTHON_VERSIONS_ACCEPTED may not be set "+ | |
852 | "(only given a default value, appended to) in this file; it would be ok in Makefile, Makefile.common, options.mk.", | 949 | "(only given a default value, appended to) in this file; it would be ok in Makefile, Makefile.common, options.mk.", | |
853 | "WARN: ~/module.mk:7: Invalid version number \"-13\".", | 950 | "WARN: ~/module.mk:7: Invalid version number \"-13\".", | |
854 | "ERROR: ~/module.mk:7: All values for PYTHON_VERSIONS_ACCEPTED must be positive integers.", | 951 | "ERROR: ~/module.mk:7: All values for PYTHON_VERSIONS_ACCEPTED must be positive integers.", | |
855 | "WARN: ~/module.mk:8: The variable PYTHON_VERSIONS_ACCEPTED may not be set "+ | 952 | "WARN: ~/module.mk:8: The variable PYTHON_VERSIONS_ACCEPTED may not be set "+ | |
@@ -922,15 +1019,16 @@ func (s *Suite) Test_MkLineChecker_Check | @@ -922,15 +1019,16 @@ func (s *Suite) Test_MkLineChecker_Check | |||
922 | ".include \"../../lang/${LATEST_PYTHON}/module.mk\"", | 1019 | ".include \"../../lang/${LATEST_PYTHON}/module.mk\"", | |
923 | "", | 1020 | "", | |
924 | ".include \"module.mk\"", | 1021 | ".include \"module.mk\"", | |
925 | ".include \"../../category/../category/package/module.mk\"", // Oops | 1022 | ".include \"../../category/../category/package/module.mk\"", // Oops | |
926 | ".include \"../../mk/bsd.prefs.mk\"", | 1023 | ".include \"../../mk/bsd.prefs.mk\"", | |
927 | ".include \"../package/module.mk\"") | 1024 | ".include \"../package/module.mk\"") | |
928 | 1025 | |||
929 | mklines.Check() | 1026 | mklines.Check() | |
930 | 1027 | |||
931 | t.CheckOutputLines( | 1028 | t.CheckOutputLines( | |
932 | "ERROR: ~/category/package/module.mk:2: A main pkgsrc package must not depend on a pkgsrc-wip package.", | 1029 | "ERROR: ~/category/package/module.mk:2: A main pkgsrc package must not depend on a pkgsrc-wip package.", | |
933 | "ERROR: ~/category/package/module.mk:3: A main pkgsrc package must not depend on a pkgsrc-wip package.", | 1030 | "ERROR: ~/category/package/module.mk:3: A main pkgsrc package must not depend on a pkgsrc-wip package.", | |
934 | "WARN: ~/category/package/module.mk:5: LATEST_PYTHON is used but not defined.", | 1031 | "WARN: ~/category/package/module.mk:5: LATEST_PYTHON is used but not defined.", | |
1032 | // TODO: This warning is unspecific, there is also a pkglint warning "should be ../../category/package". | |||
935 | "WARN: ~/category/package/module.mk:11: Invalid relative path \"../package/module.mk\".") | 1033 | "WARN: ~/category/package/module.mk:11: Invalid relative path \"../package/module.mk\".") | |
936 | } | 1034 | } |
@@ -1,147 +1,165 @@ | @@ -1,147 +1,165 @@ | |||
1 | package main | 1 | package main | |
2 | 2 | |||
3 | import ( | 3 | import ( | |
4 | "strings" | 4 | "strings" | |
5 | ) | 5 | ) | |
6 | 6 | |||
7 | // MkLines contains data for the Makefile (or *.mk) that is currently checked. | 7 | // MkLines contains data for the Makefile (or *.mk) that is currently checked. | |
8 | type MkLines = *MkLinesImpl | 8 | type MkLines = *MkLinesImpl | |
9 | 9 | |||
10 | type MkLinesImpl struct { | 10 | type MkLinesImpl struct { | |
11 | mklines []MkLine | 11 | mklines []MkLine | |
12 | lines Lines | 12 | lines Lines | |
13 | forVars map[string]bool // The variables currently used in .for loops | 13 | target string // Current make(1) target; only available during checkAll | |
14 | target string // Current make(1) target | 14 | vars Scope // | |
15 | vars Scope | |||
16 | buildDefs map[string]bool // Variables that are registered in BUILD_DEFS, to ensure that all user-defined variables are added to it. | 15 | buildDefs map[string]bool // Variables that are registered in BUILD_DEFS, to ensure that all user-defined variables are added to it. | |
17 | plistVarAdded map[string]MkLine // Identifiers that are added to PLIST_VARS. | 16 | plistVarAdded map[string]MkLine // Identifiers that are added to PLIST_VARS. | |
18 | plistVarSet map[string]MkLine // Identifiers for which PLIST.${id} is defined. | 17 | plistVarSet map[string]MkLine // Identifiers for which PLIST.${id} is defined. | |
19 | plistVarSkip bool // True if any of the PLIST_VARS identifiers refers to a variable. | 18 | plistVarSkip bool // True if any of the PLIST_VARS identifiers refers to a variable. | |
20 | Tools *Tools // Tools defined in file scope. | 19 | Tools *Tools // Tools defined in file scope. | |
21 | indentation *Indentation // Indentation depth of preprocessing directives; only available during MkLines.ForEach. | 20 | indentation *Indentation // Indentation depth of preprocessing directives; only available during MkLines.ForEach. | |
21 | forVars map[string]bool // The variables currently used in .for loops; only available during MkLines.checkAll. | |||
22 | Once | 22 | Once | |
23 | ||||
24 | // TODO: Consider extracting plistVarAdded, plistVarSet, plistVarSkip into an own type. | |||
25 | // TODO: Describe where each of the above fields is valid. | |||
23 | } | 26 | } | |
24 | 27 | |||
25 | func NewMkLines(lines Lines) MkLines { | 28 | func NewMkLines(lines Lines) MkLines { | |
26 | mklines := make([]MkLine, lines.Len()) | 29 | mklines := make([]MkLine, lines.Len()) | |
27 | for i, line := range lines.Lines { | 30 | for i, line := range lines.Lines { | |
28 | mklines[i] = NewMkLine(line) | 31 | mklines[i] = NewMkLine(line) | |
29 | } | 32 | } | |
30 | 33 | |||
31 | tools := NewTools(lines.FileName) | 34 | tools := NewTools(lines.FileName) | |
32 | tools.Fallback(G.Pkgsrc.Tools) | 35 | tools.Fallback(G.Pkgsrc.Tools) | |
33 | 36 | |||
34 | return &MkLinesImpl{ | 37 | return &MkLinesImpl{ | |
35 | mklines, | 38 | mklines, | |
36 | lines, | 39 | lines, | |
37 | make(map[string]bool), | |||
38 | "", | 40 | "", | |
39 | NewScope(), | 41 | NewScope(), | |
40 | make(map[string]bool), | 42 | make(map[string]bool), | |
41 | make(map[string]MkLine), | 43 | make(map[string]MkLine), | |
42 | make(map[string]MkLine), | 44 | make(map[string]MkLine), | |
43 | false, | 45 | false, | |
44 | tools, | 46 | tools, | |
45 | nil, | 47 | nil, | |
48 | make(map[string]bool), | |||
46 | Once{}} | 49 | Once{}} | |
47 | } | 50 | } | |
48 | 51 | |||
52 | // TODO: Consider defining an interface MkLinesChecker (different name, though, since this one confuses even me) | |||
53 | // that checks a single topic, like: | |||
54 | // | |||
55 | // * PlistVars | |||
56 | // * ForLoops | |||
57 | // * MakeTargets | |||
58 | // * Tools | |||
59 | // * Indentation | |||
60 | // * LoadTimeVarUse | |||
61 | // * Subst | |||
62 | // * VarAlign | |||
63 | // | |||
64 | // These could be run in parallel to get the diagnostics strictly from top to bottom. | |||
65 | // Some of the checkers will probably depend on one another. | |||
66 | // | |||
67 | // The driving code for these checkers could look like: | |||
68 | // | |||
69 | // ck.Init | |||
70 | // ck.BeforeLine | |||
71 | // ck.Line | |||
72 | // ck.AfterLine | |||
73 | // ck.Finish | |||
74 | ||||
75 | // UseVar remembers that the given variable is used in the given line. | |||
76 | // This controls the "defined but not used" warning. | |||
49 | func (mklines *MkLinesImpl) UseVar(mkline MkLine, varname string) { | 77 | func (mklines *MkLinesImpl) UseVar(mkline MkLine, varname string) { | |
50 | mklines.vars.Use(varname, mkline) | 78 | mklines.vars.Use(varname, mkline) | |
51 | if G.Pkg != nil { | 79 | if G.Pkg != nil { | |
52 | G.Pkg.vars.Use(varname, mkline) | 80 | G.Pkg.vars.Use(varname, mkline) | |
53 | } | 81 | } | |
54 | } | 82 | } | |
55 | 83 | |||
56 | func (mklines *MkLinesImpl) Check() { | 84 | func (mklines *MkLinesImpl) Check() { | |
57 | if trace.Tracing { | 85 | if trace.Tracing { | |
58 | defer trace.Call1(mklines.lines.FileName)() | 86 | defer trace.Call1(mklines.lines.FileName)() | |
59 | } | 87 | } | |
60 | 88 | |||
61 | G.Mk = mklines | 89 | G.Mk = mklines | |
62 | defer func() { G.Mk = nil }() | 90 | defer func() { G.Mk = nil }() | |
63 | 91 | |||
64 | // In the first pass, all additions to BUILD_DEFS and USE_TOOLS | 92 | // In the first pass, all additions to BUILD_DEFS and USE_TOOLS | |
65 | // are collected to make the order of the definitions irrelevant. | 93 | // are collected to make the order of the definitions irrelevant. | |
66 | mklines.DetermineUsedVariables() | 94 | mklines.collectUsedVariables() | |
67 | mklines.DetermineDefinedVariables() | 95 | mklines.collectDefinedVariables() | |
68 | mklines.collectPlistVars() | 96 | mklines.collectPlistVars() | |
69 | mklines.collectElse() | 97 | mklines.collectElse() | |
70 | 98 | |||
71 | // In the second pass, the actual checks are done. | 99 | // In the second pass, the actual checks are done. | |
72 | mklines.checkAll() | 100 | mklines.checkAll() | |
73 | 101 | |||
74 | SaveAutofixChanges(mklines.lines) | 102 | SaveAutofixChanges(mklines.lines) | |
75 | } | 103 | } | |
76 | 104 | |||
77 | func (mklines *MkLinesImpl) checkAll() { | 105 | func (mklines *MkLinesImpl) checkAll() { | |
78 | allowedTargets := func() map[string]bool { | 106 | allowedTargets := map[string]bool{ | |
79 | targets := make(map[string]bool) | 107 | "pre-fetch": true, "do-fetch": true, "post-fetch": true, | |
80 | prefixes := [...]string{"pre", "do", "post"} | 108 | "pre-extract": true, "do-extract": true, "post-extract": true, | |
81 | actions := [...]string{"fetch", "extract", "patch", "tools", "wrapper", "configure", "build", "test", "install", "package", "clean"} | 109 | "pre-patch": true, "do-patch": true, "post-patch": true, | |
82 | for _, prefix := range prefixes { | 110 | "pre-tools": true, "do-tools": true, "post-tools": true, | |
83 | for _, action := range actions { | 111 | "pre-wrapper": true, "do-wrapper": true, "post-wrapper": true, | |
84 | targets[prefix+"-"+action] = true | 112 | "pre-configure": true, "do-configure": true, "post-configure": true, | |
85 | } | 113 | "pre-build": true, "do-build": true, "post-build": true, | |
86 | } | 114 | "pre-test": true, "do-test": true, "post-test": true, | |
87 | return targets | 115 | "pre-install": true, "do-install": true, "post-install": true, | |
88 | }() | 116 | "pre-package": true, "do-package": true, "post-package": true, | |
117 | "pre-clean": true, "do-clean": true, "post-clean": true} | |||
118 | G.Assertf(len(allowedTargets) == 33, "Error in allowedTargets initialization") | |||
89 | 119 | |||
90 | mklines.lines.CheckRcsID(0, `#[\t ]+`, "# ") | 120 | mklines.lines.CheckRcsID(0, `#[\t ]+`, "# ") | |
91 | 121 | |||
92 | substContext := NewSubstContext() | 122 | substContext := NewSubstContext() | |
93 | var varalign VaralignBlock | 123 | var varalign VaralignBlock | |
94 | isHacksMk := mklines.lines.BaseName == "hacks.mk" | 124 | isHacksMk := mklines.lines.BaseName == "hacks.mk" | |
95 | 125 | |||
96 | lineAction := func(mkline MkLine) bool { | 126 | lineAction := func(mkline MkLine) bool { | |
97 | if isHacksMk { | 127 | if isHacksMk { | |
128 | // Needs to be set here because it is reset in MkLines.ForEach. | |||
98 | mklines.Tools.SeenPrefs = true | 129 | mklines.Tools.SeenPrefs = true | |
99 | } | 130 | } | |
100 | 131 | |||
101 | ck := MkLineChecker{mkline} | 132 | ck := MkLineChecker{mkline} | |
102 | ck.Check() | 133 | ck.Check() | |
103 | varalign.Check(mkline) | 134 | ||
135 | varalign.Process(mkline) | |||
104 | mklines.Tools.ParseToolLine(mkline, false, false) | 136 | mklines.Tools.ParseToolLine(mkline, false, false) | |
105 | 137 | |||
106 | switch { | 138 | switch { | |
107 | case mkline.IsEmpty(): | 139 | case mkline.IsEmpty(): | |
108 | substContext.Finish(mkline) | 140 | substContext.Finish(mkline) | |
109 | 141 | |||
110 | case mkline.IsVarassign(): | 142 | case mkline.IsVarassign(): | |
111 | mklines.target = "" | 143 | mklines.target = "" | |
112 | mkline.Tokenize(mkline.Value(), true) // Just for the side-effect of the warnings. | 144 | mkline.Tokenize(mkline.Value(), true) // Just for the side-effect of the warnings. | |
113 | substContext.Varassign(mkline) | 145 | substContext.Varassign(mkline) | |
114 | 146 | |||
115 | switch mkline.Varcanon() { | 147 | mklines.checkVarassignPlist(mkline) | |
116 | case "PLIST_VARS": | |||
117 | ids := mkline.ValueFields(resolveVariableRefs(mkline.Value())) | |||
118 | for _, id := range ids { | |||
119 | if !mklines.plistVarSkip && mklines.plistVarSet[id] == nil { | |||
120 | mkline.Warnf("%q is added to PLIST_VARS, but PLIST.%s is not defined in this file.", id, id) | |||
121 | } | |||
122 | } | |||
123 | ||||
124 | case "PLIST.*": | |||
125 | id := mkline.Varparam() | |||
126 | if !mklines.plistVarSkip && mklines.plistVarAdded[id] == nil { | |||
127 | mkline.Warnf("PLIST.%s is defined, but %q is not added to PLIST_VARS in this file.", id, id) | |||
128 | } | |||
129 | } | |||
130 | 148 | |||
131 | case mkline.IsInclude(): | 149 | case mkline.IsInclude(): | |
132 | mklines.target = "" | 150 | mklines.target = "" | |
133 | if G.Pkg != nil { | 151 | if G.Pkg != nil { | |
134 | G.Pkg.CheckInclude(mkline, mklines.indentation) | 152 | G.Pkg.checkIncludeConditionally(mkline, mklines.indentation) | |
135 | } | 153 | } | |
136 | 154 | |||
137 | case mkline.IsDirective(): | 155 | case mkline.IsDirective(): | |
138 | ck.checkDirective(mklines.forVars, mklines.indentation) | 156 | ck.checkDirective(mklines.forVars, mklines.indentation) | |
139 | substContext.Directive(mkline) | 157 | substContext.Directive(mkline) | |
140 | 158 | |||
141 | case mkline.IsDependency(): | 159 | case mkline.IsDependency(): | |
142 | ck.checkDependencyRule(allowedTargets) | 160 | ck.checkDependencyRule(allowedTargets) | |
143 | mklines.target = mkline.Targets() | 161 | mklines.target = mkline.Targets() | |
144 | 162 | |||
145 | case mkline.IsShellCommand(): | 163 | case mkline.IsShellCommand(): | |
146 | mkline.Tokenize(mkline.ShellCommand(), true) // Just for the side-effect of the warnings. | 164 | mkline.Tokenize(mkline.ShellCommand(), true) // Just for the side-effect of the warnings. | |
147 | } | 165 | } | |
@@ -155,26 +173,43 @@ func (mklines *MkLinesImpl) checkAll() { | @@ -155,26 +173,43 @@ func (mklines *MkLinesImpl) checkAll() { | |||
155 | 173 | |||
156 | // TODO: Extract this code so that it is clearly visible in the stack trace. | 174 | // TODO: Extract this code so that it is clearly visible in the stack trace. | |
157 | if trace.Tracing { | 175 | if trace.Tracing { | |
158 | trace.Stepf("Starting main checking loop") | 176 | trace.Stepf("Starting main checking loop") | |
159 | } | 177 | } | |
160 | mklines.ForEachEnd(lineAction, atEnd) | 178 | mklines.ForEachEnd(lineAction, atEnd) | |
161 | 179 | |||
162 | substContext.Finish(NewMkLine(mklines.lines.EOFLine())) // TODO: mklines.EOFLine() | 180 | substContext.Finish(NewMkLine(mklines.lines.EOFLine())) // TODO: mklines.EOFLine() | |
163 | varalign.Finish() | 181 | varalign.Finish() | |
164 | 182 | |||
165 | ChecklinesTrailingEmptyLines(mklines.lines) | 183 | ChecklinesTrailingEmptyLines(mklines.lines) | |
166 | } | 184 | } | |
167 | 185 | |||
186 | func (mklines *MkLinesImpl) checkVarassignPlist(mkline MkLine) { | |||
187 | switch mkline.Varcanon() { | |||
188 | case "PLIST_VARS": | |||
189 | for _, id := range mkline.ValueFields(resolveVariableRefs(mkline.Value())) { | |||
190 | if !mklines.plistVarSkip && mklines.plistVarSet[id] == nil { | |||
191 | mkline.Warnf("%q is added to PLIST_VARS, but PLIST.%s is not defined in this file.", id, id) | |||
192 | } | |||
193 | } | |||
194 | ||||
195 | case "PLIST.*": | |||
196 | id := mkline.Varparam() | |||
197 | if !mklines.plistVarSkip && mklines.plistVarAdded[id] == nil { | |||
198 | mkline.Warnf("PLIST.%s is defined, but %q is not added to PLIST_VARS in this file.", id, id) | |||
199 | } | |||
200 | } | |||
201 | } | |||
202 | ||||
168 | // ForEach calls the action for each line, until the action returns false. | 203 | // ForEach calls the action for each line, until the action returns false. | |
169 | // It keeps track of the indentation (see MkLines.indentation) | 204 | // It keeps track of the indentation (see MkLines.indentation) | |
170 | // and all conditional variables (see Indentation.IsConditional). | 205 | // and all conditional variables (see Indentation.IsConditional). | |
171 | func (mklines *MkLinesImpl) ForEach(action func(mkline MkLine)) { | 206 | func (mklines *MkLinesImpl) ForEach(action func(mkline MkLine)) { | |
172 | mklines.ForEachEnd( | 207 | mklines.ForEachEnd( | |
173 | func(mkline MkLine) bool { action(mkline); return true }, | 208 | func(mkline MkLine) bool { action(mkline); return true }, | |
174 | func(mkline MkLine) {}) | 209 | func(mkline MkLine) {}) | |
175 | } | 210 | } | |
176 | 211 | |||
177 | // ForEachEnd calls the action for each line, until the action returns false. | 212 | // ForEachEnd calls the action for each line, until the action returns false. | |
178 | // It keeps track of the indentation and all conditional variables. | 213 | // It keeps track of the indentation and all conditional variables. | |
179 | // At the end, atEnd is called with the last line as its argument. | 214 | // At the end, atEnd is called with the last line as its argument. | |
180 | func (mklines *MkLinesImpl) ForEachEnd(action func(mkline MkLine) bool, atEnd func(lastMkline MkLine)) { | 215 | func (mklines *MkLinesImpl) ForEachEnd(action func(mkline MkLine) bool, atEnd func(lastMkline MkLine)) { | |
@@ -189,133 +224,137 @@ func (mklines *MkLinesImpl) ForEachEnd(a | @@ -189,133 +224,137 @@ func (mklines *MkLinesImpl) ForEachEnd(a | |||
189 | 224 | |||
190 | for _, mkline := range mklines.mklines { | 225 | for _, mkline := range mklines.mklines { | |
191 | mklines.indentation.TrackBefore(mkline) | 226 | mklines.indentation.TrackBefore(mkline) | |
192 | if !action(mkline) { | 227 | if !action(mkline) { | |
193 | break | 228 | break | |
194 | } | 229 | } | |
195 | mklines.indentation.TrackAfter(mkline) | 230 | mklines.indentation.TrackAfter(mkline) | |
196 | } | 231 | } | |
197 | 232 | |||
198 | atEnd(mklines.mklines[len(mklines.mklines)-1]) | 233 | atEnd(mklines.mklines[len(mklines.mklines)-1]) | |
199 | mklines.indentation = nil | 234 | mklines.indentation = nil | |
200 | } | 235 | } | |
201 | 236 | |||
202 | func (mklines *MkLinesImpl) DetermineDefinedVariables() { | 237 | func (mklines *MkLinesImpl) collectDefinedVariables() { | |
203 | if trace.Tracing { | 238 | if trace.Tracing { | |
204 | defer trace.Call0()() | 239 | defer trace.Call0()() | |
205 | } | 240 | } | |
206 | 241 | |||
207 | for _, mkline := range mklines.mklines { | 242 | for _, mkline := range mklines.mklines { | |
208 | mklines.Tools.ParseToolLine(mkline, false, true) | 243 | mklines.Tools.ParseToolLine(mkline, false, true) | |
209 | 244 | |||
210 | if !mkline.IsVarassign() { | 245 | if !mkline.IsVarassign() { | |
211 | continue | 246 | continue | |
212 | } | 247 | } | |
213 | 248 | |||
214 | defineVar(mkline, mkline.Varname()) | 249 | defineVar(mkline, mkline.Varname()) | |
215 | 250 | |||
216 | varcanon := mkline.Varcanon() | 251 | varcanon := mkline.Varcanon() | |
217 | switch varcanon { | 252 | switch varcanon { | |
218 | case | 253 | case | |
219 | "BUILD_DEFS", | 254 | "BUILD_DEFS", | |
220 | "PKG_GROUPS_VARS", | 255 | "PKG_GROUPS_VARS", // see mk/misc/unprivileged.mk | |
221 | "PKG_USERS_VARS": | 256 | "PKG_USERS_VARS": // see mk/misc/unprivileged.mk | |
222 | for _, varname := range fields(mkline.Value()) { | 257 | for _, varname := range mkline.Fields() { | |
223 | mklines.buildDefs[varname] = true | 258 | mklines.buildDefs[varname] = true | |
224 | if trace.Tracing { | 259 | if trace.Tracing { | |
225 | trace.Step1("%q is added to BUILD_DEFS.", varname) | 260 | trace.Step1("%q is added to BUILD_DEFS.", varname) | |
226 | } | 261 | } | |
227 | } | 262 | } | |
228 | 263 | |||
229 | case | 264 | case | |
230 | "BUILTIN_FIND_FILES_VAR", | 265 | "BUILTIN_FIND_FILES_VAR", | |
231 | "BUILTIN_FIND_HEADERS_VAR": | 266 | "BUILTIN_FIND_HEADERS_VAR": | |
232 | for _, varname := range fields(mkline.Value()) { | 267 | for _, varname := range mkline.Fields() { | |
233 | mklines.vars.Define(varname, mkline) | 268 | mklines.vars.Define(varname, mkline) | |
234 | } | 269 | } | |
235 | 270 | |||
236 | case "PLIST_VARS": | 271 | case "PLIST_VARS": | |
237 | ids := mkline.ValueFields(resolveVariableRefs(mkline.Value())) | 272 | for _, id := range mkline.ValueFields(resolveVariableRefs(mkline.Value())) { | |
238 | for _, id := range ids { | |||
239 | if trace.Tracing { | 273 | if trace.Tracing { | |
240 | trace.Step1("PLIST.%s is added to PLIST_VARS.", id) | 274 | trace.Step1("PLIST.%s is added to PLIST_VARS.", id) | |
241 | } | 275 | } | |
276 | ||||
242 | if containsVarRef(id) { | 277 | if containsVarRef(id) { | |
243 | mklines.UseVar(mkline, "PLIST.*") | 278 | mklines.UseVar(mkline, "PLIST.*") | |
244 | mklines.plistVarSkip = true | 279 | mklines.plistVarSkip = true | |
245 | } else { | 280 | } else { | |
246 | mklines.UseVar(mkline, "PLIST."+id) | 281 | mklines.UseVar(mkline, "PLIST."+id) | |
247 | } | 282 | } | |
248 | } | 283 | } | |
249 | 284 | |||
250 | case "SUBST_VARS.*": | 285 | case "SUBST_VARS.*": | |
251 | for _, svar := range fields(mkline.Value()) { | 286 | for _, substVar := range mkline.Fields() { | |
252 | mklines.UseVar(mkline, varnameCanon(svar)) | 287 | mklines.UseVar(mkline, varnameCanon(substVar)) | |
253 | if trace.Tracing { | 288 | if trace.Tracing { | |
254 | trace.Step1("varuse %s", svar) | 289 | trace.Step1("varuse %s", substVar) | |
255 | } | 290 | } | |
256 | } | 291 | } | |
257 | 292 | |||
258 | case "OPSYSVARS": | 293 | case "OPSYSVARS": | |
259 | for _, osvar := range fields(mkline.Value()) { | 294 | for _, opsysVar := range mkline.Fields() { | |
260 | mklines.UseVar(mkline, osvar+".*") | 295 | mklines.UseVar(mkline, opsysVar+".*") | |
261 | defineVar(mkline, osvar) | 296 | defineVar(mkline, opsysVar) | |
262 | } | 297 | } | |
263 | } | 298 | } | |
264 | } | 299 | } | |
265 | } | 300 | } | |
266 | 301 | |||
267 | func (mklines *MkLinesImpl) collectPlistVars() { | 302 | func (mklines *MkLinesImpl) collectPlistVars() { | |
303 | // TODO: The PLIST_VARS code above looks very similar. | |||
268 | for _, mkline := range mklines.mklines { | 304 | for _, mkline := range mklines.mklines { | |
269 | if mkline.IsVarassign() { | 305 | if mkline.IsVarassign() { | |
270 | switch mkline.Varcanon() { | 306 | switch mkline.Varcanon() { | |
271 | case "PLIST_VARS": | 307 | case "PLIST_VARS": | |
272 | ids := mkline.ValueFields(resolveVariableRefs(mkline.Value())) | 308 | for _, id := range mkline.ValueFields(resolveVariableRefs(mkline.Value())) { | |
273 | for _, id := range ids { | |||
274 | if containsVarRef(id) { | 309 | if containsVarRef(id) { | |
275 | mklines.plistVarSkip = true | 310 | mklines.plistVarSkip = true | |
276 | } else { | 311 | } else { | |
277 | mklines.plistVarAdded[id] = mkline | 312 | mklines.plistVarAdded[id] = mkline | |
278 | } | 313 | } | |
279 | } | 314 | } | |
280 | case "PLIST.*": | 315 | case "PLIST.*": | |
281 | id := mkline.Varparam() | 316 | id := mkline.Varparam() | |
282 | if containsVarRef(id) { | 317 | if containsVarRef(id) { | |
283 | mklines.plistVarSkip = true | 318 | mklines.plistVarSkip = true | |
284 | } else { | 319 | } else { | |
285 | mklines.plistVarSet[id] = mkline | 320 | mklines.plistVarSet[id] = mkline | |
286 | } | 321 | } | |
287 | } | 322 | } | |
288 | } | 323 | } | |
289 | } | 324 | } | |
290 | } | 325 | } | |
291 | 326 | |||
292 | func (mklines *MkLinesImpl) collectElse() { | 327 | func (mklines *MkLinesImpl) collectElse() { | |
293 | // Make a dry-run over the lines, which sets data.elseLine (in mkline.go) as a side-effect. | 328 | // Make a dry-run over the lines, which sets data.elseLine (in mkline.go) as a side-effect. | |
294 | mklines.ForEach(func(mkline MkLine) {}) | 329 | mklines.ForEach(func(mkline MkLine) {}) | |
330 | // TODO: Check whether this ForEach is redundant because it is already run somewhere else. | |||
295 | } | 331 | } | |
296 | 332 | |||
297 | func (mklines *MkLinesImpl) DetermineUsedVariables() { | 333 | func (mklines *MkLinesImpl) collectUsedVariables() { | |
298 | for _, mkline := range mklines.mklines { | 334 | for _, mkline := range mklines.mklines { | |
299 | for _, varname := range mkline.DetermineUsedVariables() { | 335 | for _, varname := range mkline.DetermineUsedVariables() { | |
300 | mklines.UseVar(mkline, varname) | 336 | mklines.UseVar(mkline, varname) | |
301 | } | 337 | } | |
302 | } | 338 | } | |
303 | 339 | |||
304 | mklines.determineDocumentedVariables() | 340 | mklines.collectDocumentedVariables() | |
305 | } | 341 | } | |
306 | 342 | |||
307 | // Loosely based on mk/help/help.awk, revision 1.28 | 343 | // collectDocumentedVariables collects the variables that are mentioned in the human-readable | |
308 | func (mklines *MkLinesImpl) determineDocumentedVariables() { | 344 | // documentation of the Makefile fragments from the pkgsrc infrastructure. | |
345 | // | |||
346 | // Loosely based on mk/help/help.awk, revision 1.28, but much simpler. | |||
347 | func (mklines *MkLinesImpl) collectDocumentedVariables() { | |||
309 | scope := NewScope() | 348 | scope := NewScope() | |
310 | commentLines := 0 | 349 | commentLines := 0 | |
311 | relevant := true | 350 | relevant := true | |
312 | 351 | |||
313 | finish := func() { | 352 | finish := func() { | |
314 | if commentLines >= 3 && relevant { | 353 | if commentLines >= 3 && relevant { | |
315 | for varname, mkline := range scope.used { | 354 | for varname, mkline := range scope.used { | |
316 | mklines.vars.Use(varname, mkline) | 355 | mklines.vars.Use(varname, mkline) | |
317 | } | 356 | } | |
318 | } | 357 | } | |
319 | 358 | |||
320 | scope = NewScope() | 359 | scope = NewScope() | |
321 | commentLines = 0 | 360 | commentLines = 0 | |
@@ -347,42 +386,45 @@ func (mklines *MkLinesImpl) determineDoc | @@ -347,42 +386,45 @@ func (mklines *MkLinesImpl) determineDoc | |||
347 | 386 | |||
348 | if 1 < len(words) && words[1] == "Copyright" { | 387 | if 1 < len(words) && words[1] == "Copyright" { | |
349 | relevant = false | 388 | relevant = false | |
350 | } | 389 | } | |
351 | 390 | |||
352 | case mkline.IsEmpty(): | 391 | case mkline.IsEmpty(): | |
353 | finish() | 392 | finish() | |
354 | } | 393 | } | |
355 | } | 394 | } | |
356 | 395 | |||
357 | finish() | 396 | finish() | |
358 | } | 397 | } | |
359 | 398 | |||
360 | func (mklines *MkLinesImpl) CheckRedundantVariables() { | 399 | func (mklines *MkLinesImpl) CheckRedundantAssignments() { | |
361 | scope := NewRedundantScope() | 400 | scope := NewRedundantScope() | |
401 | ||||
362 | isRelevant := func(old, new MkLine) bool { | 402 | isRelevant := func(old, new MkLine) bool { | |
363 | if old.Basename != "Makefile" && new.Basename == "Makefile" { | 403 | if old.Basename != "Makefile" && new.Basename == "Makefile" { | |
364 | return false | 404 | return false | |
365 | } | 405 | } | |
366 | if new.Op() == opAssignEval { | 406 | if new.Op() == opAssignEval { | |
367 | return false | 407 | return false | |
368 | } | 408 | } | |
369 | return true | 409 | return true | |
370 | } | 410 | } | |
411 | ||||
371 | scope.OnIgnore = func(old, new MkLine) { | 412 | scope.OnIgnore = func(old, new MkLine) { | |
372 | if isRelevant(old, new) && old.Value() == new.Value() { | 413 | if isRelevant(old, new) && old.Value() == new.Value() { | |
373 | old.Notef("Definition of %s is redundant because of %s.", new.Varname(), old.RefTo(new)) | 414 | old.Notef("Definition of %s is redundant because of %s.", new.Varname(), old.RefTo(new)) | |
374 | } | 415 | } | |
375 | } | 416 | } | |
417 | ||||
376 | scope.OnOverwrite = func(old, new MkLine) { | 418 | scope.OnOverwrite = func(old, new MkLine) { | |
377 | if isRelevant(old, new) { | 419 | if isRelevant(old, new) { | |
378 | old.Warnf("Variable %s is overwritten in %s.", new.Varname(), old.RefTo(new)) | 420 | old.Warnf("Variable %s is overwritten in %s.", new.Varname(), old.RefTo(new)) | |
379 | G.Explain( | 421 | G.Explain( | |
380 | "The variable definition in this line does not have an effect since", | 422 | "The variable definition in this line does not have an effect since", | |
381 | "it is overwritten elsewhere. This typically happens because of a", | 423 | "it is overwritten elsewhere. This typically happens because of a", | |
382 | "typo (writing = instead of +=) or because the line that overwrites", | 424 | "typo (writing = instead of +=) or because the line that overwrites", | |
383 | "is in another file that is used by several packages.") | 425 | "is in another file that is used by several packages.") | |
384 | } | 426 | } | |
385 | } | 427 | } | |
386 | 428 | |||
387 | mklines.ForEach(scope.Handle) | 429 | mklines.ForEach(scope.Handle) | |
388 | } | 430 | } | |
@@ -438,74 +480,72 @@ type VaralignBlock struct { | @@ -438,74 +480,72 @@ type VaralignBlock struct { | |||
438 | infos []*varalignBlockInfo | 480 | infos []*varalignBlockInfo | |
439 | skip bool | 481 | skip bool | |
440 | } | 482 | } | |
441 | 483 | |||
442 | type varalignBlockInfo struct { | 484 | type varalignBlockInfo struct { | |
443 | mkline MkLine | 485 | mkline MkLine | |
444 | varnameOp string // Variable name + assignment operator | 486 | varnameOp string // Variable name + assignment operator | |
445 | varnameOpWidth int // Screen width of varnameOp | 487 | varnameOpWidth int // Screen width of varnameOp | |
446 | space string // Whitespace between varnameOp and the variable value | 488 | space string // Whitespace between varnameOp and the variable value | |
447 | totalWidth int // Screen width of varnameOp + space | 489 | totalWidth int // Screen width of varnameOp + space | |
448 | continuation bool // A continuation line with no value in the first line. | 490 | continuation bool // A continuation line with no value in the first line. | |
449 | } | 491 | } | |
450 | 492 | |||
451 | func (va *VaralignBlock) Check(mkline MkLine) { | 493 | func (va *VaralignBlock) Process(mkline MkLine) { | |
452 | switch { | 494 | switch { | |
453 | case !G.Opts.WarnSpace: | 495 | case !G.Opts.WarnSpace: | |
454 | return | 496 | return | |
455 | 497 | |||
456 | case mkline.IsEmpty(): | 498 | case mkline.IsEmpty(): | |
457 | va.Finish() | 499 | va.Finish() | |
458 | return | 500 | return | |
459 | 501 | |||
460 | case mkline.IsCommentedVarassign(): | 502 | case mkline.IsVarassign(), mkline.IsCommentedVarassign(): | |
461 | break | 503 | va.processVarassign(mkline) | |
462 | 504 | |||
463 | case mkline.IsComment(): | 505 | case mkline.IsComment(), mkline.IsDirective(): | |
464 | return | 506 | return | |
465 | 507 | |||
466 | case mkline.IsDirective(): | 508 | default: | |
467 | return | |||
468 | ||||
469 | case !mkline.IsVarassign(): | |||
470 | trace.Stepf("Skipping") | 509 | trace.Stepf("Skipping") | |
471 | va.skip = true | 510 | va.skip = true | |
472 | return | 511 | return | |
473 | } | 512 | } | |
513 | } | |||
474 | 514 | |||
515 | func (va *VaralignBlock) processVarassign(mkline MkLine) { | |||
475 | switch { | 516 | switch { | |
476 | case mkline.Op() == opAssignEval && matches(mkline.Varname(), `^[a-z]`): | 517 | case mkline.Op() == opAssignEval && matches(mkline.Varname(), `^[a-z]`): | |
477 | // Arguments to procedures do not take part in block alignment. | 518 | // Arguments to procedures do not take part in block alignment. | |
478 | // | 519 | // | |
479 | // Example: | 520 | // Example: | |
480 | // pkgpath := ${PKGPATH} | 521 | // pkgpath := ${PKGPATH} | |
481 | return | 522 | return | |
482 | 523 | |||
483 | case mkline.Value() == "" && mkline.VarassignComment() == "": | 524 | case mkline.Value() == "" && mkline.VarassignComment() == "": | |
484 | // Multiple-inclusion guards usually appear in a block of | 525 | // Multiple-inclusion guards usually appear in a block of | |
485 | // their own and therefore do not need alignment. | 526 | // their own and therefore do not need alignment. | |
486 | // | 527 | // | |
487 | // Example: | 528 | // Example: | |
488 | // .if !defined(INCLUSION_GUARD_MK) | 529 | // .if !defined(INCLUSION_GUARD_MK) | |
489 | // INCLUSION_GUARD_MK:= | 530 | // INCLUSION_GUARD_MK:= | |
490 | // # ... | 531 | // # ... | |
491 | // .endif | 532 | // .endif | |
492 | return | 533 | return | |
493 | } | 534 | } | |
494 | 535 | |||
495 | continuation := false | 536 | continuation := false | |
496 | if mkline.IsMultiline() { | 537 | if mkline.IsMultiline() { | |
497 | // Interpreting the continuation marker as variable value | 538 | // Parsing the continuation marker as variable value is cheating but works well. | |
498 | // is cheating but works well. | |||
499 | text := strings.TrimSuffix(mkline.raw[0].orignl, "\n") | 539 | text := strings.TrimSuffix(mkline.raw[0].orignl, "\n") | |
500 | m, _, _, _, _, _, value, _, _ := MatchVarassign(text) | 540 | m, _, _, _, _, _, value, _, _ := MatchVarassign(text) | |
501 | continuation = m && value == "\\" | 541 | continuation = m && value == "\\" | |
502 | } | 542 | } | |
503 | 543 | |||
504 | valueAlign := mkline.ValueAlign() | 544 | valueAlign := mkline.ValueAlign() | |
505 | varnameOp := strings.TrimRight(valueAlign, " \t") | 545 | varnameOp := strings.TrimRight(valueAlign, " \t") | |
506 | space := valueAlign[len(varnameOp):] | 546 | space := valueAlign[len(varnameOp):] | |
507 | 547 | |||
508 | width := tabWidth(valueAlign) | 548 | width := tabWidth(valueAlign) | |
509 | va.infos = append(va.infos, &varalignBlockInfo{mkline, varnameOp, tabWidth(varnameOp), space, width, continuation}) | 549 | va.infos = append(va.infos, &varalignBlockInfo{mkline, varnameOp, tabWidth(varnameOp), space, width, continuation}) | |
510 | } | 550 | } | |
511 | 551 | |||
@@ -571,34 +611,34 @@ func (va *VaralignBlock) optimalWidth(in | @@ -571,34 +611,34 @@ func (va *VaralignBlock) optimalWidth(in | |||
571 | } | 611 | } | |
572 | 612 | |||
573 | if width := info.totalWidth; info.varnameOpWidth != outlier { | 613 | if width := info.totalWidth; info.varnameOpWidth != outlier { | |
574 | if minTotalWidth == 0 || width < minTotalWidth { | 614 | if minTotalWidth == 0 || width < minTotalWidth { | |
575 | minTotalWidth = width | 615 | minTotalWidth = width | |
576 | } | 616 | } | |
577 | maxTotalWidth = imax(maxTotalWidth, width) | 617 | maxTotalWidth = imax(maxTotalWidth, width) | |
578 | } | 618 | } | |
579 | } | 619 | } | |
580 | 620 | |||
581 | if trace.Tracing { | 621 | if trace.Tracing { | |
582 | trace.Stepf("Indentation including whitespace is between %d and %d.", | 622 | trace.Stepf("Indentation including whitespace is between %d and %d.", | |
583 | minTotalWidth, maxTotalWidth) | 623 | minTotalWidth, maxTotalWidth) | |
584 | trace.Stepf("Minimum required indentation is %d + 1.", | 624 | trace.Stepf("Minimum required indentation is %d + 1.", minVarnameOpWidth) | |
585 | minVarnameOpWidth) | |||
586 | if outlier != 0 { | 625 | if outlier != 0 { | |
587 | trace.Stepf("The outlier is at indentation %d.", outlier) | 626 | trace.Stepf("The outlier is at indentation %d.", outlier) | |
588 | } | 627 | } | |
589 | } | 628 | } | |
590 | 629 | |||
591 | if minTotalWidth > minVarnameOpWidth && minTotalWidth == maxTotalWidth && minTotalWidth%8 == 0 { | 630 | if minTotalWidth > minVarnameOpWidth && minTotalWidth == maxTotalWidth && minTotalWidth%8 == 0 { | |
631 | // The whole paragraph is already indented to the same width. | |||
592 | return minTotalWidth | 632 | return minTotalWidth | |
593 | } | 633 | } | |
594 | 634 | |||
595 | if minVarnameOpWidth == 0 { | 635 | if minVarnameOpWidth == 0 { | |
596 | // Only continuation lines in this paragraph. | 636 | // Only continuation lines in this paragraph. | |
597 | return 0 | 637 | return 0 | |
598 | } | 638 | } | |
599 | 639 | |||
600 | return (minVarnameOpWidth & -8) + 8 | 640 | return (minVarnameOpWidth & -8) + 8 | |
601 | } | 641 | } | |
602 | 642 | |||
603 | func (va *VaralignBlock) realign(mkline MkLine, varnameOp, oldSpace string, continuation bool, newWidth int) { | 643 | func (va *VaralignBlock) realign(mkline MkLine, varnameOp, oldSpace string, continuation bool, newWidth int) { | |
604 | hasSpace := contains(oldSpace, " ") | 644 | hasSpace := contains(oldSpace, " ") | |
@@ -633,35 +673,37 @@ func (va *VaralignBlock) realignInitialL | @@ -633,35 +673,37 @@ func (va *VaralignBlock) realignInitialL | |||
633 | switch { | 673 | switch { | |
634 | case hasSpace && wrongColumn: | 674 | case hasSpace && wrongColumn: | |
635 | fix.Notef("This variable value should be aligned with tabs, not spaces, to column %d.", newWidth+1) | 675 | fix.Notef("This variable value should be aligned with tabs, not spaces, to column %d.", newWidth+1) | |
636 | case hasSpace && oldSpace != newSpace: | 676 | case hasSpace && oldSpace != newSpace: | |
637 | fix.Notef("Variable values should be aligned with tabs, not spaces.") | 677 | fix.Notef("Variable values should be aligned with tabs, not spaces.") | |
638 | case wrongColumn: | 678 | case wrongColumn: | |
639 | fix.Notef("This variable value should be aligned to column %d.", newWidth+1) | 679 | fix.Notef("This variable value should be aligned to column %d.", newWidth+1) | |
640 | default: | 680 | default: | |
641 | return | 681 | return | |
642 | } | 682 | } | |
643 | 683 | |||
644 | if wrongColumn { | 684 | if wrongColumn { | |
645 | fix.Explain( | 685 | fix.Explain( | |
646 | "Normally, all variable values in a block should start at the same", | 686 | "Normally, all variable values in a block should start at the same column.", | |
647 | "column. There are some exceptions to this rule:", | 687 | "This provides orientation, especially for sequences", | |
688 | "of variables that often appear in the same order.", | |||
689 | "For these it suffices to look at the variable values only.", | |||
690 | "", | |||
691 | "There are some exceptions to this rule:", | |||
648 | "", | 692 | "", | |
649 | "Definitions for long variable names may be indented with a single", | 693 | "Definitions for long variable names may be indented with a single space instead of tabs,", | |
650 | "space instead of tabs, but only if they appear in a block that is", | 694 | "but only if they appear in a block that is otherwise indented using tabs.", | |
651 | "otherwise indented using tabs.", | |||
652 | "", | 695 | "", | |
653 | "Variable definitions that span multiple lines are not checked for", | 696 | "Variable definitions that span multiple lines are not checked for alignment at all.", | |
654 | "alignment at all.", | |||
655 | "", | 697 | "", | |
656 | "When the block contains something else than variable definitions", | 698 | "When the block contains something else than variable definitions", | |
657 | "and directives like .if or .for, it is not checked at all.") | 699 | "and directives like .if or .for, it is not checked at all.") | |
658 | } | 700 | } | |
659 | 701 | |||
660 | fix.ReplaceAfter(varnameOp, oldSpace, newSpace) | 702 | fix.ReplaceAfter(varnameOp, oldSpace, newSpace) | |
661 | fix.Apply() | 703 | fix.Apply() | |
662 | } | 704 | } | |
663 | 705 | |||
664 | func (va *VaralignBlock) realignContinuationLines(mkline MkLine, newWidth int) { | 706 | func (va *VaralignBlock) realignContinuationLines(mkline MkLine, newWidth int) { | |
665 | indentation := strings.Repeat("\t", newWidth/8) + strings.Repeat(" ", newWidth%8) | 707 | indentation := strings.Repeat("\t", newWidth/8) + strings.Repeat(" ", newWidth%8) | |
666 | fix := mkline.Autofix() | 708 | fix := mkline.Autofix() | |
667 | fix.Notef("This line should be aligned with %q.", indentation) | 709 | fix.Notef("This line should be aligned with %q.", indentation) |
@@ -30,41 +30,38 @@ func (s *Suite) Test_MkLines_Check__auto | @@ -30,41 +30,38 @@ func (s *Suite) Test_MkLines_Check__auto | |||
30 | t.CheckFileLines("filename.mk", | 30 | t.CheckFileLines("filename.mk", | |
31 | "# $"+"NetBSD$", | 31 | "# $"+"NetBSD$", | |
32 | ".if defined(A)", | 32 | ".if defined(A)", | |
33 | ". for a in ${A}", | 33 | ". for a in ${A}", | |
34 | ". if defined(C)", | 34 | ". if defined(C)", | |
35 | ". endif", | 35 | ". endif", | |
36 | ". endfor", | 36 | ". endfor", | |
37 | ".endif") | 37 | ".endif") | |
38 | } | 38 | } | |
39 | 39 | |||
40 | func (s *Suite) Test_MkLines_Check__unusual_target(c *check.C) { | 40 | func (s *Suite) Test_MkLines_Check__unusual_target(c *check.C) { | |
41 | t := s.Init(c) | 41 | t := s.Init(c) | |
42 | 42 | |||
43 | t.SetupVartypes() | |||
44 | t.SetupTool("cc", "CC", AtRunTime) | |||
43 | mklines := t.NewMkLines("Makefile", | 45 | mklines := t.NewMkLines("Makefile", | |
44 | MkRcsID, | 46 | MkRcsID, | |
45 | "", | 47 | "", | |
46 | "echo: echo.c", | 48 | "echo: echo.c", | |
47 | "\tcc -o ${.TARGET} ${.IMPSRC}") | 49 | "\tcc -o ${.TARGET} ${.IMPSRC}") | |
48 | 50 | |||
49 | mklines.Check() | 51 | mklines.Check() | |
50 | 52 | |||
51 | // FIXME: .TARGET is always defined. | |||
52 | // FIXME: .IMPSRC is always defined. | |||
53 | t.CheckOutputLines( | 53 | t.CheckOutputLines( | |
54 | "WARN: Makefile:3: Unusual target \"echo\".", | 54 | "WARN: Makefile:3: Unusual target \"echo\".") | |
55 | "WARN: Makefile:4: Unknown shell command \"cc\".", | |||
56 | "WARN: Makefile:4: .TARGET is used but not defined.", | |||
57 | "WARN: Makefile:4: .IMPSRC is used but not defined.") | |||
58 | } | 55 | } | |
59 | 56 | |||
60 | func (s *Suite) Test_MkLines__quoting_LDFLAGS_for_GNU_configure(c *check.C) { | 57 | func (s *Suite) Test_MkLines__quoting_LDFLAGS_for_GNU_configure(c *check.C) { | |
61 | t := s.Init(c) | 58 | t := s.Init(c) | |
62 | 59 | |||
63 | t.SetupVartypes() | 60 | t.SetupVartypes() | |
64 | G.Pkg = NewPackage(t.File("category/pkgbase")) | 61 | G.Pkg = NewPackage(t.File("category/pkgbase")) | |
65 | mklines := t.NewMkLines("Makefile", | 62 | mklines := t.NewMkLines("Makefile", | |
66 | MkRcsID, | 63 | MkRcsID, | |
67 | "GNU_CONFIGURE=\tyes", | 64 | "GNU_CONFIGURE=\tyes", | |
68 | "CONFIGURE_ENV+=\tX_LIBS=${X11_LDFLAGS:Q}") | 65 | "CONFIGURE_ENV+=\tX_LIBS=${X11_LDFLAGS:Q}") | |
69 | 66 | |||
70 | mklines.Check() | 67 | mklines.Check() | |
@@ -74,74 +71,79 @@ func (s *Suite) Test_MkLines__quoting_LD | @@ -74,74 +71,79 @@ func (s *Suite) Test_MkLines__quoting_LD | |||
74 | "WARN: Makefile:3: Please use ${X11_LDFLAGS:M*:Q} instead of ${X11_LDFLAGS:Q}.") | 71 | "WARN: Makefile:3: Please use ${X11_LDFLAGS:M*:Q} instead of ${X11_LDFLAGS:Q}.") | |
75 | } | 72 | } | |
76 | 73 | |||
77 | func (s *Suite) Test_MkLines__for_loop_multiple_variables(c *check.C) { | 74 | func (s *Suite) Test_MkLines__for_loop_multiple_variables(c *check.C) { | |
78 | t := s.Init(c) | 75 | t := s.Init(c) | |
79 | 76 | |||
80 | t.SetupVartypes() | 77 | t.SetupVartypes() | |
81 | t.SetupTool("echo", "ECHO", AtRunTime) | 78 | t.SetupTool("echo", "ECHO", AtRunTime) | |
82 | t.SetupTool("find", "FIND", AtRunTime) | 79 | t.SetupTool("find", "FIND", AtRunTime) | |
83 | t.SetupTool("pax", "PAX", AtRunTime) | 80 | t.SetupTool("pax", "PAX", AtRunTime) | |
84 | mklines := t.NewMkLines("Makefile", // From audio/squeezeboxserver | 81 | mklines := t.NewMkLines("Makefile", // From audio/squeezeboxserver | |
85 | MkRcsID, | 82 | MkRcsID, | |
86 | "", | 83 | "", | |
84 | "SBS_COPY=\tsource target", | |||
85 | "", | |||
87 | ".for _list_ _dir_ in ${SBS_COPY}", | 86 | ".for _list_ _dir_ in ${SBS_COPY}", | |
88 | "\tcd ${WRKSRC} && ${FIND} ${${_list_}} -type f ! -name '*.orig' 2>/dev/null "+ | 87 | "\tcd ${WRKSRC} && ${FIND} ${${_list_}} -type f ! -name '*.orig' 2>/dev/null "+ | |
89 | "| pax -rw -pm ${DESTDIR}${PREFIX}/${${_dir_}}", | 88 | "| pax -rw -pm ${DESTDIR}${PREFIX}/${${_dir_}}", | |
90 | ".endfor") | 89 | ".endfor") | |
91 | 90 | |||
92 | mklines.Check() | 91 | mklines.Check() | |
93 | 92 | |||
94 | t.CheckOutputLines( | 93 | t.CheckOutputLines( | |
95 | "WARN: Makefile:3: Variable names starting with an underscore (_list_) are reserved for internal pkgsrc use.", | 94 | "WARN: Makefile:5: Variable names starting with an underscore (_list_) "+ | |
96 | "WARN: Makefile:3: Variable names starting with an underscore (_dir_) are reserved for internal pkgsrc use.", | 95 | "are reserved for internal pkgsrc use.", | |
97 | "WARN: Makefile:3: SBS_COPY is used but not defined.", | 96 | "WARN: Makefile:5: Variable names starting with an underscore (_dir_) "+ | |
98 | "WARN: Makefile:4: The exitcode of \"${FIND}\" at the left of the | operator is ignored.") | 97 | "are reserved for internal pkgsrc use.", | |
98 | "WARN: Makefile:6: The exitcode of \"${FIND}\" at the left of the | operator is ignored.") | |||
99 | } | 99 | } | |
100 | 100 | |||
101 | func (s *Suite) Test_MkLines__comparing_YesNo_variable_to_string(c *check.C) { | 101 | func (s *Suite) Test_MkLines__comparing_YesNo_variable_to_string(c *check.C) { | |
102 | t := s.Init(c) | 102 | t := s.Init(c) | |
103 | 103 | |||
104 | t.SetupVartypes() | 104 | t.SetupVartypes() | |
105 | mklines := t.NewMkLines("databases/gdbm_compat/builtin.mk", | 105 | mklines := t.NewMkLines("databases/gdbm_compat/builtin.mk", | |
106 | MkRcsID, | 106 | MkRcsID, | |
107 | ".if ${USE_BUILTIN.gdbm} == \"no\"", | 107 | ".if ${USE_BUILTIN.gdbm} == \"no\"", | |
108 | ".endif", | 108 | ".endif", | |
109 | ".if ${USE_BUILTIN.gdbm:tu} == \"no\"", // Can never be true, since "no" is not uppercase. | 109 | ".if ${USE_BUILTIN.gdbm:tu} == \"no\"", // Can never be true, since "no" is not uppercase. | |
110 | ".endif") | 110 | ".endif") | |
111 | 111 | |||
112 | mklines.Check() | 112 | mklines.Check() | |
113 | 113 | |||
114 | t.CheckOutputLines( | 114 | t.CheckOutputLines( | |
115 | "WARN: databases/gdbm_compat/builtin.mk:2: " + | 115 | "WARN: databases/gdbm_compat/builtin.mk:2: " + | |
116 | "USE_BUILTIN.gdbm should be matched against \"[yY][eE][sS]\" or \"[nN][oO]\", not compared with \"no\".") | 116 | "USE_BUILTIN.gdbm should be matched against \"[yY][eE][sS]\" or \"[nN][oO]\", " + | |
117 | "not compared with \"no\".") | |||
117 | } | 118 | } | |
118 | 119 | |||
119 | func (s *Suite) Test_MkLines__varuse_sh_modifier(c *check.C) { | 120 | func (s *Suite) Test_MkLines__varuse_sh_modifier(c *check.C) { | |
120 | t := s.Init(c) | 121 | t := s.Init(c) | |
121 | 122 | |||
122 | t.SetupVartypes() | 123 | t.SetupVartypes() | |
123 | t.SetupTool("sed", "SED", AfterPrefsMk) | 124 | t.SetupTool("sed", "SED", AfterPrefsMk) | |
124 | mklines := t.NewMkLines("lang/qore/module.mk", | 125 | mklines := t.NewMkLines("lang/qore/module.mk", | |
125 | MkRcsID, | 126 | MkRcsID, | |
126 | "qore-version=\tqore --short-version | ${SED} -e s/-.*//", | 127 | "qore-version=\tqore --short-version | ${SED} -e s/-.*//", | |
127 | "PLIST_SUBST+=\tQORE_VERSION=\"${qore-version:sh}\"") | 128 | "PLIST_SUBST+=\tQORE_VERSION=\"${qore-version:sh}\"") | |
128 | 129 | |||
129 | vars2 := mklines.mklines[1].DetermineUsedVariables() | 130 | vars2 := mklines.mklines[1].DetermineUsedVariables() | |
130 | 131 | |||
131 | c.Check(vars2, deepEquals, []string{"SED"}) | 132 | c.Check(vars2, deepEquals, []string{"SED"}) | |
132 | 133 | |||
133 | vars3 := mklines.mklines[2].DetermineUsedVariables() | 134 | vars3 := mklines.mklines[2].DetermineUsedVariables() | |
134 | 135 | |||
136 | // qore-version, despite its unusual name, is a pretty normal Make variable. | |||
135 | c.Check(vars3, deepEquals, []string{"qore-version"}) | 137 | c.Check(vars3, deepEquals, []string{"qore-version"}) | |
136 | 138 | |||
137 | mklines.Check() | 139 | mklines.Check() | |
138 | 140 | |||
139 | // No warnings about defined but not used or vice versa | 141 | // No warnings about defined but not used or vice versa | |
140 | t.CheckOutputEmpty() | 142 | t.CheckOutputEmpty() | |
141 | } | 143 | } | |
142 | 144 | |||
143 | // For parameterized variables, the "defined but not used" and | 145 | // For parameterized variables, the "defined but not used" and | |
144 | // the "used but not defined" checks are loosened a bit. | 146 | // the "used but not defined" checks are loosened a bit. | |
145 | // When VAR.param1 is defined or used, VAR.param2 is also regarded | 147 | // When VAR.param1 is defined or used, VAR.param2 is also regarded | |
146 | // as defined or used since often in pkgsrc, parameterized variables | 148 | // as defined or used since often in pkgsrc, parameterized variables | |
147 | // are not referred to by their exact names but by VAR.${param}. | 149 | // are not referred to by their exact names but by VAR.${param}. | |
@@ -151,63 +153,79 @@ func (s *Suite) Test_MkLines__varuse_par | @@ -151,63 +153,79 @@ func (s *Suite) Test_MkLines__varuse_par | |||
151 | t.SetupVartypes() | 153 | t.SetupVartypes() | |
152 | mklines := t.NewMkLines("converters/wv2/Makefile", | 154 | mklines := t.NewMkLines("converters/wv2/Makefile", | |
153 | MkRcsID, | 155 | MkRcsID, | |
154 | "CONFIGURE_ARGS+=\t\t${CONFIGURE_ARGS.${ICONV_TYPE}-iconv}", | 156 | "CONFIGURE_ARGS+=\t\t${CONFIGURE_ARGS.${ICONV_TYPE}-iconv}", | |
155 | "CONFIGURE_ARGS.gnu-iconv=\t--with-libiconv=${BUILDLINK_PREFIX.iconv}") | 157 | "CONFIGURE_ARGS.gnu-iconv=\t--with-libiconv=${BUILDLINK_PREFIX.iconv}") | |
156 | 158 | |||
157 | mklines.Check() | 159 | mklines.Check() | |
158 | 160 | |||
159 | // No warnings about CONFIGURE_ARGS.* being defined but not used or vice versa. | 161 | // No warnings about CONFIGURE_ARGS.* being defined but not used or vice versa. | |
160 | t.CheckOutputLines( | 162 | t.CheckOutputLines( | |
161 | "WARN: converters/wv2/Makefile:2: ICONV_TYPE is used but not defined.") | 163 | "WARN: converters/wv2/Makefile:2: ICONV_TYPE is used but not defined.") | |
162 | } | 164 | } | |
163 | 165 | |||
164 | // Even very complicated shell commands are parsed correctly. | 166 | // When an ODE runtime loop is used to expand variables to shell commands, | |
165 | // Since the variable is defined in this same Makefile, it is | 167 | // pkglint only understands that there is a variable that is executed as | |
166 | // assumed to be a known shell command and therefore does not need | 168 | // shell command. | |
167 | // USE_TOOLS or a similar declaration. | 169 | // | |
170 | // In this example, GCONF_SCHEMAS is a list of filenames, but pkglint doesn't know this | |||
171 | // because there is no built-in rule saying *_SCHEMAS are filenames. | |||
172 | // If the variable name had been GCONF_SCHEMA_FILES, pkglint would know. | |||
173 | // | |||
174 | // As of November 2018, pkglint sees GCONF_SCHEMAS as being the shell command. | |||
175 | // It doesn't expand the @s@ loop to see what really happens. | |||
176 | // | |||
177 | // If it did that, it could notice that GCONF_SCHEMAS expands to a single shell command, | |||
178 | // and in that command INSTALL_DATA is used as the command for the first time, | |||
179 | // and as a regular command line argument in all other times. | |||
180 | // This combination is strange enough to warrant a warning. | |||
181 | // | |||
182 | // The bug here is the missing semicolon just before the @}. | |||
183 | // | |||
184 | // Pkglint could offer to either add the missing semicolon. | |||
185 | // Or, if it knows what INSTALL_DATA does, it could simply say that INSTALL_DATA | |||
186 | // can handle multiple files in a single invocation. | |||
168 | func (s *Suite) Test_MkLines__loop_modifier(c *check.C) { | 187 | func (s *Suite) Test_MkLines__loop_modifier(c *check.C) { | |
169 | t := s.Init(c) | 188 | t := s.Init(c) | |
170 | 189 | |||
171 | t.SetupVartypes() | 190 | t.SetupVartypes() | |
172 | mklines := t.NewMkLines("chat/xchat/Makefile", | 191 | mklines := t.NewMkLines("chat/xchat/Makefile", | |
173 | MkRcsID, | 192 | MkRcsID, | |
174 | "GCONF_SCHEMAS=\tapps_xchat_url_handler.schemas", | 193 | "GCONF_SCHEMAS=\tapps_xchat_url_handler.schemas", | |
175 | "post-install:", | 194 | "post-install:", | |
176 | "\t${GCONF_SCHEMAS:@.s.@"+ | 195 | "\t${GCONF_SCHEMAS:@s@"+ | |
177 | "${INSTALL_DATA} ${WRKSRC}/src/common/dbus/${.s.} ${DESTDIR}${GCONF_SCHEMAS_DIR}/@}") | 196 | "${INSTALL_DATA} ${WRKSRC}/src/common/dbus/${s} ${DESTDIR}${GCONF_SCHEMAS_DIR}/@}") | |
178 | 197 | |||
179 | mklines.Check() | 198 | mklines.Check() | |
180 | 199 | |||
181 | // Earlier versions of pkglint warned about a missing @ at the end. | 200 | // Earlier versions of pkglint warned about a missing @ at the end. | |
182 | t.CheckOutputEmpty() | 201 | t.CheckOutputEmpty() | |
183 | } | 202 | } | |
184 | 203 | |||
185 | // PR 46570 | |||
186 | func (s *Suite) Test_MkLines__PKG_SKIP_REASON_depending_on_OPSYS(c *check.C) { | 204 | func (s *Suite) Test_MkLines__PKG_SKIP_REASON_depending_on_OPSYS(c *check.C) { | |
187 | t := s.Init(c) | 205 | t := s.Init(c) | |
188 | 206 | |||
189 | t.SetupVartypes() | 207 | t.SetupVartypes() | |
190 | mklines := t.NewMkLines("Makefile", | 208 | mklines := t.NewMkLines("Makefile", | |
191 | MkRcsID, | 209 | MkRcsID, | |
192 | "PKG_SKIP_REASON+=\t\"Fails everywhere\"", | 210 | "PKG_SKIP_REASON+=\t\"Fails everywhere\"", | |
193 | ".if ${OPSYS} == \"Cygwin\"", | 211 | ".if ${OPSYS} == \"Cygwin\"", | |
194 | "PKG_SKIP_REASON+=\t\"Fails on Cygwin\"", | 212 | "PKG_SKIP_REASON+=\t\"Fails on Cygwin\"", | |
195 | ".endif") | 213 | ".endif") | |
196 | 214 | |||
197 | mklines.Check() | 215 | mklines.Check() | |
198 | 216 | |||
199 | t.CheckOutputLines( | 217 | t.CheckOutputLines( | |
200 | "NOTE: Makefile:4: Consider defining NOT_FOR_PLATFORM instead of setting PKG_SKIP_REASON depending on ${OPSYS}.") | 218 | "NOTE: Makefile:4: Consider setting NOT_FOR_PLATFORM instead of PKG_SKIP_REASON depending on ${OPSYS}.") | |
201 | } | 219 | } | |
202 | 220 | |||
203 | // PR 46570, item "15. net/uucp/Makefile has a make loop" | 221 | // PR 46570, item "15. net/uucp/Makefile has a make loop" | |
204 | func (s *Suite) Test_MkLines__indirect_variables(c *check.C) { | 222 | func (s *Suite) Test_MkLines__indirect_variables(c *check.C) { | |
205 | t := s.Init(c) | 223 | t := s.Init(c) | |
206 | 224 | |||
207 | t.SetupTool("echo", "ECHO", AfterPrefsMk) | 225 | t.SetupTool("echo", "ECHO", AfterPrefsMk) | |
208 | mklines := t.NewMkLines("net/uucp/Makefile", | 226 | mklines := t.NewMkLines("net/uucp/Makefile", | |
209 | MkRcsID, | 227 | MkRcsID, | |
210 | "", | 228 | "", | |
211 | "post-configure:", | 229 | "post-configure:", | |
212 | ".for var in MAIL_PROGRAM CMDPATH", | 230 | ".for var in MAIL_PROGRAM CMDPATH", | |
213 | "\t"+`${RUN} ${ECHO} "#define ${var} \""${UUCP_${var}}"\""`, | 231 | "\t"+`${RUN} ${ECHO} "#define ${var} \""${UUCP_${var}}"\""`, | |
@@ -301,27 +319,27 @@ func (s *Suite) Test_MkLines_CheckForUse | @@ -301,27 +319,27 @@ func (s *Suite) Test_MkLines_CheckForUse | |||
301 | t.NewMkLines("Makefile.common", | 319 | t.NewMkLines("Makefile.common", | |
302 | MkRcsID, | 320 | MkRcsID, | |
303 | "#", | 321 | "#", | |
304 | "#", | 322 | "#", | |
305 | ).CheckForUsedComment("category/package") | 323 | ).CheckForUsedComment("category/package") | |
306 | 324 | |||
307 | t.CheckOutputLines( | 325 | t.CheckOutputLines( | |
308 | "WARN: Makefile.common:3: Please add a line \"# used by category/package\" here.", | 326 | "WARN: Makefile.common:3: Please add a line \"# used by category/package\" here.", | |
309 | "AUTOFIX: Makefile.common:3: Inserting a line \"# used by category/package\" before this line.") | 327 | "AUTOFIX: Makefile.common:3: Inserting a line \"# used by category/package\" before this line.") | |
310 | 328 | |||
311 | c.Check(G.autofixAvailable, equals, true) | 329 | c.Check(G.autofixAvailable, equals, true) | |
312 | } | 330 | } | |
313 | 331 | |||
314 | func (s *Suite) Test_MkLines_DetermineDefinedVariables(c *check.C) { | 332 | func (s *Suite) Test_MkLines_collectDefinedVariables(c *check.C) { | |
315 | t := s.Init(c) | 333 | t := s.Init(c) | |
316 | 334 | |||
317 | t.SetupCommandLine("-Wall,no-space") | 335 | t.SetupCommandLine("-Wall,no-space") | |
318 | t.SetupPkgsrc() | 336 | t.SetupPkgsrc() | |
319 | t.CreateFileLines("mk/tools/defaults.mk", | 337 | t.CreateFileLines("mk/tools/defaults.mk", | |
320 | "USE_TOOLS+= autoconf autoconf213") | 338 | "USE_TOOLS+= autoconf autoconf213") | |
321 | G.Pkgsrc.LoadInfrastructure() | 339 | G.Pkgsrc.LoadInfrastructure() | |
322 | mklines := t.NewMkLines("determine-defined-variables.mk", | 340 | mklines := t.NewMkLines("determine-defined-variables.mk", | |
323 | MkRcsID, | 341 | MkRcsID, | |
324 | "", | 342 | "", | |
325 | "USE_TOOLS+= autoconf213 autoconf", | 343 | "USE_TOOLS+= autoconf213 autoconf", | |
326 | "USE_TOOLS:= ${USE_TOOLS:Ntbl}", | 344 | "USE_TOOLS:= ${USE_TOOLS:Ntbl}", | |
327 | "", | 345 | "", | |
@@ -340,80 +358,80 @@ func (s *Suite) Test_MkLines_DetermineDe | @@ -340,80 +358,80 @@ func (s *Suite) Test_MkLines_DetermineDe | |||
340 | 358 | |||
341 | mklines.Check() | 359 | mklines.Check() | |
342 | 360 | |||
343 | // The tools autoreconf and autoheader213 are known at this point because of the USE_TOOLS line. | 361 | // The tools autoreconf and autoheader213 are known at this point because of the USE_TOOLS line. | |
344 | // The SUV variable is used implicitly by the SUBST framework, therefore no warning. | 362 | // The SUV variable is used implicitly by the SUBST framework, therefore no warning. | |
345 | // The OSV.NetBSD variable is used implicitly via the OSV variable, therefore no warning. | 363 | // The OSV.NetBSD variable is used implicitly via the OSV variable, therefore no warning. | |
346 | t.CheckOutputLines( | 364 | t.CheckOutputLines( | |
347 | // FIXME: the below warning is wrong; it's ok to have SUBST blocks in all files, maybe except buildlink3.mk. | 365 | // FIXME: the below warning is wrong; it's ok to have SUBST blocks in all files, maybe except buildlink3.mk. | |
348 | "WARN: determine-defined-variables.mk:12: The variable SUBST_VARS.subst may not be set "+ | 366 | "WARN: determine-defined-variables.mk:12: The variable SUBST_VARS.subst may not be set "+ | |
349 | "(only given a default value, appended to) in this file; it would be ok in Makefile, Makefile.common, options.mk.", | 367 | "(only given a default value, appended to) in this file; it would be ok in Makefile, Makefile.common, options.mk.", | |
350 | "WARN: determine-defined-variables.mk:16: Unknown shell command \"unknown-command\".") | 368 | "WARN: determine-defined-variables.mk:16: Unknown shell command \"unknown-command\".") | |
351 | } | 369 | } | |
352 | 370 | |||
353 | func (s *Suite) Test_MkLines_DetermineDefinedVariables__BUILTIN_FIND_FILES_VAR(c *check.C) { | 371 | func (s *Suite) Test_MkLines_collectDefinedVariables__BUILTIN_FIND_FILES_VAR(c *check.C) { | |
354 | t := s.Init(c) | 372 | t := s.Init(c) | |
355 | 373 | |||
356 | t.SetupCommandLine("-Wall,no-space") | 374 | t.SetupCommandLine("-Wall,no-space") | |
357 | t.SetupPackage("category/package") | 375 | t.SetupPackage("category/package") | |
358 | t.CreateFileLines("mk/buildlink3/bsd.builtin.mk", | 376 | t.CreateFileLines("mk/buildlink3/bsd.builtin.mk", | |
359 | MkRcsID) | 377 | MkRcsID) | |
360 | mklines := t.SetupFileMkLines("category/package/builtin.mk", | 378 | mklines := t.SetupFileMkLines("category/package/builtin.mk", | |
361 | MkRcsID, | 379 | MkRcsID, | |
362 | "", | 380 | "", | |
363 | "BUILTIN_FIND_FILES_VAR:= H_XFT2", | 381 | "BUILTIN_FIND_FILES_VAR:= H_XFT2", | |
364 | "BUILTIN_FIND_FILES.H_XFT2= ${X11BASE}/include/X11/Xft/Xft.h", | 382 | "BUILTIN_FIND_FILES.H_XFT2= ${X11BASE}/include/X11/Xft/Xft.h", | |
365 | "", | 383 | "", | |
366 | ".include \"../../mk/buildlink3/bsd.builtin.mk\"", | 384 | ".include \"../../mk/buildlink3/bsd.builtin.mk\"", | |
367 | "", | 385 | "", | |
368 | ".if ${H_XFT2:N__nonexistent__} && ${H_UNDEF:N__nonexistent__}", | 386 | ".if ${H_XFT2:N__nonexistent__} && ${H_UNDEF:N__nonexistent__}", | |
369 | ".endif") | 387 | ".endif") | |
370 | G.Pkgsrc.LoadInfrastructure() | 388 | G.Pkgsrc.LoadInfrastructure() | |
371 | 389 | |||
372 | mklines.Check() | 390 | mklines.Check() | |
373 | 391 | |||
374 | t.CheckOutputLines( | 392 | t.CheckOutputLines( | |
375 | "WARN: ~/category/package/builtin.mk:8: H_UNDEF is used but not defined.") | 393 | "WARN: ~/category/package/builtin.mk:8: H_UNDEF is used but not defined.") | |
376 | } | 394 | } | |
377 | 395 | |||
378 | func (s *Suite) Test_MkLines_DetermineUsedVariables__simple(c *check.C) { | 396 | func (s *Suite) Test_MkLines_collectUsedVariables__simple(c *check.C) { | |
379 | t := s.Init(c) | 397 | t := s.Init(c) | |
380 | 398 | |||
381 | mklines := t.NewMkLines("filename", | 399 | mklines := t.NewMkLines("filename", | |
382 | "\t${VAR}") | 400 | "\t${VAR}") | |
383 | mkline := mklines.mklines[0] | 401 | mkline := mklines.mklines[0] | |
384 | G.Mk = mklines | 402 | G.Mk = mklines | |
385 | 403 | |||
386 | mklines.DetermineUsedVariables() | 404 | mklines.collectUsedVariables() | |
387 | 405 | |||
388 | c.Check(len(mklines.vars.used), equals, 1) | 406 | c.Check(len(mklines.vars.used), equals, 1) | |
389 | c.Check(mklines.vars.FirstUse("VAR"), equals, mkline) | 407 | c.Check(mklines.vars.FirstUse("VAR"), equals, mkline) | |
390 | } | 408 | } | |
391 | 409 | |||
392 | func (s *Suite) Test_MkLines_DetermineUsedVariables__nested(c *check.C) { | 410 | func (s *Suite) Test_MkLines_collectUsedVariables__nested(c *check.C) { | |
393 | t := s.Init(c) | 411 | t := s.Init(c) | |
394 | 412 | |||
395 | mklines := t.NewMkLines("filename.mk", | 413 | mklines := t.NewMkLines("filename.mk", | |
396 | MkRcsID, | 414 | MkRcsID, | |
397 | "", | 415 | "", | |
398 | "LHS.${lparam}=\tRHS.${rparam}", | 416 | "LHS.${lparam}=\tRHS.${rparam}", | |
399 | "", | 417 | "", | |
400 | "target:", | 418 | "target:", | |
401 | "\t${outer.${inner}}") | 419 | "\t${outer.${inner}}") | |
402 | assignMkline := mklines.mklines[2] | 420 | assignMkline := mklines.mklines[2] | |
403 | shellMkline := mklines.mklines[5] | 421 | shellMkline := mklines.mklines[5] | |
404 | G.Mk = mklines | 422 | G.Mk = mklines | |
405 | 423 | |||
406 | mklines.DetermineUsedVariables() | 424 | mklines.collectUsedVariables() | |
407 | 425 | |||
408 | c.Check(len(mklines.vars.used), equals, 5) | 426 | c.Check(len(mklines.vars.used), equals, 5) | |
409 | c.Check(mklines.vars.FirstUse("lparam"), equals, assignMkline) | 427 | c.Check(mklines.vars.FirstUse("lparam"), equals, assignMkline) | |
410 | c.Check(mklines.vars.FirstUse("rparam"), equals, assignMkline) | 428 | c.Check(mklines.vars.FirstUse("rparam"), equals, assignMkline) | |
411 | c.Check(mklines.vars.FirstUse("inner"), equals, shellMkline) | 429 | c.Check(mklines.vars.FirstUse("inner"), equals, shellMkline) | |
412 | c.Check(mklines.vars.FirstUse("outer.*"), equals, shellMkline) | 430 | c.Check(mklines.vars.FirstUse("outer.*"), equals, shellMkline) | |
413 | c.Check(mklines.vars.FirstUse("outer.${inner}"), equals, shellMkline) | 431 | c.Check(mklines.vars.FirstUse("outer.${inner}"), equals, shellMkline) | |
414 | } | 432 | } | |
415 | 433 | |||
416 | func (s *Suite) Test_MkLines__private_tool_undefined(c *check.C) { | 434 | func (s *Suite) Test_MkLines__private_tool_undefined(c *check.C) { | |
417 | t := s.Init(c) | 435 | t := s.Init(c) | |
418 | 436 | |||
419 | t.SetupVartypes() | 437 | t.SetupVartypes() | |
@@ -628,27 +646,27 @@ func (s *Suite) Test_MkLines__wip_catego | @@ -628,27 +646,27 @@ func (s *Suite) Test_MkLines__wip_catego | |||
628 | "WARN: ~/wip/Makefile:14: Unusual target \"clean-tmpdir\".", | 646 | "WARN: ~/wip/Makefile:14: Unusual target \"clean-tmpdir\".", | |
629 | "", | 647 | "", | |
630 | "\tIf you want to define your own target, declare it like this:", | 648 | "\tIf you want to define your own target, declare it like this:", | |
631 | "", | 649 | "", | |
632 | "\t\t.PHONY: my-target", | 650 | "\t\t.PHONY: my-target", | |
633 | "", | 651 | "", | |
634 | "\tIn the rare case that you actually want a file-based make(1) target,", | 652 | "\tIn the rare case that you actually want a file-based make(1) target,", | |
635 | "\twrite it like this:", | 653 | "\twrite it like this:", | |
636 | "", | 654 | "", | |
637 | "\t\t${.CURDIR}/my-file:", | 655 | "\t\t${.CURDIR}/my-file:", | |
638 | "") | 656 | "") | |
639 | } | 657 | } | |
640 | 658 | |||
641 | func (s *Suite) Test_MkLines_determineDocumentedVariables(c *check.C) { | 659 | func (s *Suite) Test_MkLines_collectDocumentedVariables(c *check.C) { | |
642 | t := s.Init(c) | 660 | t := s.Init(c) | |
643 | 661 | |||
644 | t.SetupVartypes() | 662 | t.SetupVartypes() | |
645 | t.SetupTool("rm", "RM", AtRunTime) | 663 | t.SetupTool("rm", "RM", AtRunTime) | |
646 | mklines := t.NewMkLines("Makefile", | 664 | mklines := t.NewMkLines("Makefile", | |
647 | MkRcsID, | 665 | MkRcsID, | |
648 | "#", | 666 | "#", | |
649 | "# Copyright 2000-2018", | 667 | "# Copyright 2000-2018", | |
650 | "#", | 668 | "#", | |
651 | "# This whole comment is ignored, until the next empty line.", | 669 | "# This whole comment is ignored, until the next empty line.", | |
652 | "", | 670 | "", | |
653 | "# User-settable variables:", | 671 | "# User-settable variables:", | |
654 | "#", | 672 | "#", | |
@@ -660,27 +678,27 @@ func (s *Suite) Test_MkLines_determineDo | @@ -660,27 +678,27 @@ func (s *Suite) Test_MkLines_determineDo | |||
660 | "# PKG_VERBOSE", | 678 | "# PKG_VERBOSE", | |
661 | "#\tWhen this variable is defined, pkgsrc gets a bit more verbose", | 679 | "#\tWhen this variable is defined, pkgsrc gets a bit more verbose", | |
662 | "#\t(i.e. \"-v\" option is passed to some commands ...", | 680 | "#\t(i.e. \"-v\" option is passed to some commands ...", | |
663 | "", | 681 | "", | |
664 | "# VARIABLE", | 682 | "# VARIABLE", | |
665 | "#\tA paragraph of a single line is not enough to be recognized as \"relevant\".", | 683 | "#\tA paragraph of a single line is not enough to be recognized as \"relevant\".", | |
666 | "", | 684 | "", | |
667 | "# VARBASE1.<param1>", | 685 | "# VARBASE1.<param1>", | |
668 | "# VARBASE2.*", | 686 | "# VARBASE2.*", | |
669 | "# VARBASE3.${id}") | 687 | "# VARBASE3.${id}") | |
670 | 688 | |||
671 | // The variables that appear in the documentation are marked as | 689 | // The variables that appear in the documentation are marked as | |
672 | // used, to prevent the "defined but not used" warnings. | 690 | // used, to prevent the "defined but not used" warnings. | |
673 | mklines.determineDocumentedVariables() | 691 | mklines.collectDocumentedVariables() | |
674 | 692 | |||
675 | var varnames []string | 693 | var varnames []string | |
676 | for varname, mkline := range mklines.vars.used { | 694 | for varname, mkline := range mklines.vars.used { | |
677 | varnames = append(varnames, fmt.Sprintf("%s (line %s)", varname, mkline.Linenos())) | 695 | varnames = append(varnames, fmt.Sprintf("%s (line %s)", varname, mkline.Linenos())) | |
678 | } | 696 | } | |
679 | sort.Strings(varnames) | 697 | sort.Strings(varnames) | |
680 | 698 | |||
681 | expected := []string{ | 699 | expected := []string{ | |
682 | "PKG_DEBUG_LEVEL (line 9)", | 700 | "PKG_DEBUG_LEVEL (line 9)", | |
683 | "PKG_VERBOSE (line 14)", | 701 | "PKG_VERBOSE (line 14)", | |
684 | "VARBASE1.* (line 21)", | 702 | "VARBASE1.* (line 21)", | |
685 | "VARBASE2.* (line 22)", | 703 | "VARBASE2.* (line 22)", | |
686 | "VARBASE3.${id} (line 23)", | 704 | "VARBASE3.${id} (line 23)", | |
@@ -715,143 +733,143 @@ func (s *Suite) Test_MkLines__unknown_op | @@ -715,143 +733,143 @@ func (s *Suite) Test_MkLines__unknown_op | |||
715 | mklines := t.NewMkLines("options.mk", | 733 | mklines := t.NewMkLines("options.mk", | |
716 | MkRcsID, | 734 | MkRcsID, | |
717 | "#", | 735 | "#", | |
718 | "PKG_OPTIONS_VAR=\tPKG_OPTIONS.pkgbase", | 736 | "PKG_OPTIONS_VAR=\tPKG_OPTIONS.pkgbase", | |
719 | "PKG_SUPPORTED_OPTIONS=\tknown unknown", | 737 | "PKG_SUPPORTED_OPTIONS=\tknown unknown", | |
720 | "PKG_SUGGESTED_OPTIONS=\tknown unknown") | 738 | "PKG_SUGGESTED_OPTIONS=\tknown unknown") | |
721 | 739 | |||
722 | mklines.Check() | 740 | mklines.Check() | |
723 | 741 | |||
724 | t.CheckOutputLines( | 742 | t.CheckOutputLines( | |
725 | "WARN: options.mk:4: Unknown option \"unknown\".") | 743 | "WARN: options.mk:4: Unknown option \"unknown\".") | |
726 | } | 744 | } | |
727 | 745 | |||
728 | func (s *Suite) Test_MkLines_CheckRedundantVariables(c *check.C) { | 746 | func (s *Suite) Test_MkLines_CheckRedundantAssignments(c *check.C) { | |
729 | t := s.Init(c) | 747 | t := s.Init(c) | |
730 | included := t.NewMkLines("module.mk", | 748 | included := t.NewMkLines("module.mk", | |
731 | "VAR=\tvalue ${OTHER}", | 749 | "VAR=\tvalue ${OTHER}", | |
732 | "VAR?=\tvalue ${OTHER}", | 750 | "VAR?=\tvalue ${OTHER}", | |
733 | "VAR=\tnew value") | 751 | "VAR=\tnew value") | |
734 | makefile := t.NewMkLines("Makefile", | 752 | makefile := t.NewMkLines("Makefile", | |
735 | "VAR=\tthe package may overwrite variables from other files") | 753 | "VAR=\tthe package may overwrite variables from other files") | |
736 | allLines := append(append([]Line(nil), included.lines.Lines...), makefile.lines.Lines...) | 754 | allLines := append(append([]Line(nil), included.lines.Lines...), makefile.lines.Lines...) | |
737 | mklines := NewMkLines(NewLines(included.lines.FileName, allLines)) | 755 | mklines := NewMkLines(NewLines(included.lines.FileName, allLines)) | |
738 | 756 | |||
739 | // XXX: The warnings from here are not in the same order as the other warnings. | 757 | // XXX: The warnings from here are not in the same order as the other warnings. | |
740 | // XXX: There may be some warnings for the same file separated by warnings for other files. | 758 | // XXX: There may be some warnings for the same file separated by warnings for other files. | |
741 | mklines.CheckRedundantVariables() | 759 | mklines.CheckRedundantAssignments() | |
742 | 760 | |||
743 | t.CheckOutputLines( | 761 | t.CheckOutputLines( | |
744 | "NOTE: module.mk:1: Definition of VAR is redundant because of line 2.", | 762 | "NOTE: module.mk:1: Definition of VAR is redundant because of line 2.", | |
745 | "WARN: module.mk:1: Variable VAR is overwritten in line 3.") | 763 | "WARN: module.mk:1: Variable VAR is overwritten in line 3.") | |
746 | } | 764 | } | |
747 | 765 | |||
748 | func (s *Suite) Test_MkLines_CheckRedundantVariables__different_value(c *check.C) { | 766 | func (s *Suite) Test_MkLines_CheckRedundantAssignments__different_value(c *check.C) { | |
749 | t := s.Init(c) | 767 | t := s.Init(c) | |
750 | mklines := t.NewMkLines("module.mk", | 768 | mklines := t.NewMkLines("module.mk", | |
751 | "VAR=\tvalue ${OTHER}", | 769 | "VAR=\tvalue ${OTHER}", | |
752 | "VAR?=\tdifferent value") | 770 | "VAR?=\tdifferent value") | |
753 | 771 | |||
754 | mklines.CheckRedundantVariables() | 772 | mklines.CheckRedundantAssignments() | |
755 | 773 | |||
756 | t.CheckOutputEmpty() | 774 | t.CheckOutputEmpty() | |
757 | } | 775 | } | |
758 | 776 | |||
759 | func (s *Suite) Test_MkLines_CheckRedundantVariables__overwrite_same_value(c *check.C) { | 777 | func (s *Suite) Test_MkLines_CheckRedundantAssignments__overwrite_same_value(c *check.C) { | |
760 | t := s.Init(c) | 778 | t := s.Init(c) | |
761 | mklines := t.NewMkLines("module.mk", | 779 | mklines := t.NewMkLines("module.mk", | |
762 | "VAR=\tvalue ${OTHER}", | 780 | "VAR=\tvalue ${OTHER}", | |
763 | "VAR=\tvalue ${OTHER}") | 781 | "VAR=\tvalue ${OTHER}") | |
764 | 782 | |||
765 | mklines.CheckRedundantVariables() | 783 | mklines.CheckRedundantAssignments() | |
766 | 784 | |||
767 | t.CheckOutputLines( | 785 | t.CheckOutputLines( | |
768 | "NOTE: module.mk:1: Definition of VAR is redundant because of line 2.") | 786 | "NOTE: module.mk:1: Definition of VAR is redundant because of line 2.") | |
769 | } | 787 | } | |
770 | 788 | |||
771 | func (s *Suite) Test_MkLines_CheckRedundantVariables__procedure_call(c *check.C) { | 789 | func (s *Suite) Test_MkLines_CheckRedundantAssignments__procedure_call(c *check.C) { | |
772 | t := s.Init(c) | 790 | t := s.Init(c) | |
773 | mklines := t.NewMkLines("mk/pthread.buildlink3.mk", | 791 | mklines := t.NewMkLines("mk/pthread.buildlink3.mk", | |
774 | "CHECK_BUILTIN.pthread:=\tyes", | 792 | "CHECK_BUILTIN.pthread:=\tyes", | |
775 | ".include \"../../mk/pthread.builtin.mk\"", | 793 | ".include \"../../mk/pthread.builtin.mk\"", | |
776 | "CHECK_BUILTIN.pthread:=\tno") | 794 | "CHECK_BUILTIN.pthread:=\tno") | |
777 | 795 | |||
778 | mklines.CheckRedundantVariables() | 796 | mklines.CheckRedundantAssignments() | |
779 | 797 | |||
780 | t.CheckOutputEmpty() | 798 | t.CheckOutputEmpty() | |
781 | } | 799 | } | |
782 | 800 | |||
783 | func (s *Suite) Test_MkLines_CheckRedundantVariables__shell_and_eval(c *check.C) { | 801 | func (s *Suite) Test_MkLines_CheckRedundantAssignments__shell_and_eval(c *check.C) { | |
784 | t := s.Init(c) | 802 | t := s.Init(c) | |
785 | mklines := t.NewMkLines("module.mk", | 803 | mklines := t.NewMkLines("module.mk", | |
786 | "VAR:=\tvalue ${OTHER}", | 804 | "VAR:=\tvalue ${OTHER}", | |
787 | "VAR!=\tvalue ${OTHER}") | 805 | "VAR!=\tvalue ${OTHER}") | |
788 | 806 | |||
789 | mklines.CheckRedundantVariables() | 807 | mklines.CheckRedundantAssignments() | |
790 | 808 | |||
791 | // Combining := and != is too complicated to be analyzed by pkglint, | 809 | // Combining := and != is too complicated to be analyzed by pkglint, | |
792 | // therefore no warning. | 810 | // therefore no warning. | |
793 | t.CheckOutputEmpty() | 811 | t.CheckOutputEmpty() | |
794 | } | 812 | } | |
795 | 813 | |||
796 | func (s *Suite) Test_MkLines_CheckRedundantVariables__shell_and_eval_literal(c *check.C) { | 814 | func (s *Suite) Test_MkLines_CheckRedundantAssignments__shell_and_eval_literal(c *check.C) { | |
797 | t := s.Init(c) | 815 | t := s.Init(c) | |
798 | mklines := t.NewMkLines("module.mk", | 816 | mklines := t.NewMkLines("module.mk", | |
799 | "VAR:=\tvalue", | 817 | "VAR:=\tvalue", | |
800 | "VAR!=\tvalue") | 818 | "VAR!=\tvalue") | |
801 | 819 | |||
802 | mklines.CheckRedundantVariables() | 820 | mklines.CheckRedundantAssignments() | |
803 | 821 | |||
804 | // Even when := is used with a literal value (which is usually | 822 | // Even when := is used with a literal value (which is usually | |
805 | // only done for procedure calls), the shell evaluation can have | 823 | // only done for procedure calls), the shell evaluation can have | |
806 | // so many different side effects that pkglint cannot reliably | 824 | // so many different side effects that pkglint cannot reliably | |
807 | // help in this situation. | 825 | // help in this situation. | |
808 | t.CheckOutputEmpty() | 826 | t.CheckOutputEmpty() | |
809 | } | 827 | } | |
810 | 828 | |||
811 | func (s *Suite) Test_MkLines_Check__PLIST_VARS(c *check.C) { | 829 | func (s *Suite) Test_MkLines_Check__PLIST_VARS(c *check.C) { | |
812 | t := s.Init(c) | 830 | t := s.Init(c) | |
813 | 831 | |||
814 | t.SetupCommandLine("-Wno-space") | 832 | t.SetupCommandLine("-Wno-space") | |
815 | t.SetupVartypes() | 833 | t.SetupVartypes() | |
816 | t.SetupOption("both", "") | 834 | t.SetupOption("both", "") | |
817 | t.SetupOption("only-added", "") | 835 | t.SetupOption("only-added", "") | |
818 | t.SetupOption("only-defined", "") | 836 | t.SetupOption("only-defined", "") | |
837 | t.CreateFileLines("mk/bsd.options.mk") | |||
819 | 838 | |||
820 | mklines := t.SetupFileMkLines("options.mk", | 839 | mklines := t.SetupFileMkLines("category/package/options.mk", | |
821 | MkRcsID, | 840 | MkRcsID, | |
822 | "", | 841 | "", | |
823 | "PKG_OPTIONS_VAR= PKG_OPTIONS.pkg", | 842 | "PKG_OPTIONS_VAR= PKG_OPTIONS.pkg", | |
824 | "PKG_SUPPORTED_OPTIONS= both only-added only-defined", | 843 | "PKG_SUPPORTED_OPTIONS= both only-added only-defined", | |
825 | "PKG_SUGGESTED_OPTIONS= # none", | 844 | "PKG_SUGGESTED_OPTIONS= # none", | |
826 | "", | 845 | "", | |
827 | ".include \"../../mk/bsd.options.mk\"", | 846 | ".include \"../../mk/bsd.options.mk\"", | |
828 | "", | 847 | "", | |
829 | "PLIST_VARS+= both only-added", | 848 | "PLIST_VARS+= both only-added", | |
830 | "", | 849 | "", | |
831 | ".if !empty(PKG_OPTIONS:Mboth)", | 850 | ".if !empty(PKG_OPTIONS:Mboth)", | |
832 | "PLIST.both= yes", | 851 | "PLIST.both= yes", | |
833 | ".endif", | 852 | ".endif", | |
834 | "", | 853 | "", | |
835 | ".if !empty(PKG_OPTIONS:Monly-defined)", | 854 | ".if !empty(PKG_OPTIONS:Monly-defined)", | |
836 | "PLIST.only-defined= yes", | 855 | "PLIST.only-defined= yes", | |
837 | ".endif") | 856 | ".endif") | |
838 | 857 | |||
839 | mklines.Check() | 858 | mklines.Check() | |
840 | 859 | |||
841 | t.CheckOutputLines( | 860 | t.CheckOutputLines( | |
842 | "ERROR: ~/options.mk:7: \"mk/bsd.options.mk\" does not exist.", // Not relevant for this test. | 861 | "WARN: ~/category/package/options.mk:9: \"only-added\" is added to PLIST_VARS, but PLIST.only-added is not defined in this file.", | |
843 | "WARN: ~/options.mk:9: \"only-added\" is added to PLIST_VARS, but PLIST.only-added is not defined in this file.", | 862 | "WARN: ~/category/package/options.mk:16: PLIST.only-defined is defined, but \"only-defined\" is not added to PLIST_VARS in this file.") | |
844 | "WARN: ~/options.mk:16: PLIST.only-defined is defined, but \"only-defined\" is not added to PLIST_VARS in this file.") | |||
845 | } | 863 | } | |
846 | 864 | |||
847 | func (s *Suite) Test_MkLines_Check__PLIST_VARS_indirect(c *check.C) { | 865 | func (s *Suite) Test_MkLines_Check__PLIST_VARS_indirect(c *check.C) { | |
848 | t := s.Init(c) | 866 | t := s.Init(c) | |
849 | 867 | |||
850 | t.SetupCommandLine("-Wno-space") | 868 | t.SetupCommandLine("-Wno-space") | |
851 | t.SetupVartypes() | 869 | t.SetupVartypes() | |
852 | t.SetupOption("option1", "") | 870 | t.SetupOption("option1", "") | |
853 | t.SetupOption("option2", "") | 871 | t.SetupOption("option2", "") | |
854 | 872 | |||
855 | mklines := t.SetupFileMkLines("module.mk", | 873 | mklines := t.SetupFileMkLines("module.mk", | |
856 | MkRcsID, | 874 | MkRcsID, | |
857 | "", | 875 | "", | |
@@ -864,27 +882,27 @@ func (s *Suite) Test_MkLines_Check__PLIS | @@ -864,27 +882,27 @@ func (s *Suite) Test_MkLines_Check__PLIS | |||
864 | ".if 0", | 882 | ".if 0", | |
865 | "PLIST.option1= yes", | 883 | "PLIST.option1= yes", | |
866 | ".endif", | 884 | ".endif", | |
867 | "", | 885 | "", | |
868 | ".if 1", | 886 | ".if 1", | |
869 | "PLIST.option2= yes", | 887 | "PLIST.option2= yes", | |
870 | ".endif") | 888 | ".endif") | |
871 | 889 | |||
872 | mklines.Check() | 890 | mklines.Check() | |
873 | 891 | |||
874 | t.CheckOutputEmpty() | 892 | t.CheckOutputEmpty() | |
875 | } | 893 | } | |
876 | 894 | |||
877 | func (s *Suite) Test_MkLines_Check__if_else(c *check.C) { | 895 | func (s *Suite) Test_MkLines_collectElse(c *check.C) { | |
878 | t := s.Init(c) | 896 | t := s.Init(c) | |
879 | 897 | |||
880 | t.SetupCommandLine("-Wno-space") | 898 | t.SetupCommandLine("-Wno-space") | |
881 | t.SetupVartypes() | 899 | t.SetupVartypes() | |
882 | 900 | |||
883 | mklines := t.NewMkLines("module.mk", | 901 | mklines := t.NewMkLines("module.mk", | |
884 | MkRcsID, | 902 | MkRcsID, | |
885 | "", | 903 | "", | |
886 | ".if 0", | 904 | ".if 0", | |
887 | ".endif", | 905 | ".endif", | |
888 | "", | 906 | "", | |
889 | ".if 0", | 907 | ".if 0", | |
890 | ".else", | 908 | ".else", | |
@@ -1080,128 +1098,149 @@ func (s *Suite) Test_MkLines_ForEach__co | @@ -1080,128 +1098,149 @@ func (s *Suite) Test_MkLines_ForEach__co | |||
1080 | c.Check(mklines.indentation.IsConditional(), equals, true) | 1098 | c.Check(mklines.indentation.IsConditional(), equals, true) | |
1081 | seenDeveloper = true | 1099 | seenDeveloper = true | |
1082 | case "USES_GETTEXT": | 1100 | case "USES_GETTEXT": | |
1083 | c.Check(mklines.indentation.IsConditional(), equals, true) | 1101 | c.Check(mklines.indentation.IsConditional(), equals, true) | |
1084 | seenUsesGettext = true | 1102 | seenUsesGettext = true | |
1085 | } | 1103 | } | |
1086 | } | 1104 | } | |
1087 | }) | 1105 | }) | |
1088 | 1106 | |||
1089 | c.Check(seenDeveloper, equals, true) | 1107 | c.Check(seenDeveloper, equals, true) | |
1090 | c.Check(seenUsesGettext, equals, true) | 1108 | c.Check(seenUsesGettext, equals, true) | |
1091 | } | 1109 | } | |
1092 | 1110 | |||
1093 | func (s *Suite) Test_VaralignBlock_Check__autofix(c *check.C) { | 1111 | // At 2018-12-02, pkglint had resolved ${MY_PLIST_VARS} into a single word, | |
1112 | // whereas the correct behavior is to resolve it into two words. | |||
1113 | // It had produced warnings about mismatched PLIST_VARS IDs. | |||
1114 | func (s *Suite) Test_MkLines_checkVarassignPlist__indirect(c *check.C) { | |||
1115 | t := s.Init(c) | |||
1116 | ||||
1117 | t.SetupVartypes() | |||
1118 | mklines := t.SetupFileMkLines("plist.mk", | |||
1119 | MkRcsID, | |||
1120 | "", | |||
1121 | "MY_PLIST_VARS=\tvar1 var2", | |||
1122 | "PLIST_VARS+=\t${MY_PLIST_VARS}", | |||
1123 | "", | |||
1124 | "PLIST.var1=\tyes", | |||
1125 | "PLIST.var2=\tyes") | |||
1126 | ||||
1127 | mklines.Check() | |||
1128 | ||||
1129 | t.CheckOutputEmpty() | |||
1130 | } | |||
1131 | ||||
1132 | func (s *Suite) Test_VaralignBlock_Process__autofix(c *check.C) { | |||
1094 | t := s.Init(c) | 1133 | t := s.Init(c) | |
1095 | 1134 | |||
1096 | t.SetupCommandLine("-Wspace", "--show-autofix") | 1135 | t.SetupCommandLine("-Wspace", "--show-autofix") | |
1097 | 1136 | |||
1098 | mklines := t.NewMkLines("file.mk", | 1137 | mklines := t.NewMkLines("file.mk", | |
1099 | "VAR= value", // Indentation 7, fixed to 8. | 1138 | "VAR= value", // Indentation 7, fixed to 8. | |
1100 | "", // | 1139 | "", // | |
1101 | "VAR= value", // Indentation 8, fixed to 8. | 1140 | "VAR= value", // Indentation 8, fixed to 8. | |
1102 | "", // | 1141 | "", // | |
1103 | "VAR= value", // Indentation 9, fixed to 8. | 1142 | "VAR= value", // Indentation 9, fixed to 8. | |
1104 | "", // | 1143 | "", // | |
1105 | "VAR= \tvalue", // Mixed indentation 8, fixed to 8. | 1144 | "VAR= \tvalue", // Mixed indentation 8, fixed to 8. | |
1106 | "", // | 1145 | "", // | |
1107 | "VAR= \tvalue", // Mixed indentation 8, fixed to 8. | 1146 | "VAR= \tvalue", // Mixed indentation 8, fixed to 8. | |
1108 | "", // | 1147 | "", // | |
1109 | "VAR= \tvalue", // Mixed indentation 16, fixed to 16. | 1148 | "VAR= \tvalue", // Mixed indentation 16, fixed to 16. | |
1110 | "", // | 1149 | "", // | |
1111 | "VAR=\tvalue") // Already aligned with tabs only, left unchanged. | 1150 | "VAR=\tvalue") // Already aligned with tabs only, left unchanged. | |
1112 | 1151 | |||
1113 | var varalign VaralignBlock | 1152 | var varalign VaralignBlock | |
1114 | for _, line := range mklines.mklines { | 1153 | for _, line := range mklines.mklines { | |
1115 | varalign.Check(line) | 1154 | varalign.Process(line) | |
1116 | } | 1155 | } | |
1117 | varalign.Finish() | 1156 | varalign.Finish() | |
1118 | 1157 | |||
1119 | t.CheckOutputLines( | 1158 | t.CheckOutputLines( | |
1120 | "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 9.", | 1159 | "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 9.", | |
1121 | "AUTOFIX: file.mk:1: Replacing \" \" with \"\\t\".", | 1160 | "AUTOFIX: file.mk:1: Replacing \" \" with \"\\t\".", | |
1122 | "NOTE: file.mk:3: Variable values should be aligned with tabs, not spaces.", | 1161 | "NOTE: file.mk:3: Variable values should be aligned with tabs, not spaces.", | |
1123 | "AUTOFIX: file.mk:3: Replacing \" \" with \"\\t\".", | 1162 | "AUTOFIX: file.mk:3: Replacing \" \" with \"\\t\".", | |
1124 | "NOTE: file.mk:5: This variable value should be aligned with tabs, not spaces, to column 9.", | 1163 | "NOTE: file.mk:5: This variable value should be aligned with tabs, not spaces, to column 9.", | |
1125 | "AUTOFIX: file.mk:5: Replacing \" \" with \"\\t\".", | 1164 | "AUTOFIX: file.mk:5: Replacing \" \" with \"\\t\".", | |
1126 | "NOTE: file.mk:7: Variable values should be aligned with tabs, not spaces.", | 1165 | "NOTE: file.mk:7: Variable values should be aligned with tabs, not spaces.", | |
1127 | "AUTOFIX: file.mk:7: Replacing \" \\t\" with \"\\t\".", | 1166 | "AUTOFIX: file.mk:7: Replacing \" \\t\" with \"\\t\".", | |
1128 | "NOTE: file.mk:9: Variable values should be aligned with tabs, not spaces.", | 1167 | "NOTE: file.mk:9: Variable values should be aligned with tabs, not spaces.", | |
1129 | "AUTOFIX: file.mk:9: Replacing \" \\t\" with \"\\t\".", | 1168 | "AUTOFIX: file.mk:9: Replacing \" \\t\" with \"\\t\".", | |
1130 | "NOTE: file.mk:11: Variable values should be aligned with tabs, not spaces.", | 1169 | "NOTE: file.mk:11: Variable values should be aligned with tabs, not spaces.", | |
1131 | "AUTOFIX: file.mk:11: Replacing \" \\t\" with \"\\t\\t\".") | 1170 | "AUTOFIX: file.mk:11: Replacing \" \\t\" with \"\\t\\t\".") | |
1132 | } | 1171 | } | |
1133 | 1172 | |||
1134 | // When the lines of a paragraph are inconsistently aligned, | 1173 | // When the lines of a paragraph are inconsistently aligned, | |
1135 | // they are realigned to the minimum required width. | 1174 | // they are realigned to the minimum required width. | |
1136 | func (s *Suite) Test_VaralignBlock_Check__reduce_indentation(c *check.C) { | 1175 | func (s *Suite) Test_VaralignBlock_Process__reduce_indentation(c *check.C) { | |
1137 | t := s.Init(c) | 1176 | t := s.Init(c) | |
1138 | 1177 | |||
1139 | mklines := t.NewMkLines("file.mk", | 1178 | mklines := t.NewMkLines("file.mk", | |
1140 | "VAR= \tvalue", | 1179 | "VAR= \tvalue", | |
1141 | "VAR= \tvalue", | 1180 | "VAR= \tvalue", | |
1142 | "VAR=\t\t\t\tvalue", | 1181 | "VAR=\t\t\t\tvalue", | |
1143 | "", | 1182 | "", | |
1144 | "VAR=\t\t\tneedlessly", // Nothing to be fixed here, since it looks good. | 1183 | "VAR=\t\t\tneedlessly", // Nothing to be fixed here, since it looks good. | |
1145 | "VAR=\t\t\tdeep", | 1184 | "VAR=\t\t\tdeep", | |
1146 | "VAR=\t\t\tindentation") | 1185 | "VAR=\t\t\tindentation") | |
1147 | 1186 | |||
1148 | var varalign VaralignBlock | 1187 | var varalign VaralignBlock | |
1149 | for _, mkline := range mklines.mklines { | 1188 | for _, mkline := range mklines.mklines { | |
1150 | varalign.Check(mkline) | 1189 | varalign.Process(mkline) | |
1151 | } | 1190 | } | |
1152 | varalign.Finish() | 1191 | varalign.Finish() | |
1153 | 1192 | |||
1154 | t.CheckOutputLines( | 1193 | t.CheckOutputLines( | |
1155 | "NOTE: file.mk:1: Variable values should be aligned with tabs, not spaces.", | 1194 | "NOTE: file.mk:1: Variable values should be aligned with tabs, not spaces.", | |
1156 | "NOTE: file.mk:2: This variable value should be aligned with tabs, not spaces, to column 9.", | 1195 | "NOTE: file.mk:2: This variable value should be aligned with tabs, not spaces, to column 9.", | |
1157 | "NOTE: file.mk:3: This variable value should be aligned to column 9.") | 1196 | "NOTE: file.mk:3: This variable value should be aligned to column 9.") | |
1158 | } | 1197 | } | |
1159 | 1198 | |||
1160 | // For every variable assignment, there is at least one space or tab between the variable | 1199 | // For every variable assignment, there is at least one space or tab between the variable | |
1161 | // name and the value. Even if it is the longest line, and even if the value would start | 1200 | // name and the value. Even if it is the longest line, and even if the value would start | |
1162 | // exactly at a tab stop. | 1201 | // exactly at a tab stop. | |
1163 | func (s *Suite) Test_VaralignBlock_Check__longest_line_no_space(c *check.C) { | 1202 | func (s *Suite) Test_VaralignBlock_Process__longest_line_no_space(c *check.C) { | |
1164 | t := s.Init(c) | 1203 | t := s.Init(c) | |
1165 | 1204 | |||
1166 | t.SetupCommandLine("-Wspace") | 1205 | t.SetupCommandLine("-Wspace") | |
1167 | mklines := t.NewMkLines("file.mk", | 1206 | mklines := t.NewMkLines("file.mk", | |
1168 | "SUBST_CLASSES+= aaaaaaaa", | 1207 | "SUBST_CLASSES+= aaaaaaaa", | |
1169 | "SUBST_STAGE.aaaaaaaa= pre-configure", | 1208 | "SUBST_STAGE.aaaaaaaa= pre-configure", | |
1170 | "SUBST_FILES.aaaaaaaa= *.pl", | 1209 | "SUBST_FILES.aaaaaaaa= *.pl", | |
1171 | "SUBST_FILTER_CMD.aaaaaa=cat") | 1210 | "SUBST_FILTER_CMD.aaaaaa=cat") | |
1172 | 1211 | |||
1173 | var varalign VaralignBlock | 1212 | var varalign VaralignBlock | |
1174 | for _, mkline := range mklines.mklines { | 1213 | for _, mkline := range mklines.mklines { | |
1175 | varalign.Check(mkline) | 1214 | varalign.Process(mkline) | |
1176 | } | 1215 | } | |
1177 | varalign.Finish() | 1216 | varalign.Finish() | |
1178 | 1217 | |||
1179 | t.CheckOutputLines( | 1218 | t.CheckOutputLines( | |
1180 | "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 33.", | 1219 | "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 33.", | |
1181 | "NOTE: file.mk:2: This variable value should be aligned with tabs, not spaces, to column 33.", | 1220 | "NOTE: file.mk:2: This variable value should be aligned with tabs, not spaces, to column 33.", | |
1182 | "NOTE: file.mk:3: This variable value should be aligned with tabs, not spaces, to column 33.", | 1221 | "NOTE: file.mk:3: This variable value should be aligned with tabs, not spaces, to column 33.", | |
1183 | "NOTE: file.mk:4: This variable value should be aligned to column 33.") | 1222 | "NOTE: file.mk:4: This variable value should be aligned to column 33.") | |
1184 | } | 1223 | } | |
1185 | 1224 | |||
1186 | func (s *Suite) Test_VaralignBlock_Check__only_spaces(c *check.C) { | 1225 | func (s *Suite) Test_VaralignBlock_Process__only_spaces(c *check.C) { | |
1187 | t := s.Init(c) | 1226 | t := s.Init(c) | |
1188 | 1227 | |||
1189 | t.SetupCommandLine("-Wspace") | 1228 | t.SetupCommandLine("-Wspace") | |
1190 | mklines := t.NewMkLines("file.mk", | 1229 | mklines := t.NewMkLines("file.mk", | |
1191 | "SUBST_CLASSES+= aaaaaaaa", | 1230 | "SUBST_CLASSES+= aaaaaaaa", | |
1192 | "SUBST_STAGE.aaaaaaaa= pre-configure", | 1231 | "SUBST_STAGE.aaaaaaaa= pre-configure", | |
1193 | "SUBST_FILES.aaaaaaaa= *.pl", | 1232 | "SUBST_FILES.aaaaaaaa= *.pl", | |
1194 | "SUBST_FILTER_CMD.aaaaaaaa= cat") | 1233 | "SUBST_FILTER_CMD.aaaaaaaa= cat") | |
1195 | 1234 | |||
1196 | var varalign VaralignBlock | 1235 | var varalign VaralignBlock | |
1197 | for _, mkline := range mklines.mklines { | 1236 | for _, mkline := range mklines.mklines { | |
1198 | varalign.Check(mkline) | 1237 | varalign.Process(mkline) | |
1199 | } | 1238 | } | |
1200 | varalign.Finish() | 1239 | varalign.Finish() | |
1201 | 1240 | |||
1202 | t.CheckOutputLines( | 1241 | t.CheckOutputLines( | |
1203 | "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 33.", | 1242 | "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 33.", | |
1204 | "NOTE: file.mk:2: This variable value should be aligned with tabs, not spaces, to column 33.", | 1243 | "NOTE: file.mk:2: This variable value should be aligned with tabs, not spaces, to column 33.", | |
1205 | "NOTE: file.mk:3: This variable value should be aligned with tabs, not spaces, to column 33.", | 1244 | "NOTE: file.mk:3: This variable value should be aligned with tabs, not spaces, to column 33.", | |
1206 | "NOTE: file.mk:4: This variable value should be aligned with tabs, not spaces, to column 33.") | 1245 | "NOTE: file.mk:4: This variable value should be aligned with tabs, not spaces, to column 33.") | |
1207 | } | 1246 | } |
@@ -51,27 +51,27 @@ func (vt *VaralignTester) run(autofix bo | @@ -51,27 +51,27 @@ func (vt *VaralignTester) run(autofix bo | |||
51 | cmdline := []string{"-Wall"} | 51 | cmdline := []string{"-Wall"} | |
52 | if autofix { | 52 | if autofix { | |
53 | cmdline = append(cmdline, "--autofix") | 53 | cmdline = append(cmdline, "--autofix") | |
54 | } | 54 | } | |
55 | if vt.source { | 55 | if vt.source { | |
56 | cmdline = append(cmdline, "--source") | 56 | cmdline = append(cmdline, "--source") | |
57 | } | 57 | } | |
58 | t.SetupCommandLine(cmdline...) | 58 | t.SetupCommandLine(cmdline...) | |
59 | 59 | |||
60 | mklines := t.SetupFileMkLines("Makefile", vt.input...) | 60 | mklines := t.SetupFileMkLines("Makefile", vt.input...) | |
61 | 61 | |||
62 | var varalign VaralignBlock | 62 | var varalign VaralignBlock | |
63 | for _, mkline := range mklines.mklines { | 63 | for _, mkline := range mklines.mklines { | |
64 | varalign.Check(mkline) | 64 | varalign.Process(mkline) | |
65 | } | 65 | } | |
66 | varalign.Finish() | 66 | varalign.Finish() | |
67 | 67 | |||
68 | if autofix { | 68 | if autofix { | |
69 | if len(vt.autofixes) > 0 { | 69 | if len(vt.autofixes) > 0 { | |
70 | t.CheckOutputLines(vt.autofixes...) | 70 | t.CheckOutputLines(vt.autofixes...) | |
71 | } else { | 71 | } else { | |
72 | t.CheckOutputEmpty() | 72 | t.CheckOutputEmpty() | |
73 | } | 73 | } | |
74 | 74 | |||
75 | SaveAutofixChanges(mklines.lines) | 75 | SaveAutofixChanges(mklines.lines) | |
76 | t.CheckFileLinesDetab("Makefile", vt.fixed...) | 76 | t.CheckFileLinesDetab("Makefile", vt.fixed...) | |
77 | } else { | 77 | } else { |
@@ -150,28 +150,28 @@ func (pkg *Package) loadPackageMakefile( | @@ -150,28 +150,28 @@ func (pkg *Package) loadPackageMakefile( | |||
150 | 150 | |||
151 | if G.Opts.DumpMakefile { | 151 | if G.Opts.DumpMakefile { | |
152 | G.out.WriteLine("Whole Makefile (with all included files) follows:") | 152 | G.out.WriteLine("Whole Makefile (with all included files) follows:") | |
153 | for _, line := range allLines.lines.Lines { | 153 | for _, line := range allLines.lines.Lines { | |
154 | G.out.WriteLine(line.String()) | 154 | G.out.WriteLine(line.String()) | |
155 | } | 155 | } | |
156 | } | 156 | } | |
157 | 157 | |||
158 | if pkg.vars.Defined("USE_CMAKE") { | 158 | if pkg.vars.Defined("USE_CMAKE") { | |
159 | mainLines.Tools.def("cmake", "", false, AtRunTime) | 159 | mainLines.Tools.def("cmake", "", false, AtRunTime) | |
160 | mainLines.Tools.def("cpack", "", false, AtRunTime) | 160 | mainLines.Tools.def("cpack", "", false, AtRunTime) | |
161 | } | 161 | } | |
162 | 162 | |||
163 | allLines.DetermineUsedVariables() | 163 | allLines.collectUsedVariables() | |
164 | allLines.CheckRedundantVariables() | 164 | allLines.CheckRedundantAssignments() | |
165 | 165 | |||
166 | pkg.Pkgdir, _ = pkg.vars.Value("PKGDIR") | 166 | pkg.Pkgdir, _ = pkg.vars.Value("PKGDIR") | |
167 | pkg.DistinfoFile, _ = pkg.vars.Value("DISTINFO_FILE") | 167 | pkg.DistinfoFile, _ = pkg.vars.Value("DISTINFO_FILE") | |
168 | pkg.Filesdir, _ = pkg.vars.Value("FILESDIR") | 168 | pkg.Filesdir, _ = pkg.vars.Value("FILESDIR") | |
169 | pkg.Patchdir, _ = pkg.vars.Value("PATCHDIR") | 169 | pkg.Patchdir, _ = pkg.vars.Value("PATCHDIR") | |
170 | 170 | |||
171 | // See lang/php/ext.mk | 171 | // See lang/php/ext.mk | |
172 | if varIsDefinedSimilar("PHPEXT_MK") { | 172 | if varIsDefinedSimilar("PHPEXT_MK") { | |
173 | if !varIsDefinedSimilar("USE_PHP_EXT_PATCHES") { | 173 | if !varIsDefinedSimilar("USE_PHP_EXT_PATCHES") { | |
174 | pkg.Patchdir = "patches" | 174 | pkg.Patchdir = "patches" | |
175 | } | 175 | } | |
176 | if varIsDefinedSimilar("PECL_VERSION") { | 176 | if varIsDefinedSimilar("PECL_VERSION") { | |
177 | pkg.DistinfoFile = "distinfo" | 177 | pkg.DistinfoFile = "distinfo" | |
@@ -207,27 +207,27 @@ func (pkg *Package) readMakefile(filenam | @@ -207,27 +207,27 @@ func (pkg *Package) readMakefile(filenam | |||
207 | isMainMakefile := len(mainLines.mklines) == 0 | 207 | isMainMakefile := len(mainLines.mklines) == 0 | |
208 | 208 | |||
209 | result = true | 209 | result = true | |
210 | lineAction := func(mkline MkLine) bool { | 210 | lineAction := func(mkline MkLine) bool { | |
211 | if isMainMakefile { | 211 | if isMainMakefile { | |
212 | mainLines.mklines = append(mainLines.mklines, mkline) | 212 | mainLines.mklines = append(mainLines.mklines, mkline) | |
213 | mainLines.lines.Lines = append(mainLines.lines.Lines, mkline.Line) | 213 | mainLines.lines.Lines = append(mainLines.lines.Lines, mkline.Line) | |
214 | } | 214 | } | |
215 | allLines.mklines = append(allLines.mklines, mkline) | 215 | allLines.mklines = append(allLines.mklines, mkline) | |
216 | allLines.lines.Lines = append(allLines.lines.Lines, mkline.Line) | 216 | allLines.lines.Lines = append(allLines.lines.Lines, mkline.Line) | |
217 | 217 | |||
218 | var includedFile, incDir, incBase string | 218 | var includedFile, incDir, incBase string | |
219 | if mkline.IsInclude() { | 219 | if mkline.IsInclude() { | |
220 | includedFile = resolveVariableRefs(mkline.ResolveVarsInRelativePath(mkline.IncludedFile(), true)) | 220 | includedFile = resolveVariableRefs(mkline.ResolveVarsInRelativePath(mkline.IncludedFile())) | |
221 | if containsVarRef(includedFile) { | 221 | if containsVarRef(includedFile) { | |
222 | if !contains(filename, "/mk/") { | 222 | if !contains(filename, "/mk/") { | |
223 | mkline.Notef("Skipping include file %q. This may result in false warnings.", includedFile) | 223 | mkline.Notef("Skipping include file %q. This may result in false warnings.", includedFile) | |
224 | } | 224 | } | |
225 | includedFile = "" | 225 | includedFile = "" | |
226 | } | 226 | } | |
227 | incDir, incBase = path.Split(includedFile) | 227 | incDir, incBase = path.Split(includedFile) | |
228 | } | 228 | } | |
229 | 229 | |||
230 | if includedFile != "" { | 230 | if includedFile != "" { | |
231 | if mkline.Basename != "buildlink3.mk" { | 231 | if mkline.Basename != "buildlink3.mk" { | |
232 | if m, bl3File := match1(includedFile, `^\.\./\.\./(.*)/buildlink3\.mk$`); m { | 232 | if m, bl3File := match1(includedFile, `^\.\./\.\./(.*)/buildlink3\.mk$`); m { | |
233 | pkg.bl3[bl3File] = mkline.Line | 233 | pkg.bl3[bl3File] = mkline.Line | |
@@ -743,27 +743,27 @@ func (pkg *Package) checkLocallyModified | @@ -743,27 +743,27 @@ func (pkg *Package) checkLocallyModified | |||
743 | NewLineWhole(filename).Warnf("Don't commit changes to this file without asking the OWNER, %s.", owner) | 743 | NewLineWhole(filename).Warnf("Don't commit changes to this file without asking the OWNER, %s.", owner) | |
744 | G.Explain( | 744 | G.Explain( | |
745 | seeGuide("Package components, Makefile", "components.Makefile")) | 745 | seeGuide("Package components, Makefile", "components.Makefile")) | |
746 | } | 746 | } | |
747 | if maintainer != "" { | 747 | if maintainer != "" { | |
748 | NewLineWhole(filename).Notef("Please only commit changes that %s would approve.", maintainer) | 748 | NewLineWhole(filename).Notef("Please only commit changes that %s would approve.", maintainer) | |
749 | G.Explain( | 749 | G.Explain( | |
750 | "See the pkgsrc guide, section \"Package components\",", | 750 | "See the pkgsrc guide, section \"Package components\",", | |
751 | "keyword \"maintainer\", for more information.") | 751 | "keyword \"maintainer\", for more information.") | |
752 | } | 752 | } | |
753 | } | 753 | } | |
754 | } | 754 | } | |
755 | 755 | |||
756 | func (pkg *Package) CheckInclude(mkline MkLine, indentation *Indentation) { | 756 | func (pkg *Package) checkIncludeConditionally(mkline MkLine, indentation *Indentation) { | |
757 | conditionalVars := mkline.ConditionalVars() | 757 | conditionalVars := mkline.ConditionalVars() | |
758 | if len(conditionalVars) == 0 { | 758 | if len(conditionalVars) == 0 { | |
759 | conditionalVars = indentation.Varnames() | 759 | conditionalVars = indentation.Varnames() | |
760 | mkline.SetConditionalVars(conditionalVars) | 760 | mkline.SetConditionalVars(conditionalVars) | |
761 | } | 761 | } | |
762 | 762 | |||
763 | if path.Dir(abspath(mkline.Filename)) == abspath(pkg.File(".")) { | 763 | if path.Dir(abspath(mkline.Filename)) == abspath(pkg.File(".")) { | |
764 | includedFile := mkline.IncludedFile() | 764 | includedFile := mkline.IncludedFile() | |
765 | 765 | |||
766 | if indentation.IsConditional() { | 766 | if indentation.IsConditional() { | |
767 | pkg.conditionalIncludes[includedFile] = mkline | 767 | pkg.conditionalIncludes[includedFile] = mkline | |
768 | if other := pkg.unconditionalIncludes[includedFile]; other != nil { | 768 | if other := pkg.unconditionalIncludes[includedFile]; other != nil { | |
769 | mkline.Warnf("%q is included conditionally here (depending on %s) and unconditionally in %s.", | 769 | mkline.Warnf("%q is included conditionally here (depending on %s) and unconditionally in %s.", |
@@ -521,27 +521,27 @@ func (s *Suite) Test_Package_loadPackage | @@ -521,27 +521,27 @@ func (s *Suite) Test_Package_loadPackage | |||
521 | ".if !defined(PECL_VERSION)", | 521 | ".if !defined(PECL_VERSION)", | |
522 | "DISTINFO_FILE= ${.CURDIR}/${PHPPKGSRCDIR}/distinfo", | 522 | "DISTINFO_FILE= ${.CURDIR}/${PHPPKGSRCDIR}/distinfo", | |
523 | ".endif", | 523 | ".endif", | |
524 | ".if defined(USE_PHP_EXT_PATCHES)", | 524 | ".if defined(USE_PHP_EXT_PATCHES)", | |
525 | "PATCHDIR= ${.CURDIR}/${PHPPKGSRCDIR}/patches", | 525 | "PATCHDIR= ${.CURDIR}/${PHPPKGSRCDIR}/patches", | |
526 | ".endif") | 526 | ".endif") | |
527 | pkg := t.SetupPackage("category/package", | 527 | pkg := t.SetupPackage("category/package", | |
528 | "PECL_VERSION=\t1.1.2", | 528 | "PECL_VERSION=\t1.1.2", | |
529 | ".include \"../../lang/php/ext.mk\"") | 529 | ".include \"../../lang/php/ext.mk\"") | |
530 | 530 | |||
531 | G.CheckDirent(pkg) | 531 | G.CheckDirent(pkg) | |
532 | } | 532 | } | |
533 | 533 | |||
534 | func (s *Suite) Test_Package_CheckInclude__conditional_and_unconditional_include(c *check.C) { | 534 | func (s *Suite) Test_Package_checkIncludeConditionally__conditional_and_unconditional_include(c *check.C) { | |
535 | t := s.Init(c) | 535 | t := s.Init(c) | |
536 | 536 | |||
537 | t.SetupVartypes() | 537 | t.SetupVartypes() | |
538 | t.CreateFileLines("devel/zlib/buildlink3.mk", "") | 538 | t.CreateFileLines("devel/zlib/buildlink3.mk", "") | |
539 | t.CreateFileLines("licenses/gnu-gpl-v2", "") | 539 | t.CreateFileLines("licenses/gnu-gpl-v2", "") | |
540 | t.CreateFileLines("mk/bsd.pkg.mk", "") | 540 | t.CreateFileLines("mk/bsd.pkg.mk", "") | |
541 | t.CreateFileLines("sysutils/coreutils/buildlink3.mk", "") | 541 | t.CreateFileLines("sysutils/coreutils/buildlink3.mk", "") | |
542 | 542 | |||
543 | t.Chdir("category/package") | 543 | t.Chdir("category/package") | |
544 | t.CreateFileLines("Makefile", | 544 | t.CreateFileLines("Makefile", | |
545 | MkRcsID, | 545 | MkRcsID, | |
546 | "", | 546 | "", | |
547 | "COMMENT=\tDescription", | 547 | "COMMENT=\tDescription", | |
@@ -608,27 +608,27 @@ func (s *Suite) Test_Package__include_af | @@ -608,27 +608,27 @@ func (s *Suite) Test_Package__include_af | |||
608 | ".if exists(options.mk)", | 608 | ".if exists(options.mk)", | |
609 | ". include \"options.mk\"", | 609 | ". include \"options.mk\"", | |
610 | ".endif", | 610 | ".endif", | |
611 | "", | 611 | "", | |
612 | ".include \"../../mk/bsd.pkg.mk\"") | 612 | ".include \"../../mk/bsd.pkg.mk\"") | |
613 | 613 | |||
614 | G.checkdirPackage(".") | 614 | G.checkdirPackage(".") | |
615 | 615 | |||
616 | t.CheckOutputLines( | 616 | t.CheckOutputLines( | |
617 | "WARN: Makefile: Neither PLIST nor PLIST.common exist, and PLIST_SRC is unset.", | 617 | "WARN: Makefile: Neither PLIST nor PLIST.common exist, and PLIST_SRC is unset.", | |
618 | "WARN: distinfo: File not found. Please run \""+confMake+" makesum\" or define NO_CHECKSUM=yes in the package Makefile.", | 618 | "WARN: distinfo: File not found. Please run \""+confMake+" makesum\" or define NO_CHECKSUM=yes in the package Makefile.", | |
619 | "ERROR: Makefile: Each package must define its LICENSE.", | 619 | "ERROR: Makefile: Each package must define its LICENSE.", | |
620 | "WARN: Makefile: No COMMENT given.", | 620 | "WARN: Makefile: No COMMENT given.", | |
621 | "ERROR: Makefile:4: \"options.mk\" does not exist.") | 621 | "ERROR: Makefile:4: Relative path \"options.mk\" does not exist.") | |
622 | } | 622 | } | |
623 | 623 | |||
624 | // See https://github.com/rillig/pkglint/issues/1 | 624 | // See https://github.com/rillig/pkglint/issues/1 | |
625 | func (s *Suite) Test_Package_readMakefile__include_other_after_exists(c *check.C) { | 625 | func (s *Suite) Test_Package_readMakefile__include_other_after_exists(c *check.C) { | |
626 | t := s.Init(c) | 626 | t := s.Init(c) | |
627 | 627 | |||
628 | t.SetupVartypes() | 628 | t.SetupVartypes() | |
629 | t.CreateFileLines("mk/bsd.pkg.mk") | 629 | t.CreateFileLines("mk/bsd.pkg.mk") | |
630 | t.CreateFileLines("category/package/Makefile", | 630 | t.CreateFileLines("category/package/Makefile", | |
631 | MkRcsID, | 631 | MkRcsID, | |
632 | "", | 632 | "", | |
633 | ".if exists(options.mk)", | 633 | ".if exists(options.mk)", | |
634 | ". include \"another.mk\"", | 634 | ". include \"another.mk\"", |
@@ -147,27 +147,27 @@ func (ctx *SubstContext) Varassign(mklin | @@ -147,27 +147,27 @@ func (ctx *SubstContext) Varassign(mklin | |||
147 | 147 | |||
148 | case "SUBST_FILES.*": | 148 | case "SUBST_FILES.*": | |
149 | ctx.dupBool(mkline, &ctx.curr.seenFiles, varname, op, value) | 149 | ctx.dupBool(mkline, &ctx.curr.seenFiles, varname, op, value) | |
150 | 150 | |||
151 | case "SUBST_SED.*": | 151 | case "SUBST_SED.*": | |
152 | ctx.dupBool(mkline, &ctx.curr.seenSed, varname, op, value) | 152 | ctx.dupBool(mkline, &ctx.curr.seenSed, varname, op, value) | |
153 | ctx.curr.seenTransform = true | 153 | ctx.curr.seenTransform = true | |
154 | 154 | |||
155 | ctx.suggestSubstVars(mkline) | 155 | ctx.suggestSubstVars(mkline) | |
156 | 156 | |||
157 | case "SUBST_VARS.*": | 157 | case "SUBST_VARS.*": | |
158 | ctx.dupBool(mkline, &ctx.curr.seenVars, varname, op, value) | 158 | ctx.dupBool(mkline, &ctx.curr.seenVars, varname, op, value) | |
159 | ctx.curr.seenTransform = true | 159 | ctx.curr.seenTransform = true | |
160 | for _, substVar := range mkline.ValueFields(value) { | 160 | for _, substVar := range mkline.Fields() { | |
161 | if ctx.vars == nil { | 161 | if ctx.vars == nil { | |
162 | ctx.vars = make(map[string]bool) | 162 | ctx.vars = make(map[string]bool) | |
163 | } | 163 | } | |
164 | ctx.vars[substVar] = true | 164 | ctx.vars[substVar] = true | |
165 | } | 165 | } | |
166 | 166 | |||
167 | case "SUBST_FILTER_CMD.*": | 167 | case "SUBST_FILTER_CMD.*": | |
168 | ctx.dupString(mkline, &ctx.filterCmd, varname, value) | 168 | ctx.dupString(mkline, &ctx.filterCmd, varname, value) | |
169 | ctx.curr.seenTransform = true | 169 | ctx.curr.seenTransform = true | |
170 | } | 170 | } | |
171 | } | 171 | } | |
172 | 172 | |||
173 | func (ctx *SubstContext) Directive(mkline MkLine) { | 173 | func (ctx *SubstContext) Directive(mkline MkLine) { |
@@ -245,52 +245,52 @@ func (s *Suite) Test_Tools__builtin_mk(c | @@ -245,52 +245,52 @@ func (s *Suite) Test_Tools__builtin_mk(c | |||
245 | "_TOOLS_VARNAME.load= LOAD", | 245 | "_TOOLS_VARNAME.load= LOAD", | |
246 | "_TOOLS_VARNAME.run= RUN_CMD", | 246 | "_TOOLS_VARNAME.run= RUN_CMD", | |
247 | "_TOOLS_VARNAME.nowhere= NOWHERE") | 247 | "_TOOLS_VARNAME.nowhere= NOWHERE") | |
248 | t.CreateFileLines("mk/bsd.prefs.mk", | 248 | t.CreateFileLines("mk/bsd.prefs.mk", | |
249 | "USE_TOOLS+= load") | 249 | "USE_TOOLS+= load") | |
250 | t.CreateFileLines("mk/bsd.pkg.mk", | 250 | t.CreateFileLines("mk/bsd.pkg.mk", | |
251 | "USE_TOOLS+= run") | 251 | "USE_TOOLS+= run") | |
252 | t.CreateFileLines("mk/buildlink3/bsd.builtin.mk") | 252 | t.CreateFileLines("mk/buildlink3/bsd.builtin.mk") | |
253 | G.Pkgsrc.LoadInfrastructure() | 253 | G.Pkgsrc.LoadInfrastructure() | |
254 | 254 | |||
255 | // Tools that are defined by pkgsrc as load-time tools | 255 | // Tools that are defined by pkgsrc as load-time tools | |
256 | // may be used in any file at load time. | 256 | // may be used in any file at load time. | |
257 | 257 | |||
258 | mklines := t.SetupFileMkLines("builtin.mk", | 258 | mklines := t.SetupFileMkLines("category/package/builtin.mk", | |
259 | MkRcsID, | 259 | MkRcsID, | |
260 | "", | 260 | "", | |
261 | "VAR!= ${ECHO} 'too early'", | 261 | "VAR!= ${ECHO} 'too early'", | |
262 | "VAR!= ${LOAD} 'too early'", | 262 | "VAR!= ${LOAD} 'too early'", | |
263 | "VAR!= ${RUN_CMD} 'never allowed'", | 263 | "VAR!= ${RUN_CMD} 'never allowed'", | |
264 | "VAR!= ${NOWHERE} 'never allowed'", | 264 | "VAR!= ${NOWHERE} 'never allowed'", | |
265 | "", | 265 | "", | |
266 | ".include \"../../mk/buildlink3/bsd.builtin.mk\"", | 266 | ".include \"../../mk/buildlink3/bsd.builtin.mk\"", | |
267 | "", | 267 | "", | |
268 | "VAR!= ${ECHO} 'valid'", | 268 | "VAR!= ${ECHO} 'valid'", | |
269 | "VAR!= ${LOAD} 'valid'", | 269 | "VAR!= ${LOAD} 'valid'", | |
270 | "VAR!= ${RUN_CMD} 'never allowed'", | 270 | "VAR!= ${RUN_CMD} 'never allowed'", | |
271 | "VAR!= ${NOWHERE} 'never allowed'", | 271 | "VAR!= ${NOWHERE} 'never allowed'", | |
272 | "", | 272 | "", | |
273 | "VAR!= ${VAR}") | 273 | "VAR!= ${VAR}") | |
274 | 274 | |||
275 | mklines.Check() | 275 | mklines.Check() | |
276 | 276 | |||
277 | t.CheckOutputLines( | 277 | t.CheckOutputLines( | |
278 | "WARN: ~/builtin.mk:3: To use the tool ${ECHO} at load time, bsd.prefs.mk has to be included before.", | 278 | "WARN: ~/category/package/builtin.mk:3: To use the tool ${ECHO} at load time, bsd.prefs.mk has to be included before.", | |
279 | "WARN: ~/builtin.mk:4: To use the tool ${LOAD} at load time, bsd.prefs.mk has to be included before.", | 279 | "WARN: ~/category/package/builtin.mk:4: To use the tool ${LOAD} at load time, bsd.prefs.mk has to be included before.", | |
280 | "WARN: ~/builtin.mk:5: The tool ${RUN_CMD} cannot be used at load time.", | 280 | "WARN: ~/category/package/builtin.mk:5: The tool ${RUN_CMD} cannot be used at load time.", | |
281 | "WARN: ~/builtin.mk:6: The tool ${NOWHERE} cannot be used at load time.", | 281 | "WARN: ~/category/package/builtin.mk:6: The tool ${NOWHERE} cannot be used at load time.", | |
282 | "WARN: ~/builtin.mk:12: The tool ${RUN_CMD} cannot be used at load time.", | 282 | "WARN: ~/category/package/builtin.mk:12: The tool ${RUN_CMD} cannot be used at load time.", | |
283 | "WARN: ~/builtin.mk:13: The tool ${NOWHERE} cannot be used at load time.") | 283 | "WARN: ~/category/package/builtin.mk:13: The tool ${NOWHERE} cannot be used at load time.") | |
284 | } | 284 | } | |
285 | 285 | |||
286 | func (s *Suite) Test_Tools__implicit_definition_in_bsd_pkg_mk(c *check.C) { | 286 | func (s *Suite) Test_Tools__implicit_definition_in_bsd_pkg_mk(c *check.C) { | |
287 | t := s.Init(c) | 287 | t := s.Init(c) | |
288 | 288 | |||
289 | t.SetupPkgsrc() | 289 | t.SetupPkgsrc() | |
290 | t.SetupCommandLine("-Wall,no-space") | 290 | t.SetupCommandLine("-Wall,no-space") | |
291 | t.CreateFileLines("mk/tools/defaults.mk", | 291 | t.CreateFileLines("mk/tools/defaults.mk", | |
292 | MkRcsID) // None | 292 | MkRcsID) // None | |
293 | t.CreateFileLines("mk/bsd.prefs.mk", | 293 | t.CreateFileLines("mk/bsd.prefs.mk", | |
294 | "USE_TOOLS+= load") | 294 | "USE_TOOLS+= load") | |
295 | t.CreateFileLines("mk/bsd.pkg.mk", | 295 | t.CreateFileLines("mk/bsd.pkg.mk", | |
296 | "USE_TOOLS+= run") | 296 | "USE_TOOLS+= run") |
@@ -131,27 +131,27 @@ func (src *Pkgsrc) InitVartypes() { | @@ -131,27 +131,27 @@ func (src *Pkgsrc) InitVartypes() { | |||
131 | if !contains(word, "$") { | 131 | if !contains(word, "$") { | |
132 | values[word] = true | 132 | values[word] = true | |
133 | } | 133 | } | |
134 | } | 134 | } | |
135 | } | 135 | } | |
136 | } | 136 | } | |
137 | } | 137 | } | |
138 | } | 138 | } | |
139 | } | 139 | } | |
140 | 140 | |||
141 | if len(values) > 0 { | 141 | if len(values) > 0 { | |
142 | joined := keysJoined(values) | 142 | joined := keysJoined(values) | |
143 | if trace.Tracing { | 143 | if trace.Tracing { | |
144 | trace.Stepf("Enum from %s in: %s", strings.Join(varcanons, " "), filename, joined) | 144 | trace.Stepf("Enum from %s in %s with values: %s", strings.Join(varcanons, " "), filename, joined) | |
145 | } | 145 | } | |
146 | return enum(joined) | 146 | return enum(joined) | |
147 | } | 147 | } | |
148 | 148 | |||
149 | if trace.Tracing { | 149 | if trace.Tracing { | |
150 | trace.Stepf("Enum from default value: %s", defval) | 150 | trace.Stepf("Enum from default value: %s", defval) | |
151 | } | 151 | } | |
152 | return enum(defval) | 152 | return enum(defval) | |
153 | } | 153 | } | |
154 | 154 | |||
155 | // enumFromDirs reads the directories from category, takes all | 155 | // enumFromDirs reads the directories from category, takes all | |
156 | // that have a single number in them and ranks them from earliest | 156 | // that have a single number in them and ranks them from earliest | |
157 | // to latest. | 157 | // to latest. | |
@@ -477,26 +477,27 @@ func (src *Pkgsrc) InitVartypes() { | @@ -477,26 +477,27 @@ func (src *Pkgsrc) InitVartypes() { | |||
477 | usrpkg("WCALC_HTMLDIR", lkNone, BtPathname) | 477 | usrpkg("WCALC_HTMLDIR", lkNone, BtPathname) | |
478 | usrpkg("WCALC_HTMLPATH", lkNone, BtPathname) // URL path | 478 | usrpkg("WCALC_HTMLPATH", lkNone, BtPathname) // URL path | |
479 | usrpkg("WCALC_CGIDIR", lkNone, BtPrefixPathname) | 479 | usrpkg("WCALC_CGIDIR", lkNone, BtPrefixPathname) | |
480 | usrpkg("WCALC_CGIPATH", lkNone, BtPathname) // URL path | 480 | usrpkg("WCALC_CGIPATH", lkNone, BtPathname) // URL path | |
481 | usrpkg("WDM_MANAGERS", lkShell, BtIdentifier) | 481 | usrpkg("WDM_MANAGERS", lkShell, BtIdentifier) | |
482 | usrpkg("X10_PORT", lkNone, BtPathname) | 482 | usrpkg("X10_PORT", lkNone, BtPathname) | |
483 | usrpkg("XAW_TYPE", lkNone, enum("standard 3d xpm neXtaw")) | 483 | usrpkg("XAW_TYPE", lkNone, enum("standard 3d xpm neXtaw")) | |
484 | usrpkg("XLOCK_DEFAULT_MODE", lkNone, BtIdentifier) | 484 | usrpkg("XLOCK_DEFAULT_MODE", lkNone, BtIdentifier) | |
485 | usrpkg("ZSH_STATIC", lkNone, BtYes) | 485 | usrpkg("ZSH_STATIC", lkNone, BtYes) | |
486 | 486 | |||
487 | // some other variables, sorted alphabetically | 487 | // some other variables, sorted alphabetically | |
488 | 488 | |||
489 | acl(".CURDIR", lkNone, BtPathname, "buildlink3.mk:; *: use, use-loadtime") | 489 | acl(".CURDIR", lkNone, BtPathname, "buildlink3.mk:; *: use, use-loadtime") | |
490 | acl(".IMPSRC", lkShell, BtPathname, "buildlink3.mk:; *: use, use-loadtime") | |||
490 | acl(".TARGET", lkNone, BtPathname, "buildlink3.mk:; *: use, use-loadtime") | 491 | acl(".TARGET", lkNone, BtPathname, "buildlink3.mk:; *: use, use-loadtime") | |
491 | acl("@", lkNone, BtPathname, "buildlink3.mk:; *: use, use-loadtime") | 492 | acl("@", lkNone, BtPathname, "buildlink3.mk:; *: use, use-loadtime") | |
492 | acl("ALL_ENV", lkShell, BtShellWord, "") | 493 | acl("ALL_ENV", lkShell, BtShellWord, "") | |
493 | acl("ALTERNATIVES_FILE", lkNone, BtFileName, "") | 494 | acl("ALTERNATIVES_FILE", lkNone, BtFileName, "") | |
494 | acl("ALTERNATIVES_SRC", lkShell, BtPathname, "") | 495 | acl("ALTERNATIVES_SRC", lkShell, BtPathname, "") | |
495 | pkg("APACHE_MODULE", lkNone, BtYes) | 496 | pkg("APACHE_MODULE", lkNone, BtYes) | |
496 | sys("AR", lkNone, BtShellCommand) | 497 | sys("AR", lkNone, BtShellCommand) | |
497 | sys("AS", lkNone, BtShellCommand) | 498 | sys("AS", lkNone, BtShellCommand) | |
498 | pkglist("AUTOCONF_REQD", lkShell, BtVersion) | 499 | pkglist("AUTOCONF_REQD", lkShell, BtVersion) | |
499 | acl("AUTOMAKE_OVERRIDE", lkShell, BtPathmask, "") | 500 | acl("AUTOMAKE_OVERRIDE", lkShell, BtPathmask, "") | |
500 | pkglist("AUTOMAKE_REQD", lkShell, BtVersion) | 501 | pkglist("AUTOMAKE_REQD", lkShell, BtVersion) | |
501 | pkg("AUTO_MKDIRS", lkNone, BtYesNo) | 502 | pkg("AUTO_MKDIRS", lkNone, BtYesNo) | |
502 | usr("BATCH", lkNone, BtYes) | 503 | usr("BATCH", lkNone, BtYes) |
@@ -206,27 +206,27 @@ func (s *Suite) Test_VartypeCheck_Depend | @@ -206,27 +206,27 @@ func (s *Suite) Test_VartypeCheck_Depend | |||
206 | "broken[0-9]*../../x11/alacarte", | 206 | "broken[0-9]*../../x11/alacarte", | |
207 | "broken>=:../../x11/alacarte", | 207 | "broken>=:../../x11/alacarte", | |
208 | "broken=0:../../x11/alacarte", | 208 | "broken=0:../../x11/alacarte", | |
209 | "broken=:../../x11/alacarte", | 209 | "broken=:../../x11/alacarte", | |
210 | "broken-:../../x11/alacarte", | 210 | "broken-:../../x11/alacarte", | |
211 | "broken>:../../x11/alacarte", | 211 | "broken>:../../x11/alacarte", | |
212 | "gtk2+>=2.16:../../x11/alacarte", | 212 | "gtk2+>=2.16:../../x11/alacarte", | |
213 | "gettext-[0-9]*:../../devel/gettext", | 213 | "gettext-[0-9]*:../../devel/gettext", | |
214 | "gmake-[0-9]*:../../devel/gmake") | 214 | "gmake-[0-9]*:../../devel/gmake") | |
215 | 215 | |||
216 | vt.Output( | 216 | vt.Output( | |
217 | "WARN: ~/category/package/filename.mk:1: Invalid dependency pattern with path \"Perl\".", | 217 | "WARN: ~/category/package/filename.mk:1: Invalid dependency pattern with path \"Perl\".", | |
218 | "WARN: ~/category/package/filename.mk:2: Dependencies should have the form \"../../category/package\".", | 218 | "WARN: ~/category/package/filename.mk:2: Dependencies should have the form \"../../category/package\".", | |
219 | "ERROR: ~/category/package/filename.mk:3: \"../../lang/perl5\" does not exist.", | 219 | "ERROR: ~/category/package/filename.mk:3: Relative path \"../../lang/perl5\" does not exist.", | |
220 | "ERROR: ~/category/package/filename.mk:3: There is no package in \"lang/perl5\".", | 220 | "ERROR: ~/category/package/filename.mk:3: There is no package in \"lang/perl5\".", | |
221 | "WARN: ~/category/package/filename.mk:3: Please use USE_TOOLS+=perl:run instead of this dependency.", | 221 | "WARN: ~/category/package/filename.mk:3: Please use USE_TOOLS+=perl:run instead of this dependency.", | |
222 | "WARN: ~/category/package/filename.mk:4: Invalid dependency pattern \"broken0.12.1\".", | 222 | "WARN: ~/category/package/filename.mk:4: Invalid dependency pattern \"broken0.12.1\".", | |
223 | "WARN: ~/category/package/filename.mk:5: Invalid dependency pattern \"broken[0-9]*\".", | 223 | "WARN: ~/category/package/filename.mk:5: Invalid dependency pattern \"broken[0-9]*\".", | |
224 | "WARN: ~/category/package/filename.mk:6: Invalid dependency pattern with path \"broken[0-9]*../../x11/alacarte\".", | 224 | "WARN: ~/category/package/filename.mk:6: Invalid dependency pattern with path \"broken[0-9]*../../x11/alacarte\".", | |
225 | "WARN: ~/category/package/filename.mk:7: Invalid dependency pattern \"broken>=\".", | 225 | "WARN: ~/category/package/filename.mk:7: Invalid dependency pattern \"broken>=\".", | |
226 | "WARN: ~/category/package/filename.mk:8: Invalid dependency pattern \"broken=0\".", | 226 | "WARN: ~/category/package/filename.mk:8: Invalid dependency pattern \"broken=0\".", | |
227 | "WARN: ~/category/package/filename.mk:9: Invalid dependency pattern \"broken=\".", | 227 | "WARN: ~/category/package/filename.mk:9: Invalid dependency pattern \"broken=\".", | |
228 | "WARN: ~/category/package/filename.mk:10: Invalid dependency pattern \"broken-\".", | 228 | "WARN: ~/category/package/filename.mk:10: Invalid dependency pattern \"broken-\".", | |
229 | "WARN: ~/category/package/filename.mk:11: Invalid dependency pattern \"broken>\".", | 229 | "WARN: ~/category/package/filename.mk:11: Invalid dependency pattern \"broken>\".", | |
230 | "WARN: ~/category/package/filename.mk:13: Please use USE_TOOLS+=msgfmt instead of this dependency.", | 230 | "WARN: ~/category/package/filename.mk:13: Please use USE_TOOLS+=msgfmt instead of this dependency.", | |
231 | "WARN: ~/category/package/filename.mk:14: Please use USE_TOOLS+=gmake instead of this dependency.") | 231 | "WARN: ~/category/package/filename.mk:14: Please use USE_TOOLS+=gmake instead of this dependency.") | |
232 | } | 232 | } | |
@@ -295,27 +295,27 @@ func (s *Suite) Test_VartypeCheck_Enum__ | @@ -295,27 +295,27 @@ func (s *Suite) Test_VartypeCheck_Enum__ | |||
295 | mklines := t.NewMkLines("module.mk", | 295 | mklines := t.NewMkLines("module.mk", | |
296 | MkRcsID, | 296 | MkRcsID, | |
297 | "", | 297 | "", | |
298 | ".if !empty(MACHINE_ARCH:Mi386) || ${MACHINE_ARCH} == i386", | 298 | ".if !empty(MACHINE_ARCH:Mi386) || ${MACHINE_ARCH} == i386", | |
299 | ".endif", | 299 | ".endif", | |
300 | ".if !empty(PKGSRC_COMPILER:Mclang) || ${PKGSRC_COMPILER} == clang", | 300 | ".if !empty(PKGSRC_COMPILER:Mclang) || ${PKGSRC_COMPILER} == clang", | |
301 | ".endif", | 301 | ".endif", | |
302 | ".if ${MACHINE_ARCH:Ni386:Nx86_64:Nsparc64}", | 302 | ".if ${MACHINE_ARCH:Ni386:Nx86_64:Nsparc64}", | |
303 | ".endif") | 303 | ".endif") | |
304 | 304 | |||
305 | mklines.Check() | 305 | mklines.Check() | |
306 | 306 | |||
307 | t.CheckOutputLines( | 307 | t.CheckOutputLines( | |
308 | "NOTE: module.mk:3: MACHINE_ARCH should be compared using == instead of the :M or :N modifier without wildcards.", | 308 | "NOTE: module.mk:3: MACHINE_ARCH should be compared using == instead of matching against \":Mi386\".", | |
309 | "WARN: module.mk:5: Use ${PKGSRC_COMPILER:Mclang} instead of the == operator.") | 309 | "WARN: module.mk:5: Use ${PKGSRC_COMPILER:Mclang} instead of the == operator.") | |
310 | } | 310 | } | |
311 | 311 | |||
312 | func (s *Suite) Test_VartypeCheck_FetchURL(c *check.C) { | 312 | func (s *Suite) Test_VartypeCheck_FetchURL(c *check.C) { | |
313 | t := s.Init(c) | 313 | t := s.Init(c) | |
314 | vt := NewVartypeCheckTester(t, (*VartypeCheck).FetchURL) | 314 | vt := NewVartypeCheckTester(t, (*VartypeCheck).FetchURL) | |
315 | 315 | |||
316 | t.SetupMasterSite("MASTER_SITE_GNU", "http://ftp.gnu.org/pub/gnu/") | 316 | t.SetupMasterSite("MASTER_SITE_GNU", "http://ftp.gnu.org/pub/gnu/") | |
317 | t.SetupMasterSite("MASTER_SITE_GITHUB", "https://github.com/") | 317 | t.SetupMasterSite("MASTER_SITE_GITHUB", "https://github.com/") | |
318 | 318 | |||
319 | vt.Varname("MASTER_SITES") | 319 | vt.Varname("MASTER_SITES") | |
320 | vt.Values( | 320 | vt.Values( | |
321 | "https://github.com/example/project/", | 321 | "https://github.com/example/project/", | |
@@ -514,27 +514,27 @@ func (s *Suite) Test_VartypeCheck_LdFlag | @@ -514,27 +514,27 @@ func (s *Suite) Test_VartypeCheck_LdFlag | |||
514 | vt.Output( | 514 | vt.Output( | |
515 | "WARN: filename:4: Unknown linker flag \"-unknown\".", | 515 | "WARN: filename:4: Unknown linker flag \"-unknown\".", | |
516 | "WARN: filename:5: Linker flag \"no-hyphen\" should start with a hyphen.", | 516 | "WARN: filename:5: Linker flag \"no-hyphen\" should start with a hyphen.", | |
517 | "WARN: filename:6: Please use \"${COMPILER_RPATH_FLAG}\" instead of \"-Wl,--rpath\".") | 517 | "WARN: filename:6: Please use \"${COMPILER_RPATH_FLAG}\" instead of \"-Wl,--rpath\".") | |
518 | } | 518 | } | |
519 | 519 | |||
520 | func (s *Suite) Test_VartypeCheck_License(c *check.C) { | 520 | func (s *Suite) Test_VartypeCheck_License(c *check.C) { | |
521 | t := s.Init(c) | 521 | t := s.Init(c) | |
522 | t.SetupPkgsrc() // Adds the gnu-gpl-v2 and 2-clause-bsd licenses | 522 | t.SetupPkgsrc() // Adds the gnu-gpl-v2 and 2-clause-bsd licenses | |
523 | 523 | |||
524 | G.Mk = t.NewMkLines("perl5.mk", | 524 | G.Mk = t.NewMkLines("perl5.mk", | |
525 | MkRcsID, | 525 | MkRcsID, | |
526 | "PERL5_LICENSE= gnu-gpl-v2 OR artistic") | 526 | "PERL5_LICENSE= gnu-gpl-v2 OR artistic") | |
527 | G.Mk.DetermineDefinedVariables() | 527 | G.Mk.collectDefinedVariables() | |
528 | 528 | |||
529 | vt := NewVartypeCheckTester(t, (*VartypeCheck).License) | 529 | vt := NewVartypeCheckTester(t, (*VartypeCheck).License) | |
530 | 530 | |||
531 | vt.Varname("LICENSE") | 531 | vt.Varname("LICENSE") | |
532 | vt.Values( | 532 | vt.Values( | |
533 | "gnu-gpl-v2", | 533 | "gnu-gpl-v2", | |
534 | "AND mit", | 534 | "AND mit", | |
535 | "${PERL5_LICENSE}", // Is properly resolved, see perl5.mk above. | 535 | "${PERL5_LICENSE}", // Is properly resolved, see perl5.mk above. | |
536 | "${UNKNOWN_LICENSE}") | 536 | "${UNKNOWN_LICENSE}") | |
537 | 537 | |||
538 | vt.Output( | 538 | vt.Output( | |
539 | "ERROR: filename:2: Parse error for license condition \"AND mit\".", | 539 | "ERROR: filename:2: Parse error for license condition \"AND mit\".", | |
540 | "WARN: filename:3: License file ~/licenses/artistic does not exist.", | 540 | "WARN: filename:3: License file ~/licenses/artistic does not exist.", | |
@@ -743,29 +743,29 @@ func (s *Suite) Test_VartypeCheck_PkgPat | @@ -743,29 +743,29 @@ func (s *Suite) Test_VartypeCheck_PkgPat | |||
743 | vt := NewVartypeCheckTester(t, (*VartypeCheck).PkgPath) | 743 | vt := NewVartypeCheckTester(t, (*VartypeCheck).PkgPath) | |
744 | 744 | |||
745 | t.CreateFileLines("category/other-package/Makefile") | 745 | t.CreateFileLines("category/other-package/Makefile") | |
746 | t.Chdir("category/package") | 746 | t.Chdir("category/package") | |
747 | 747 | |||
748 | vt.Varname("PKGPATH") | 748 | vt.Varname("PKGPATH") | |
749 | vt.Values( | 749 | vt.Values( | |
750 | "category/other-package", | 750 | "category/other-package", | |
751 | "${OTHER_VAR}", | 751 | "${OTHER_VAR}", | |
752 | "invalid", | 752 | "invalid", | |
753 | "../../invalid/relative") | 753 | "../../invalid/relative") | |
754 | 754 | |||
755 | vt.Output( | 755 | vt.Output( | |
756 | "ERROR: filename:3: \"../../invalid\" does not exist.", | 756 | "ERROR: filename:3: Relative path \"../../invalid\" does not exist.", | |
757 | "WARN: filename:3: \"../../invalid\" is not a valid relative package directory.", | 757 | "WARN: filename:3: \"../../invalid\" is not a valid relative package directory.", | |
758 | "ERROR: filename:4: \"../../../../invalid/relative\" does not exist.", | 758 | "ERROR: filename:4: Relative path \"../../../../invalid/relative\" does not exist.", | |
759 | "WARN: filename:4: \"../../../../invalid/relative\" is not a valid relative package directory.") | 759 | "WARN: filename:4: \"../../../../invalid/relative\" is not a valid relative package directory.") | |
760 | } | 760 | } | |
761 | 761 | |||
762 | func (s *Suite) Test_VartypeCheck_PkgRevision(c *check.C) { | 762 | func (s *Suite) Test_VartypeCheck_PkgRevision(c *check.C) { | |
763 | vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).PkgRevision) | 763 | vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).PkgRevision) | |
764 | 764 | |||
765 | vt.Varname("PKGREVISION") | 765 | vt.Varname("PKGREVISION") | |
766 | vt.Values( | 766 | vt.Values( | |
767 | "3a") | 767 | "3a") | |
768 | 768 | |||
769 | vt.Output( | 769 | vt.Output( | |
770 | "WARN: filename:1: PKGREVISION must be a positive integer number.", | 770 | "WARN: filename:1: PKGREVISION must be a positive integer number.", | |
771 | "ERROR: filename:1: PKGREVISION only makes sense directly in the package Makefile.") | 771 | "ERROR: filename:1: PKGREVISION only makes sense directly in the package Makefile.") | |
@@ -852,29 +852,29 @@ func (s *Suite) Test_VartypeCheck_Relati | @@ -852,29 +852,29 @@ func (s *Suite) Test_VartypeCheck_Relati | |||
852 | 852 | |||
853 | t.CreateFileLines("category/other-package/Makefile") | 853 | t.CreateFileLines("category/other-package/Makefile") | |
854 | t.Chdir("category/package") | 854 | t.Chdir("category/package") | |
855 | 855 | |||
856 | vt.Varname("DISTINFO_FILE") | 856 | vt.Varname("DISTINFO_FILE") | |
857 | vt.Values( | 857 | vt.Values( | |
858 | "category/other-package", | 858 | "category/other-package", | |
859 | "../../category/other-package", | 859 | "../../category/other-package", | |
860 | "${OTHER_VAR}", | 860 | "${OTHER_VAR}", | |
861 | "invalid", | 861 | "invalid", | |
862 | "../../invalid/relative") | 862 | "../../invalid/relative") | |
863 | 863 | |||
864 | vt.Output( | 864 | vt.Output( | |
865 | "ERROR: filename:1: \"category/other-package\" does not exist.", | 865 | "ERROR: filename:1: Relative path \"category/other-package\" does not exist.", | |
866 | "ERROR: filename:4: \"invalid\" does not exist.", | 866 | "ERROR: filename:4: Relative path \"invalid\" does not exist.", | |
867 | "ERROR: filename:5: \"../../invalid/relative\" does not exist.") | 867 | "ERROR: filename:5: Relative path \"../../invalid/relative\" does not exist.") | |
868 | } | 868 | } | |
869 | 869 | |||
870 | func (s *Suite) Test_VartypeCheck_Restricted(c *check.C) { | 870 | func (s *Suite) Test_VartypeCheck_Restricted(c *check.C) { | |
871 | vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Restricted) | 871 | vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Restricted) | |
872 | 872 | |||
873 | vt.Varname("NO_BIN_ON_CDROM") | 873 | vt.Varname("NO_BIN_ON_CDROM") | |
874 | vt.Values( | 874 | vt.Values( | |
875 | "May only be distributed free of charge") | 875 | "May only be distributed free of charge") | |
876 | 876 | |||
877 | vt.Output( | 877 | vt.Output( | |
878 | "WARN: filename:1: The only valid value for NO_BIN_ON_CDROM is ${RESTRICTED}.") | 878 | "WARN: filename:1: The only valid value for NO_BIN_ON_CDROM is ${RESTRICTED}.") | |
879 | } | 879 | } | |
880 | 880 |