Sun May 26 14:05:57 2019 UTC ()
pkgtools/pkglint: update to 5.7.12

Changes since 5.7.11:

* Fixed an alignment bug when pkglint replaced SUBST_SED with
  SUBST_VARS.

* Added many test cases.


(rillig)
diff -r1.22 -r1.23 pkgsrc/pkgtools/pkglint/files/autofix.go
diff -r1.23 -r1.24 pkgsrc/pkgtools/pkglint/files/autofix_test.go
diff -r1.23 -r1.24 pkgsrc/pkgtools/pkglint/files/substcontext.go
diff -r1.57 -r1.58 pkgsrc/pkgtools/pkglint/files/mkline_test.go
diff -r1.39 -r1.40 pkgsrc/pkgtools/pkglint/files/mklinechecker.go
diff -r1.35 -r1.36 pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go
diff -r1.48 -r1.49 pkgsrc/pkgtools/pkglint/files/mklines.go
diff -r1.48 -r1.49 pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go
diff -r1.26 -r1.27 pkgsrc/pkgtools/pkglint/files/pkgsrc.go
diff -r1.4 -r1.5 pkgsrc/pkgtools/pkglint/files/redundantscope.go
diff -r1.40 -r1.41 pkgsrc/pkgtools/pkglint/files/shell.go
diff -r1.46 -r1.47 pkgsrc/pkgtools/pkglint/files/shell_test.go
diff -r1.24 -r1.25 pkgsrc/pkgtools/pkglint/files/substcontext_test.go
diff -r1.14 -r1.15 pkgsrc/pkgtools/pkglint/files/tools.go
diff -r1.14 -r1.15 pkgsrc/pkgtools/pkglint/files/vardefs_test.go
diff -r1.44 -r1.45 pkgsrc/pkgtools/pkglint/files/util.go
diff -r1.28 -r1.29 pkgsrc/pkgtools/pkglint/files/util_test.go
diff -r1.2 -r1.3 pkgsrc/pkgtools/pkglint/files/var_test.go
diff -r1.65 -r1.66 pkgsrc/pkgtools/pkglint/files/vardefs.go
diff -r1.31 -r1.32 pkgsrc/pkgtools/pkglint/files/vartype.go
diff -r1.18 -r1.19 pkgsrc/pkgtools/pkglint/files/vartype_test.go

cvs diff -r1.22 -r1.23 pkgsrc/pkgtools/pkglint/files/Attic/autofix.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/autofix.go 2019/05/21 17:59:48 1.22
+++ pkgsrc/pkgtools/pkglint/files/Attic/autofix.go 2019/05/26 14:05:57 1.23
@@ -90,26 +90,35 @@ func (fix *Autofix) Replace(from string, @@ -90,26 +90,35 @@ func (fix *Autofix) Replace(from string,
90// ReplaceAfter replaces the text "prefix+from" with "prefix+to", a single time. 90// ReplaceAfter replaces the text "prefix+from" with "prefix+to", a single time.
91// In the diagnostic, only the replacement of "from" with "to" is mentioned. 91// In the diagnostic, only the replacement of "from" with "to" is mentioned.
92func (fix *Autofix) ReplaceAfter(prefix, from string, to string) { 92func (fix *Autofix) ReplaceAfter(prefix, from string, to string) {
93 fix.assertRealLine() 93 fix.assertRealLine()
94 if fix.skip() { 94 if fix.skip() {
95 return 95 return
96 } 96 }
97 97
98 for _, rawLine := range fix.line.raw { 98 for _, rawLine := range fix.line.raw {
99 replaced := strings.Replace(rawLine.textnl, prefix+from, prefix+to, 1) 99 replaced := strings.Replace(rawLine.textnl, prefix+from, prefix+to, 1)
100 if replaced != rawLine.textnl { 100 if replaced != rawLine.textnl {
101 if G.Logger.IsAutofix() { 101 if G.Logger.IsAutofix() {
102 rawLine.textnl = replaced 102 rawLine.textnl = replaced
 103
 104 // Fix the parsed text as well.
 105 // This is only approximate and won't work in some edge cases
 106 // that involve escaped comments or replacements across line breaks.
 107 //
 108 // TODO: Do this properly by parsing the whole line again,
 109 // and ideally everything that depends on the parsed line.
 110 // This probably requires a generic notification mechanism.
 111 fix.line.Text = strings.Replace(fix.line.Text, prefix+from, prefix+to, 1)
103 } 112 }
104 fix.Describef(rawLine.Lineno, "Replacing %q with %q.", from, to) 113 fix.Describef(rawLine.Lineno, "Replacing %q with %q.", from, to)
105 return 114 return
106 } 115 }
107 } 116 }
108} 117}
109 118
110// ReplaceRegex replaces the first howOften or all occurrences (if negative) 119// ReplaceRegex replaces the first howOften or all occurrences (if negative)
111// of the `from` pattern with the fixed string `toText`. 120// of the `from` pattern with the fixed string `toText`.
112// 121//
113// Placeholders like `$1` are _not_ expanded in the `toText`. 122// Placeholders like `$1` are _not_ expanded in the `toText`.
114// (If you know how to do the expansion correctly, feel free to implement it.) 123// (If you know how to do the expansion correctly, feel free to implement it.)
115func (fix *Autofix) ReplaceRegex(from regex.Pattern, toText string, howOften int) { 124func (fix *Autofix) ReplaceRegex(from regex.Pattern, toText string, howOften int) {
@@ -131,26 +140,45 @@ func (fix *Autofix) ReplaceRegex(from re @@ -131,26 +140,45 @@ func (fix *Autofix) ReplaceRegex(from re
131 return toText 140 return toText
132 } 141 }
133 142
134 replaced := replaceAllFunc(rawLine.textnl, from, replace) 143 replaced := replaceAllFunc(rawLine.textnl, from, replace)
135 if replaced != rawLine.textnl { 144 if replaced != rawLine.textnl {
136 if G.Logger.IsAutofix() { 145 if G.Logger.IsAutofix() {
137 rawLine.textnl = replaced 146 rawLine.textnl = replaced
138 } 147 }
139 for _, fromText := range froms { 148 for _, fromText := range froms {
140 fix.Describef(rawLine.Lineno, "Replacing %q with %q.", fromText, toText) 149 fix.Describef(rawLine.Lineno, "Replacing %q with %q.", fromText, toText)
141 } 150 }
142 } 151 }
143 } 152 }
 153
 154 // Fix the parsed text as well.
 155 // This is only approximate and won't work in some edge cases
 156 // that involve escaped comments or replacements across line breaks.
 157 //
 158 // TODO: Do this properly by parsing the whole line again,
 159 // and ideally everything that depends on the parsed line.
 160 // This probably requires a generic notification mechanism.
 161 done = 0
 162 fix.line.Text = replaceAllFunc(
 163 fix.line.Text,
 164 from,
 165 func(fromText string) string {
 166 if howOften >= 0 && done >= howOften {
 167 return fromText
 168 }
 169 done++
 170 return toText
 171 })
144} 172}
145 173
146// Custom runs a custom fix action, unless the fix is skipped anyway 174// Custom runs a custom fix action, unless the fix is skipped anyway
147// because of the --only option. 175// because of the --only option.
148// 176//
149// The fixer function must check whether it can actually fix something, 177// The fixer function must check whether it can actually fix something,
150// and if so, call Describef to describe the actual fix. 178// and if so, call Describef to describe the actual fix.
151// 179//
152// If showAutofix and autofix are both false, the fix must only be 180// If showAutofix and autofix are both false, the fix must only be
153// described by calling Describef. No observable modification must be done, 181// described by calling Describef. No observable modification must be done,
154// not even in memory. 182// not even in memory.
155// 183//
156// If showAutofix is true but autofix is false, the fix should be done in 184// If showAutofix is true but autofix is false, the fix should be done in

cvs diff -r1.23 -r1.24 pkgsrc/pkgtools/pkglint/files/Attic/autofix_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/autofix_test.go 2019/05/21 17:59:48 1.23
+++ pkgsrc/pkgtools/pkglint/files/Attic/autofix_test.go 2019/05/26 14:05:57 1.24
@@ -955,26 +955,74 @@ func (s *Suite) Test_Autofix_Apply__sour @@ -955,26 +955,74 @@ func (s *Suite) Test_Autofix_Apply__sour
955 955
956 t.CheckOutputLines( 956 t.CheckOutputLines(
957 "NOTE: filename:5: This line is quite short.", 957 "NOTE: filename:5: This line is quite short.",
958 "AUTOFIX: filename:5: Replacing \"text\" with \"replacement\".", 958 "AUTOFIX: filename:5: Replacing \"text\" with \"replacement\".",
959 "-\ttext", 959 "-\ttext",
960 "+\treplacement", 960 "+\treplacement",
961 "", 961 "",
962 "WARN: filename:5: Follow-up warning, separated.", 962 "WARN: filename:5: Follow-up warning, separated.",
963 "AUTOFIX: filename:5: Replacing \"replacement\" with \"text again\".", 963 "AUTOFIX: filename:5: Replacing \"replacement\" with \"text again\".",
964 "-\ttext", 964 "-\ttext",
965 "+\ttext again") 965 "+\ttext again")
966} 966}
967 967
 968// After fixing part of a line, the whole line needs to be parsed again.
 969//
 970// As of May 2019, this is not done yet.
 971func (s *Suite) Test_Autofix_Apply__text_after_replacing_string(c *check.C) {
 972 t := s.Init(c)
 973
 974 t.SetUpCommandLine("-Wall", "--autofix")
 975 mkline := t.NewMkLine("filename.mk", 123, "VAR=\tvalue")
 976
 977 fix := mkline.Autofix()
 978 fix.Notef("Just a demo.")
 979 fix.Replace("value", "new value")
 980 fix.Apply()
 981
 982 t.CheckOutputLines(
 983 "AUTOFIX: filename.mk:123: Replacing \"value\" with \"new value\".")
 984
 985 t.Check(mkline.raw[0].textnl, equals, "VAR=\tnew value\n")
 986 t.Check(mkline.raw[0].orignl, equals, "VAR=\tvalue\n")
 987 t.Check(mkline.Text, equals, "VAR=\tnew value")
 988 // FIXME: should be updated as well.
 989 t.Check(mkline.Value(), equals, "value")
 990}
 991
 992// After fixing part of a line, the whole line needs to be parsed again.
 993//
 994// As of May 2019, this is not done yet.
 995func (s *Suite) Test_Autofix_Apply__text_after_replacing_regex(c *check.C) {
 996 t := s.Init(c)
 997
 998 t.SetUpCommandLine("-Wall", "--autofix")
 999 mkline := t.NewMkLine("filename.mk", 123, "VAR=\tvalue")
 1000
 1001 fix := mkline.Autofix()
 1002 fix.Notef("Just a demo.")
 1003 fix.ReplaceRegex(`va...`, "new value", -1)
 1004 fix.Apply()
 1005
 1006 t.CheckOutputLines(
 1007 "AUTOFIX: filename.mk:123: Replacing \"value\" with \"new value\".")
 1008
 1009 t.Check(mkline.raw[0].textnl, equals, "VAR=\tnew value\n")
 1010 t.Check(mkline.raw[0].orignl, equals, "VAR=\tvalue\n")
 1011 t.Check(mkline.Text, equals, "VAR=\tnew value")
 1012 // FIXME: should be updated as well.
 1013 t.Check(mkline.Value(), equals, "value")
 1014}
 1015
968func (s *Suite) Test_Autofix_Realign__wrong_line_type(c *check.C) { 1016func (s *Suite) Test_Autofix_Realign__wrong_line_type(c *check.C) {
969 t := s.Init(c) 1017 t := s.Init(c)
970 1018
971 mklines := t.NewMkLines("file.mk", 1019 mklines := t.NewMkLines("file.mk",
972 MkRcsID, 1020 MkRcsID,
973 ".if \\", 1021 ".if \\",
974 "${PKGSRC_RUN_TESTS}") 1022 "${PKGSRC_RUN_TESTS}")
975 1023
976 mkline := mklines.mklines[1] 1024 mkline := mklines.mklines[1]
977 fix := mkline.Autofix() 1025 fix := mkline.Autofix()
978 1026
979 t.ExpectPanic( 1027 t.ExpectPanic(
980 func() { fix.Realign(mkline, 16) }, 1028 func() { fix.Realign(mkline, 16) },

cvs diff -r1.23 -r1.24 pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go 2019/04/20 17:43:25 1.23
+++ pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go 2019/05/26 14:05:57 1.24
@@ -1,19 +1,16 @@ @@ -1,19 +1,16 @@
1package pkglint 1package pkglint
2 2
3import ( 3import "netbsd.org/pkglint/textproc"
4 "netbsd.org/pkglint/textproc" 
5 "strings" 
6) 
7 4
8// SubstContext records the state of a block of variable assignments 5// SubstContext records the state of a block of variable assignments
9// that make up a SUBST class (see `mk/subst.mk`). 6// that make up a SUBST class (see `mk/subst.mk`).
10type SubstContext struct { 7type SubstContext struct {
11 id string 8 id string
12 stage string 9 stage string
13 message string 10 message string
14 curr *SubstContextStats 11 curr *SubstContextStats
15 inAllBranches SubstContextStats 12 inAllBranches SubstContextStats
16 filterCmd string 13 filterCmd string
17 vars map[string]bool 14 vars map[string]bool
18} 15}
19 16
@@ -37,26 +34,37 @@ func (st *SubstContextStats) And(other * @@ -37,26 +34,37 @@ func (st *SubstContextStats) And(other *
37 st.seenFiles = st.seenFiles && other.seenFiles 34 st.seenFiles = st.seenFiles && other.seenFiles
38 st.seenSed = st.seenSed && other.seenSed 35 st.seenSed = st.seenSed && other.seenSed
39 st.seenVars = st.seenVars && other.seenVars 36 st.seenVars = st.seenVars && other.seenVars
40 st.seenTransform = st.seenTransform && other.seenTransform 37 st.seenTransform = st.seenTransform && other.seenTransform
41} 38}
42 39
43func (st *SubstContextStats) Or(other SubstContextStats) { 40func (st *SubstContextStats) Or(other SubstContextStats) {
44 st.seenFiles = st.seenFiles || other.seenFiles 41 st.seenFiles = st.seenFiles || other.seenFiles
45 st.seenSed = st.seenSed || other.seenSed 42 st.seenSed = st.seenSed || other.seenSed
46 st.seenVars = st.seenVars || other.seenVars 43 st.seenVars = st.seenVars || other.seenVars
47 st.seenTransform = st.seenTransform || other.seenTransform 44 st.seenTransform = st.seenTransform || other.seenTransform
48} 45}
49 46
 47func (ctx *SubstContext) Process(mkline MkLine) {
 48 switch {
 49 case mkline.IsEmpty():
 50 ctx.Finish(mkline)
 51 case mkline.IsVarassign():
 52 ctx.Varassign(mkline)
 53 case mkline.IsDirective():
 54 ctx.Directive(mkline)
 55 }
 56}
 57
50func (ctx *SubstContext) Varassign(mkline MkLine) { 58func (ctx *SubstContext) Varassign(mkline MkLine) {
51 if trace.Tracing { 59 if trace.Tracing {
52 trace.Stepf("SubstContext.Varassign curr=%v all=%v", ctx.curr, ctx.inAllBranches) 60 trace.Stepf("SubstContext.Varassign curr=%v all=%v", ctx.curr, ctx.inAllBranches)
53 } 61 }
54 62
55 varname := mkline.Varname() 63 varname := mkline.Varname()
56 varcanon := mkline.Varcanon() 64 varcanon := mkline.Varcanon()
57 varparam := mkline.Varparam() 65 varparam := mkline.Varparam()
58 op := mkline.Op() 66 op := mkline.Op()
59 value := mkline.Value() 67 value := mkline.Value()
60 if varcanon == "SUBST_CLASSES" || varcanon == "SUBST_CLASSES.*" { 68 if varcanon == "SUBST_CLASSES" || varcanon == "SUBST_CLASSES.*" {
61 classes := mkline.ValueFields(value) 69 classes := mkline.ValueFields(value)
62 if len(classes) > 1 { 70 if len(classes) > 1 {
@@ -193,30 +201,27 @@ func (ctx *SubstContext) Directive(mklin @@ -193,30 +201,27 @@ func (ctx *SubstContext) Directive(mklin
193 } 201 }
194 if dir == "if" || dir == "elif" || dir == "else" { 202 if dir == "if" || dir == "elif" || dir == "else" {
195 ctx.curr = ctx.curr.Copy() 203 ctx.curr = ctx.curr.Copy()
196 } 204 }
197 if dir == "endif" { 205 if dir == "endif" {
198 ctx.curr.Or(ctx.inAllBranches) 206 ctx.curr.Or(ctx.inAllBranches)
199 } 207 }
200 if trace.Tracing { 208 if trace.Tracing {
201 trace.Stepf("- SubstContext.Directive %v %v", ctx.curr, ctx.inAllBranches) 209 trace.Stepf("- SubstContext.Directive %v %v", ctx.curr, ctx.inAllBranches)
202 } 210 }
203} 211}
204 212
205func (ctx *SubstContext) IsComplete() bool { 213func (ctx *SubstContext) IsComplete() bool {
206 return ctx.id != "" && 214 return ctx.stage != "" && ctx.curr.seenFiles && ctx.curr.seenTransform
207 ctx.stage != "" && 
208 ctx.curr.seenFiles && 
209 ctx.curr.seenTransform 
210} 215}
211 216
212func (ctx *SubstContext) Finish(mkline MkLine) { 217func (ctx *SubstContext) Finish(mkline MkLine) {
213 if ctx.id == "" { 218 if ctx.id == "" {
214 return 219 return
215 } 220 }
216 221
217 id := ctx.id 222 id := ctx.id
218 if ctx.stage == "" { 223 if ctx.stage == "" {
219 mkline.Warnf("Incomplete SUBST block: SUBST_STAGE.%s missing.", id) 224 mkline.Warnf("Incomplete SUBST block: SUBST_STAGE.%s missing.", id)
220 } 225 }
221 if !ctx.curr.seenFiles { 226 if !ctx.curr.seenFiles {
222 mkline.Warnf("Incomplete SUBST block: SUBST_FILES.%s missing.", id) 227 mkline.Warnf("Incomplete SUBST block: SUBST_FILES.%s missing.", id)
@@ -285,25 +290,21 @@ func (ctx *SubstContext) suggestSubstVar @@ -285,25 +290,21 @@ func (ctx *SubstContext) suggestSubstVar
285 290
286 varop := sprintf("SUBST_VARS.%s%s%s", 291 varop := sprintf("SUBST_VARS.%s%s%s",
287 ctx.id, 292 ctx.id,
288 ifelseStr(hasSuffix(ctx.id, "+"), " ", ""), 293 ifelseStr(hasSuffix(ctx.id, "+"), " ", ""),
289 ifelseStr(ctx.curr.seenVars, "+=", "=")) 294 ifelseStr(ctx.curr.seenVars, "+=", "="))
290 295
291 fix := mkline.Autofix() 296 fix := mkline.Autofix()
292 fix.Notef("The substitution command %q can be replaced with \"%s %s\".", 297 fix.Notef("The substitution command %q can be replaced with \"%s %s\".",
293 token, varop, varname) 298 token, varop, varname)
294 fix.Explain( 299 fix.Explain(
295 "Replacing @VAR@ with ${VAR} is such a typical pattern that pkgsrc has built-in support for it,", 300 "Replacing @VAR@ with ${VAR} is such a typical pattern that pkgsrc has built-in support for it,",
296 "requiring only the variable name instead of the full sed command.") 301 "requiring only the variable name instead of the full sed command.")
297 if mkline.VarassignComment() == "" && len(tokens) == 2 && tokens[0] == "-e" { 302 if mkline.VarassignComment() == "" && len(tokens) == 2 && tokens[0] == "-e" {
298 // TODO: Extract the alignment computation somewhere else, so that it is generally available. 303 fix.Replace(mkline.Text, alignWith(varop, mkline.ValueAlign())+varname)
299 alignBefore := tabWidth(mkline.ValueAlign()) 
300 alignAfter := tabWidth(varop + "\t") 
301 tabs := strings.Repeat("\t", imax((alignAfter-alignBefore)/8, 0)) 
302 fix.Replace(mkline.Text, varop+"\t"+tabs+varname) 
303 } 304 }
304 fix.Anyway() 305 fix.Anyway()
305 fix.Apply() 306 fix.Apply()
306 307
307 ctx.curr.seenVars = true 308 ctx.curr.seenVars = true
308 } 309 }
309} 310}

cvs diff -r1.57 -r1.58 pkgsrc/pkgtools/pkglint/files/Attic/mkline_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/mkline_test.go 2019/04/27 19:33:57 1.57
+++ pkgsrc/pkgtools/pkglint/files/Attic/mkline_test.go 2019/05/26 14:05:57 1.58
@@ -554,31 +554,26 @@ func (s *Suite) Test_MkLine_VariableNeed @@ -554,31 +554,26 @@ func (s *Suite) Test_MkLine_VariableNeed
554 mklines := t.NewMkLines("x11/mlterm/Makefile", 554 mklines := t.NewMkLines("x11/mlterm/Makefile",
555 MkRcsID, 555 MkRcsID,
556 "SUBST_SED.link=-e 's|(LIBTOOL_LINK).*(LIBS)|& ${LDFLAGS:M*:Q}|g'", 556 "SUBST_SED.link=-e 's|(LIBTOOL_LINK).*(LIBS)|& ${LDFLAGS:M*:Q}|g'",
557 "SUBST_SED.link=-e 's|(LIBTOOL_LINK).*(LIBS)|& '${LDFLAGS:M*:Q}'|g'") 557 "SUBST_SED.link=-e 's|(LIBTOOL_LINK).*(LIBS)|& '${LDFLAGS:M*:Q}'|g'")
558 558
559 MkLineChecker{mklines, mklines.mklines[1]}.Check() 559 MkLineChecker{mklines, mklines.mklines[1]}.Check()
560 MkLineChecker{mklines, mklines.mklines[2]}.Check() 560 MkLineChecker{mklines, mklines.mklines[2]}.Check()
561 561
562 t.CheckOutputLines( 562 t.CheckOutputLines(
563 "WARN: x11/mlterm/Makefile:2: Please move ${LDFLAGS:M*:Q} outside of any quoting characters.") 563 "WARN: x11/mlterm/Makefile:2: Please move ${LDFLAGS:M*:Q} outside of any quoting characters.")
564} 564}
565 565
566// No quoting is necessary when lists of options are appended to each other. 566// No quoting is necessary when lists of options are appended to each other.
567// PKG_OPTIONS are declared as "lkShell" although they are processed 
568// using make's .for loop, which splits them at whitespace and usually 
569// requires the variable to be declared as "lkSpace". 
570// In this case it doesn't matter though since each option is an identifier, 
571// and these do not pose any quoting or escaping problems. 
572func (s *Suite) Test_MkLine_VariableNeedsQuoting__package_options(c *check.C) { 567func (s *Suite) Test_MkLine_VariableNeedsQuoting__package_options(c *check.C) {
573 t := s.Init(c) 568 t := s.Init(c)
574 569
575 t.SetUpVartypes() 570 t.SetUpVartypes()
576 mklines := t.NewMkLines("Makefile", 571 mklines := t.NewMkLines("Makefile",
577 MkRcsID, 572 MkRcsID,
578 "PKG_SUGGESTED_OPTIONS+=\t${PKG_DEFAULT_OPTIONS:Mcdecimal} ${PKG_OPTIONS.py-trytond:Mcdecimal}") 573 "PKG_SUGGESTED_OPTIONS+=\t${PKG_DEFAULT_OPTIONS:Mcdecimal} ${PKG_OPTIONS.py-trytond:Mcdecimal}")
579 574
580 MkLineChecker{mklines, mklines.mklines[1]}.Check() 575 MkLineChecker{mklines, mklines.mklines[1]}.Check()
581 576
582 // No warning about a missing :Q modifier. 577 // No warning about a missing :Q modifier.
583 t.CheckOutputEmpty() 578 t.CheckOutputEmpty()
584} 579}
@@ -1149,26 +1144,43 @@ func (s *Suite) Test_MkLine_ValueTokens( @@ -1149,26 +1144,43 @@ func (s *Suite) Test_MkLine_ValueTokens(
1149 1144
1150 test("value # comment", 1145 test("value # comment",
1151 tokens( 1146 tokens(
1152 text("value"))) 1147 text("value")))
1153 1148
1154 test("value ${UNFINISHED", 1149 test("value ${UNFINISHED",
1155 tokens( 1150 tokens(
1156 text("value "), 1151 text("value "),
1157 varUseText("${UNFINISHED", "UNFINISHED")), 1152 varUseText("${UNFINISHED", "UNFINISHED")),
1158 1153
1159 "WARN: Makefile:1: Missing closing \"}\" for \"UNFINISHED\".") 1154 "WARN: Makefile:1: Missing closing \"}\" for \"UNFINISHED\".")
1160} 1155}
1161 1156
 1157func (s *Suite) Test_MkLine_ValueTokens__parse_error(c *check.C) {
 1158 t := s.Init(c)
 1159
 1160 mkline := t.NewMkLine("filename.mk", 123, "VAR=\t$")
 1161
 1162 tokens, rest := mkline.ValueTokens()
 1163
 1164 t.Check(tokens, check.IsNil)
 1165 t.Check(rest, equals, "$")
 1166
 1167 // Returns the same values, this time from the cache.
 1168 tokens, rest = mkline.ValueTokens()
 1169
 1170 t.Check(tokens, check.IsNil)
 1171 t.Check(rest, equals, "$")
 1172}
 1173
1162func (s *Suite) Test_MkLine_ValueTokens__caching(c *check.C) { 1174func (s *Suite) Test_MkLine_ValueTokens__caching(c *check.C) {
1163 t := s.Init(c) 1175 t := s.Init(c)
1164 1176
1165 tokens := func(tokens ...*MkToken) []*MkToken { return tokens } 1177 tokens := func(tokens ...*MkToken) []*MkToken { return tokens }
1166 1178
1167 mkline := t.NewMkLine("Makefile", 1, "PATH=\tvalue ${UNFINISHED") 1179 mkline := t.NewMkLine("Makefile", 1, "PATH=\tvalue ${UNFINISHED")
1168 valueTokens, rest := mkline.ValueTokens() 1180 valueTokens, rest := mkline.ValueTokens()
1169 1181
1170 c.Check(valueTokens, deepEquals, 1182 c.Check(valueTokens, deepEquals,
1171 tokens( 1183 tokens(
1172 &MkToken{"value ", nil}, 1184 &MkToken{"value ", nil},
1173 &MkToken{"${UNFINISHED", NewMkVarUse("UNFINISHED")})) 1185 &MkToken{"${UNFINISHED", NewMkVarUse("UNFINISHED")}))
1174 c.Check(rest, equals, "") 1186 c.Check(rest, equals, "")

cvs diff -r1.39 -r1.40 pkgsrc/pkgtools/pkglint/files/Attic/mklinechecker.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/mklinechecker.go 2019/05/21 17:59:48 1.39
+++ pkgsrc/pkgtools/pkglint/files/Attic/mklinechecker.go 2019/05/26 14:05:57 1.40
@@ -214,28 +214,28 @@ func (ck MkLineChecker) checkDirectiveFo @@ -214,28 +214,28 @@ func (ck MkLineChecker) checkDirectiveFo
214 } else { 214 } else {
215 mkline.Errorf("Invalid variable name %q.", forvar) 215 mkline.Errorf("Invalid variable name %q.", forvar)
216 } 216 }
217 217
218 forVars[forvar] = true 218 forVars[forvar] = true
219 } 219 }
220 220
221 // XXX: The type BtUnknown is very unspecific here. For known variables 221 // XXX: The type BtUnknown is very unspecific here. For known variables
222 // or constant values this could probably be improved. 222 // or constant values this could probably be improved.
223 // 223 //
224 // The guessed flag could also be determined more correctly. As of November 2018, 224 // The guessed flag could also be determined more correctly. As of November 2018,
225 // running pkglint over the whole pkgsrc tree did not produce any different result 225 // running pkglint over the whole pkgsrc tree did not produce any different result
226 // whether guessed was true or false. 226 // whether guessed was true or false.
227 forLoopType := Vartype{btForLoop, List, []ACLEntry{{"*", aclpAllRead}}} 227 forLoopType := NewVartype(btForLoop, List, NewACLEntry("*", aclpAllRead))
228 forLoopContext := VarUseContext{&forLoopType, vucTimeParse, VucQuotPlain, false} 228 forLoopContext := VarUseContext{forLoopType, vucTimeParse, VucQuotPlain, false}
229 mkline.ForEachUsed(func(varUse *MkVarUse, time vucTime) { 229 mkline.ForEachUsed(func(varUse *MkVarUse, time vucTime) {
230 ck.CheckVaruse(varUse, &forLoopContext) 230 ck.CheckVaruse(varUse, &forLoopContext)
231 }) 231 })
232 } 232 }
233} 233}
234 234
235func (ck MkLineChecker) checkDirectiveIndentation(expectedDepth int) { 235func (ck MkLineChecker) checkDirectiveIndentation(expectedDepth int) {
236 if ck.MkLines == nil || !G.Opts.WarnSpace { 236 if ck.MkLines == nil || !G.Opts.WarnSpace {
237 return 237 return
238 } 238 }
239 mkline := ck.MkLine 239 mkline := ck.MkLine
240 indent := mkline.Indent() 240 indent := mkline.Indent()
241 if expected := strings.Repeat(" ", expectedDepth); indent != expected { 241 if expected := strings.Repeat(" ", expectedDepth); indent != expected {
@@ -992,27 +992,27 @@ func (ck MkLineChecker) checkVarassignLe @@ -992,27 +992,27 @@ func (ck MkLineChecker) checkVarassignLe
992 ck.MkLine.Warnf("Variable names starting with an underscore (%s) are reserved for internal pkgsrc use.", varname) 992 ck.MkLine.Warnf("Variable names starting with an underscore (%s) are reserved for internal pkgsrc use.", varname)
993 } 993 }
994 994
995 ck.checkVarassignLeftNotUsed() 995 ck.checkVarassignLeftNotUsed()
996 ck.checkVarassignLeftDeprecated() 996 ck.checkVarassignLeftDeprecated()
997 ck.checkVarassignLeftBsdPrefs() 997 ck.checkVarassignLeftBsdPrefs()
998 if !ck.checkVarassignLeftUserSettable() { 998 if !ck.checkVarassignLeftUserSettable() {
999 ck.checkVarassignLeftPermissions() 999 ck.checkVarassignLeftPermissions()
1000 } 1000 }
1001 ck.checkVarassignLeftRationale() 1001 ck.checkVarassignLeftRationale()
1002 1002
1003 ck.checkTextVarUse( 1003 ck.checkTextVarUse(
1004 ck.MkLine.Varname(), 1004 ck.MkLine.Varname(),
1005 &Vartype{BtVariableName, NoVartypeOptions, []ACLEntry{{"*", aclpAll}}}, 1005 NewVartype(BtVariableName, NoVartypeOptions, NewACLEntry("*", aclpAll)),
1006 vucTimeParse) 1006 vucTimeParse)
1007} 1007}
1008 1008
1009func (ck MkLineChecker) checkVarassignOp() { 1009func (ck MkLineChecker) checkVarassignOp() {
1010 ck.checkVarassignOpShell() 1010 ck.checkVarassignOpShell()
1011} 1011}
1012 1012
1013func (ck MkLineChecker) checkVarassignOpShell() { 1013func (ck MkLineChecker) checkVarassignOpShell() {
1014 mkline := ck.MkLine 1014 mkline := ck.MkLine
1015 1015
1016 switch { 1016 switch {
1017 case mkline.Op() != opAssignShell: 1017 case mkline.Op() != opAssignShell:
1018 return 1018 return

cvs diff -r1.35 -r1.36 pkgsrc/pkgtools/pkglint/files/Attic/mklinechecker_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/mklinechecker_test.go 2019/05/21 17:59:48 1.35
+++ pkgsrc/pkgtools/pkglint/files/Attic/mklinechecker_test.go 2019/05/26 14:05:57 1.36
@@ -1208,27 +1208,27 @@ func (s *Suite) Test_MkLineChecker_check @@ -1208,27 +1208,27 @@ func (s *Suite) Test_MkLineChecker_check
1208 1208
1209 // Since the variable is usable at load time, pkglint assumes it is also 1209 // Since the variable is usable at load time, pkglint assumes it is also
1210 // usable at run time. This is not the case for VAR, but probably doesn't 1210 // usable at run time. This is not the case for VAR, but probably doesn't
1211 // happen in practice anyway. 1211 // happen in practice anyway.
1212 t.CheckOutputEmpty() 1212 t.CheckOutputEmpty()
1213} 1213}
1214 1214
1215func (s *Suite) Test_MkLineChecker_checkVarusePermissions__assigned_to_infrastructure_variable(c *check.C) { 1215func (s *Suite) Test_MkLineChecker_checkVarusePermissions__assigned_to_infrastructure_variable(c *check.C) {
1216 t := s.Init(c) 1216 t := s.Init(c)
1217 1217
1218 // This combination of BtUnknown and all permissions is typical for 1218 // This combination of BtUnknown and all permissions is typical for
1219 // otherwise unknown variables from the pkgsrc infrastructure. 1219 // otherwise unknown variables from the pkgsrc infrastructure.
1220 G.Pkgsrc.vartypes.Define("INFRA", BtUnknown, NoVartypeOptions, 1220 G.Pkgsrc.vartypes.Define("INFRA", BtUnknown, NoVartypeOptions,
1221 ACLEntry{"*", aclpAll}) 1221 NewACLEntry("*", aclpAll))
1222 G.Pkgsrc.vartypes.DefineParse("VAR", BtUnknown, NoVartypeOptions, 1222 G.Pkgsrc.vartypes.DefineParse("VAR", BtUnknown, NoVartypeOptions,
1223 "buildlink3.mk: none", 1223 "buildlink3.mk: none",
1224 "*: use") 1224 "*: use")
1225 mklines := t.NewMkLines("buildlink3.mk", 1225 mklines := t.NewMkLines("buildlink3.mk",
1226 MkRcsID, 1226 MkRcsID,
1227 "INFRA=\t${VAR}") 1227 "INFRA=\t${VAR}")
1228 1228
1229 mklines.Check() 1229 mklines.Check()
1230 1230
1231 // Since INFRA is defined in the infrastructure and pkglint 1231 // Since INFRA is defined in the infrastructure and pkglint
1232 // knows nothing else about this variable, it assumes that INFRA 1232 // knows nothing else about this variable, it assumes that INFRA
1233 // may be used at load time. This is done to prevent wrong warnings. 1233 // may be used at load time. This is done to prevent wrong warnings.
1234 // 1234 //

cvs diff -r1.48 -r1.49 pkgsrc/pkgtools/pkglint/files/Attic/mklines.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/mklines.go 2019/04/27 19:33:57 1.48
+++ pkgsrc/pkgtools/pkglint/files/Attic/mklines.go 2019/05/26 14:05:57 1.49
@@ -120,47 +120,44 @@ func (mklines *MkLinesImpl) checkAll() { @@ -120,47 +120,44 @@ func (mklines *MkLinesImpl) checkAll() {
120 isHacksMk := mklines.lines.BaseName == "hacks.mk" 120 isHacksMk := mklines.lines.BaseName == "hacks.mk"
121 121
122 lineAction := func(mkline MkLine) bool { 122 lineAction := func(mkline MkLine) bool {
123 if isHacksMk { 123 if isHacksMk {
124 // Needs to be set here because it is reset in MkLines.ForEach. 124 // Needs to be set here because it is reset in MkLines.ForEach.
125 mklines.Tools.SeenPrefs = true 125 mklines.Tools.SeenPrefs = true
126 } 126 }
127 127
128 ck := MkLineChecker{mklines, mkline} 128 ck := MkLineChecker{mklines, mkline}
129 ck.Check() 129 ck.Check()
130 130
131 varalign.Process(mkline) 131 varalign.Process(mkline)
132 mklines.Tools.ParseToolLine(mklines, mkline, false, false) 132 mklines.Tools.ParseToolLine(mklines, mkline, false, false)
 133 substContext.Process(mkline)
133 134
134 switch { 135 switch {
135 case mkline.IsEmpty(): 
136 substContext.Finish(mkline) 
137 136
138 case mkline.IsVarassign(): 137 case mkline.IsVarassign():
139 mklines.target = "" 138 mklines.target = ""
140 mkline.Tokenize(mkline.Value(), true) // Just for the side-effect of the warnings. 139 mkline.Tokenize(mkline.Value(), true) // Just for the side-effect of the warnings.
141 substContext.Varassign(mkline) 
142 140
143 mklines.checkVarassignPlist(mkline) 141 mklines.checkVarassignPlist(mkline)
144 142
145 case mkline.IsInclude(): 143 case mkline.IsInclude():
146 mklines.target = "" 144 mklines.target = ""
147 if G.Pkg != nil { 145 if G.Pkg != nil {
148 G.Pkg.checkIncludeConditionally(mkline, mklines.indentation) 146 G.Pkg.checkIncludeConditionally(mkline, mklines.indentation)
149 } 147 }
150 148
151 case mkline.IsDirective(): 149 case mkline.IsDirective():
152 ck.checkDirective(mklines.forVars, mklines.indentation) 150 ck.checkDirective(mklines.forVars, mklines.indentation)
153 substContext.Directive(mkline) 
154 151
155 case mkline.IsDependency(): 152 case mkline.IsDependency():
156 ck.checkDependencyRule(allowedTargets) 153 ck.checkDependencyRule(allowedTargets)
157 mklines.target = mkline.Targets() 154 mklines.target = mkline.Targets()
158 155
159 case mkline.IsShellCommand(): 156 case mkline.IsShellCommand():
160 mkline.Tokenize(mkline.ShellCommand(), true) // Just for the side-effect of the warnings. 157 mkline.Tokenize(mkline.ShellCommand(), true) // Just for the side-effect of the warnings.
161 } 158 }
162 159
163 return true 160 return true
164 } 161 }
165 162
166 atEnd := func(mkline MkLine) { 163 atEnd := func(mkline MkLine) {

cvs diff -r1.48 -r1.49 pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck_test.go 2019/05/21 17:59:48 1.48
+++ pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck_test.go 2019/05/26 14:05:57 1.49
@@ -524,33 +524,38 @@ func (s *Suite) Test_VartypeCheck_Filena @@ -524,33 +524,38 @@ func (s *Suite) Test_VartypeCheck_Filena
524 524
525 // There's no guarantee that a filename only contains [A-Za-z0-9.]. 525 // There's no guarantee that a filename only contains [A-Za-z0-9.].
526 // Therefore there are no useful checks in this situation. 526 // Therefore there are no useful checks in this situation.
527 vt.Output( 527 vt.Output(
528 "WARN: filename.mk:11: The filename pattern \"Filename with spaces.docx\" " + 528 "WARN: filename.mk:11: The filename pattern \"Filename with spaces.docx\" " +
529 "contains the invalid characters \" \".") 529 "contains the invalid characters \" \".")
530} 530}
531 531
532func (s *Suite) Test_VartypeCheck_FileMask(c *check.C) { 532func (s *Suite) Test_VartypeCheck_FileMask(c *check.C) {
533 vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).FileMask) 533 vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).FileMask)
534 534
535 vt.Varname("FNAME") 535 vt.Varname("FNAME")
536 vt.Values( 536 vt.Values(
 537 "filename.txt",
 538 "*.txt",
 539 "[12345].txt",
 540 "[0-9].txt",
 541 "???.txt",
537 "FileMask with spaces.docx", 542 "FileMask with spaces.docx",
538 "OS/2-manual.txt") 543 "OS/2-manual.txt")
539 544
540 vt.Output( 545 vt.Output(
541 "WARN: filename.mk:1: The filename pattern \"FileMask with spaces.docx\" "+ 546 "WARN: filename.mk:6: The filename pattern \"FileMask with spaces.docx\" "+
542 "contains the invalid characters \" \".", 547 "contains the invalid characters \" \".",
543 "WARN: filename.mk:2: The filename pattern \"OS/2-manual.txt\" "+ 548 "WARN: filename.mk:7: The filename pattern \"OS/2-manual.txt\" "+
544 "contains the invalid character \"/\".") 549 "contains the invalid character \"/\".")
545 550
546 vt.Op(opUseMatch) 551 vt.Op(opUseMatch)
547 vt.Values( 552 vt.Values(
548 "FileMask with spaces.docx") 553 "FileMask with spaces.docx")
549 554
550 // There's no guarantee that a filename only contains [A-Za-z0-9.]. 555 // There's no guarantee that a filename only contains [A-Za-z0-9.].
551 // Therefore it might be necessary to allow all characters here. 556 // Therefore it might be necessary to allow all characters here.
552 // TODO: Investigate whether this restriction is useful in practice. 557 // TODO: Investigate whether this restriction is useful in practice.
553 vt.Output( 558 vt.Output(
554 "WARN: filename.mk:11: The filename pattern \"FileMask with spaces.docx\" " + 559 "WARN: filename.mk:11: The filename pattern \"FileMask with spaces.docx\" " +
555 "contains the invalid characters \" \".") 560 "contains the invalid characters \" \".")
556} 561}

cvs diff -r1.26 -r1.27 pkgsrc/pkgtools/pkglint/files/Attic/pkgsrc.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/pkgsrc.go 2019/05/06 20:27:17 1.26
+++ pkgsrc/pkgtools/pkglint/files/Attic/pkgsrc.go 2019/05/26 14:05:57 1.27
@@ -337,51 +337,51 @@ func (src *Pkgsrc) loadTools() { @@ -337,51 +337,51 @@ func (src *Pkgsrc) loadTools() {
337 tools.Trace() 337 tools.Trace()
338 } 338 }
339} 339}
340 340
341// loadUntypedVars scans all pkgsrc infrastructure files in mk/ 341// loadUntypedVars scans all pkgsrc infrastructure files in mk/
342// to find variable definitions that are not yet covered in 342// to find variable definitions that are not yet covered in
343// Pkgsrc.InitVartypes. 343// Pkgsrc.InitVartypes.
344// 344//
345// Even if pkglint cannot guess the type of each variable, 345// Even if pkglint cannot guess the type of each variable,
346// at least prevent the "used but not defined" warnings. 346// at least prevent the "used but not defined" warnings.
347func (src *Pkgsrc) loadUntypedVars() { 347func (src *Pkgsrc) loadUntypedVars() {
348 348
349 // Setting guessed to false prevents the vartype.guessed case in MkLineChecker.CheckVaruse. 349 // Setting guessed to false prevents the vartype.guessed case in MkLineChecker.CheckVaruse.
350 unknownType := Vartype{BtUnknown, NoVartypeOptions, []ACLEntry{{"*", aclpAll}}} 350 unknownType := NewVartype(BtUnknown, NoVartypeOptions, NewACLEntry("*", aclpAll))
351 351
352 define := func(varcanon string, mkline MkLine) { 352 define := func(varcanon string, mkline MkLine) {
353 switch { 353 switch {
354 case src.vartypes.DefinedCanon(varcanon): 354 case src.vartypes.DefinedCanon(varcanon):
355 // Already defined, can also be a tool. 355 // Already defined, can also be a tool.
356 356
357 case hasPrefix(varcanon, "_"): 357 case hasPrefix(varcanon, "_"):
358 // Variables starting with an underscore are reserved for the 358 // Variables starting with an underscore are reserved for the
359 // infrastructure and are not available for use by packages. 359 // infrastructure and are not available for use by packages.
360 360
361 case contains(varcanon, "$"): 361 case contains(varcanon, "$"):
362 // Indirect, but not the usual parameterized form. Variables of 362 // Indirect, but not the usual parameterized form. Variables of
363 // this form should not be unintentionally visible from outside 363 // this form should not be unintentionally visible from outside
364 // the infrastructure since they don't follow the pkgsrc naming 364 // the infrastructure since they don't follow the pkgsrc naming
365 // conventions. 365 // conventions.
366 366
367 case hasSuffix(varcanon, "_MK"): 367 case hasSuffix(varcanon, "_MK"):
368 // Multiple-inclusion guards are internal to the infrastructure. 368 // Multiple-inclusion guards are internal to the infrastructure.
369 369
370 default: 370 default:
371 if trace.Tracing { 371 if trace.Tracing {
372 trace.Stepf("Untyped variable %q in %s", varcanon, mkline) 372 trace.Stepf("Untyped variable %q in %s", varcanon, mkline)
373 } 373 }
374 src.vartypes.DefineType(varcanon, &unknownType) 374 src.vartypes.DefineType(varcanon, unknownType)
375 } 375 }
376 } 376 }
377 377
378 handleMkFile := func(path string) { 378 handleMkFile := func(path string) {
379 mklines := LoadMk(path, 0) 379 mklines := LoadMk(path, 0)
380 if mklines != nil && len(mklines.mklines) > 0 { 380 if mklines != nil && len(mklines.mklines) > 0 {
381 mklines.collectDefinedVariables() 381 mklines.collectDefinedVariables()
382 mklines.collectUsedVariables() 382 mklines.collectUsedVariables()
383 for varname, mkline := range mklines.vars.firstDef { 383 for varname, mkline := range mklines.vars.firstDef {
384 define(varnameCanon(varname), mkline) 384 define(varnameCanon(varname), mkline)
385 } 385 }
386 for varname, mkline := range mklines.vars.used { 386 for varname, mkline := range mklines.vars.used {
387 define(varnameCanon(varname), mkline) 387 define(varnameCanon(varname), mkline)
@@ -912,95 +912,104 @@ func (src *Pkgsrc) VariableType(mklines  @@ -912,95 +912,104 @@ func (src *Pkgsrc) VariableType(mklines
912 vartype = src.vartypes.Canon(varname) 912 vartype = src.vartypes.Canon(varname)
913 if vartype != nil && vartype.basicType != BtUnknown { 913 if vartype != nil && vartype.basicType != BtUnknown {
914 return vartype 914 return vartype
915 } 915 }
916 916
917 if tool := G.ToolByVarname(mklines, varname); tool != nil { 917 if tool := G.ToolByVarname(mklines, varname); tool != nil {
918 if trace.Tracing { 918 if trace.Tracing {
919 trace.Stepf("Use of tool %+v", tool) 919 trace.Stepf("Use of tool %+v", tool)
920 } 920 }
921 perms := aclpUse 921 perms := aclpUse
922 if tool.Validity == AfterPrefsMk && mklines.Tools.SeenPrefs { 922 if tool.Validity == AfterPrefsMk && mklines.Tools.SeenPrefs {
923 perms |= aclpUseLoadtime 923 perms |= aclpUseLoadtime
924 } 924 }
925 return &Vartype{BtShellCommand, NoVartypeOptions, []ACLEntry{{"*", perms}}} 925 return NewVartype(BtShellCommand, NoVartypeOptions, NewACLEntry("*", perms))
926 } 926 }
927 927
928 if m, toolVarname := match1(varname, `^TOOLS_(.*)`); m { 928 if m, toolVarname := match1(varname, `^TOOLS_(.*)`); m {
929 if tool := G.ToolByVarname(mklines, toolVarname); tool != nil { 929 if tool := G.ToolByVarname(mklines, toolVarname); tool != nil {
930 return &Vartype{BtPathname, NoVartypeOptions, []ACLEntry{{"*", aclpUse}}} 930 return NewVartype(BtPathname, NoVartypeOptions, NewACLEntry("*", aclpUse))
931 } 931 }
932 } 932 }
933 933
934 return src.guessVariableType(varname) 934 return src.guessVariableType(varname)
935} 935}
936 936
 937// guessVariableType guesses the data type of the variable based on naming conventions.
937func (src *Pkgsrc) guessVariableType(varname string) (vartype *Vartype) { 938func (src *Pkgsrc) guessVariableType(varname string) (vartype *Vartype) {
938 allowAll := []ACLEntry{{"*", aclpAll}} 939 plainType := func(basicType *BasicType, permissions ACLPermissions) *Vartype {
939 allowRuntime := []ACLEntry{{"*", aclpAllRuntime}} 940 gtype := NewVartype(basicType, Guessed, NewACLEntry("*", permissions))
 941 trace.Step2("The guessed type of %q is %q.", varname, gtype.String())
 942 return gtype
 943 }
 944 listType := func(basicType *BasicType, permissions ACLPermissions) *Vartype {
 945 gtype := NewVartype(basicType, List|Guessed, NewACLEntry("*", permissions))
 946 trace.Step2("The guessed type of %q is %q.", varname, gtype.String())
 947 return gtype
 948 }
940 949
941 // Guess the data type of the variable based on naming conventions. 
942 varbase := varnameBase(varname) 950 varbase := varnameBase(varname)
943 var gtype *Vartype 
944 switch { 951 switch {
945 case hasSuffix(varbase, "DIRS"): 952 case hasSuffix(varbase, "DIRS"):
946 gtype = &Vartype{BtPathmask, List | Guessed, allowRuntime} 953 return listType(BtPathmask, aclpAllRuntime)
947 case hasSuffix(varbase, "DIR") && !hasSuffix(varbase, "DESTDIR"), hasSuffix(varname, "_HOME"): 954 case hasSuffix(varbase, "DIR") && !hasSuffix(varbase, "DESTDIR"), hasSuffix(varname, "_HOME"):
948 // TODO: hasSuffix(varbase, "BASE") 955 // TODO: hasSuffix(varbase, "BASE")
949 gtype = &Vartype{BtPathname, Guessed, allowRuntime} 956 return plainType(BtPathname, aclpAllRuntime)
950 case hasSuffix(varbase, "FILES"): 957 case hasSuffix(varbase, "FILES"):
951 gtype = &Vartype{BtPathmask, List | Guessed, allowRuntime} 958 return listType(BtPathmask, aclpAllRuntime)
952 case hasSuffix(varbase, "FILE"): 959 case hasSuffix(varbase, "FILE"):
953 gtype = &Vartype{BtPathname, Guessed, allowRuntime} 960 return plainType(BtPathname, aclpAllRuntime)
954 case hasSuffix(varbase, "PATH"): 961 case hasSuffix(varbase, "PATH"):
955 gtype = &Vartype{BtPathlist, Guessed, allowRuntime} 962 return plainType(BtPathlist, aclpAllRuntime)
956 case hasSuffix(varbase, "PATHS"): 963 case hasSuffix(varbase, "PATHS"):
957 gtype = &Vartype{BtPathname, List | Guessed, allowRuntime} 964 return listType(BtPathname, aclpAllRuntime)
958 case hasSuffix(varbase, "_USER"): 965 case hasSuffix(varbase, "_USER"):
959 gtype = &Vartype{BtUserGroupName, Guessed, allowAll} 966 return plainType(BtUserGroupName, aclpAll)
960 case hasSuffix(varbase, "_GROUP"): 967 case hasSuffix(varbase, "_GROUP"):
961 gtype = &Vartype{BtUserGroupName, Guessed, allowAll} 968 return plainType(BtUserGroupName, aclpAll)
962 case hasSuffix(varbase, "_ENV"): 969 case hasSuffix(varbase, "_ENV"):
963 gtype = &Vartype{BtShellWord, List | Guessed, allowRuntime} 970 return listType(BtShellWord, aclpAllRuntime)
964 case hasSuffix(varbase, "_CMD"): 971 case hasSuffix(varbase, "_CMD"):
965 gtype = &Vartype{BtShellCommand, Guessed, allowRuntime} 972 return plainType(BtShellCommand, aclpAllRuntime)
966 case hasSuffix(varbase, "_ARGS"): 973 case hasSuffix(varbase, "_ARGS"):
967 gtype = &Vartype{BtShellWord, List | Guessed, allowRuntime} 974 return listType(BtShellWord, aclpAllRuntime)
968 case hasSuffix(varbase, "_CFLAGS"), hasSuffix(varname, "_CPPFLAGS"), hasSuffix(varname, "_CXXFLAGS"): 975 case hasSuffix(varbase, "_CFLAGS"), hasSuffix(varname, "_CPPFLAGS"), hasSuffix(varname, "_CXXFLAGS"):
969 gtype = &Vartype{BtCFlag, List | Guessed, allowRuntime} 976 return listType(BtCFlag, aclpAllRuntime)
970 case hasSuffix(varname, "_LDFLAGS"): 977 case hasSuffix(varname, "_LDFLAGS"):
971 gtype = &Vartype{BtLdFlag, List | Guessed, allowRuntime} 978 return listType(BtLdFlag, aclpAllRuntime)
972 case hasSuffix(varbase, "_MK"): 979 case hasSuffix(varbase, "_MK"):
973 // TODO: Add BtGuard for inclusion guards, since these variables may only be checked using defined(). 980 // TODO: Add BtGuard for inclusion guards, since these variables may only be checked using defined().
974 gtype = &Vartype{BtUnknown, Guessed, allowAll} 981 return plainType(BtUnknown, aclpAll)
975 case hasSuffix(varbase, "_SKIP"): 982 case hasSuffix(varbase, "_SKIP"):
976 gtype = &Vartype{BtPathmask, List | Guessed, allowRuntime} 983 return listType(BtPathmask, aclpAllRuntime)
977 } 984 }
978 985
979 if gtype == nil { 986 // Variables whose name doesn't match the above patterns may be
980 vartype = src.vartypes.Canon(varname) 987 // looked up from the pkgsrc infrastructure.
981 if vartype != nil { 988 //
982 return vartype 989 // As of May 2019, pkglint only distinguishes plain variables and
983 } 990 // list variables, but not "unknown". Therefore the above patterns
 991 // must take precedence over this rule, because otherwise, list
 992 // variables from the infrastructure would be guessed to be plain
 993 // variables.
 994 vartype = src.vartypes.Canon(varname)
 995 if vartype != nil {
 996 return vartype
984 } 997 }
985 998
986 if trace.Tracing { 999 if trace.Tracing {
987 if gtype != nil { 1000 trace.Step1("No type definition found for %q.", varname)
988 trace.Step2("The guessed type of %q is %q.", varname, gtype.String()) 
989 } else { 
990 trace.Step1("No type definition found for %q.", varname) 
991 } 
992 } 1001 }
993 return gtype 1002 return nil
994} 1003}
995 1004
996// Change describes a modification to a single package, from the doc/CHANGES-* files. 1005// Change describes a modification to a single package, from the doc/CHANGES-* files.
997type Change struct { 1006type Change struct {
998 Location Location 1007 Location Location
999 Action string 1008 Action string
1000 Pkgpath string 1009 Pkgpath string
1001 Version string 1010 Version string
1002 Author string 1011 Author string
1003 Date string 1012 Date string
1004} 1013}
1005 1014
1006// SuggestedUpdate describes a desired package update, from the doc/TODO file. 1015// SuggestedUpdate describes a desired package update, from the doc/TODO file.

cvs diff -r1.4 -r1.5 pkgsrc/pkgtools/pkglint/files/Attic/redundantscope.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/redundantscope.go 2019/04/20 17:43:24 1.4
+++ pkgsrc/pkgtools/pkglint/files/Attic/redundantscope.go 2019/05/26 14:05:57 1.5
@@ -19,35 +19,39 @@ type RedundantScope struct { @@ -19,35 +19,39 @@ type RedundantScope struct {
19} 19}
20type redundantScopeVarinfo struct { 20type redundantScopeVarinfo struct {
21 vari *Var 21 vari *Var
22 includePaths []includePath 22 includePaths []includePath
23 lastAction uint8 // 0 = none, 1 = read, 2 = write 23 lastAction uint8 // 0 = none, 1 = read, 2 = write
24} 24}
25 25
26func NewRedundantScope() *RedundantScope { 26func NewRedundantScope() *RedundantScope {
27 return &RedundantScope{vars: make(map[string]*redundantScopeVarinfo)} 27 return &RedundantScope{vars: make(map[string]*redundantScopeVarinfo)}
28} 28}
29 29
30func (s *RedundantScope) Check(mklines MkLines) { 30func (s *RedundantScope) Check(mklines MkLines) {
31 mklines.ForEach(func(mkline MkLine) { 31 mklines.ForEach(func(mkline MkLine) {
32 s.updateIncludePath(mkline) 32 s.checkLine(mklines, mkline)
 33 })
 34}
33 35
34 switch { 36func (s *RedundantScope) checkLine(mklines MkLines, mkline MkLine) {
35 case mkline.IsVarassign(): 37 s.updateIncludePath(mkline)
36 s.handleVarassign(mkline, mklines.indentation) 
37 } 
38 38
39 s.handleVarUse(mkline) 39 switch {
40 }) 40 case mkline.IsVarassign():
 41 s.handleVarassign(mkline, mklines.indentation)
 42 }
 43
 44 s.handleVarUse(mkline)
41} 45}
42 46
43func (s *RedundantScope) updateIncludePath(mkline MkLine) { 47func (s *RedundantScope) updateIncludePath(mkline MkLine) {
44 if mkline.firstLine == 1 { 48 if mkline.firstLine == 1 {
45 s.includePath.push(mkline.Location.Filename) 49 s.includePath.push(mkline.Location.Filename)
46 } else { 50 } else {
47 s.includePath.popUntil(mkline.Location.Filename) 51 s.includePath.popUntil(mkline.Location.Filename)
48 } 52 }
49} 53}
50 54
51func (s *RedundantScope) handleVarassign(mkline MkLine, ind *Indentation) { 55func (s *RedundantScope) handleVarassign(mkline MkLine, ind *Indentation) {
52 varname := mkline.Varname() 56 varname := mkline.Varname()
53 info := s.get(varname) 57 info := s.get(varname)

cvs diff -r1.40 -r1.41 pkgsrc/pkgtools/pkglint/files/Attic/shell.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/shell.go 2019/05/06 20:27:17 1.40
+++ pkgsrc/pkgtools/pkglint/files/Attic/shell.go 2019/05/26 14:05:57 1.41
@@ -25,27 +25,27 @@ type ShellLineChecker struct { @@ -25,27 +25,27 @@ type ShellLineChecker struct {
25} 25}
26 26
27func NewShellLineChecker(mklines MkLines, mkline MkLine) *ShellLineChecker { 27func NewShellLineChecker(mklines MkLines, mkline MkLine) *ShellLineChecker {
28 return &ShellLineChecker{mklines, mkline, true} 28 return &ShellLineChecker{mklines, mkline, true}
29} 29}
30 30
31func (ck *ShellLineChecker) Warnf(format string, args ...interface{}) { 31func (ck *ShellLineChecker) Warnf(format string, args ...interface{}) {
32 ck.mkline.Warnf(format, args...) 32 ck.mkline.Warnf(format, args...)
33} 33}
34func (ck *ShellLineChecker) Explain(explanation ...string) { 34func (ck *ShellLineChecker) Explain(explanation ...string) {
35 ck.mkline.Explain(explanation...) 35 ck.mkline.Explain(explanation...)
36} 36}
37 37
38var shellCommandsType = &Vartype{BtShellCommands, NoVartypeOptions, []ACLEntry{{"*", aclpAllRuntime}}} 38var shellCommandsType = NewVartype(BtShellCommands, NoVartypeOptions, NewACLEntry("*", aclpAllRuntime))
39var shellWordVuc = &VarUseContext{shellCommandsType, vucTimeUnknown, VucQuotPlain, false} 39var shellWordVuc = &VarUseContext{shellCommandsType, vucTimeUnknown, VucQuotPlain, false}
40 40
41func (ck *ShellLineChecker) CheckWord(token string, checkQuoting bool, time ToolTime) { 41func (ck *ShellLineChecker) CheckWord(token string, checkQuoting bool, time ToolTime) {
42 if trace.Tracing { 42 if trace.Tracing {
43 defer trace.Call(token, checkQuoting)() 43 defer trace.Call(token, checkQuoting)()
44 } 44 }
45 45
46 if token == "" || hasPrefix(token, "#") { 46 if token == "" || hasPrefix(token, "#") {
47 return 47 return
48 } 48 }
49 49
50 var line = ck.mkline.Line 50 var line = ck.mkline.Line
51 51
@@ -638,33 +638,33 @@ func (scc *SimpleCommandChecker) handleC @@ -638,33 +638,33 @@ func (scc *SimpleCommandChecker) handleC
638 return true 638 return true
639} 639}
640 640
641func (scc *SimpleCommandChecker) checkRegexReplace() { 641func (scc *SimpleCommandChecker) checkRegexReplace() {
642 if trace.Tracing { 642 if trace.Tracing {
643 defer trace.Call0()() 643 defer trace.Call0()()
644 } 644 }
645 645
646 if !G.Testing { 646 if !G.Testing {
647 return 647 return
648 } 648 }
649 649
650 checkArg := func(arg string) { 650 checkArg := func(arg string) {
651 if matches(arg, `"^[\"\'].*[\"\']$`) { 651 if matches(arg, `^["'].*["']$`) {
652 return 652 return
653 } 653 }
654 654
655 // Substitution commands that consist only of safe characters cannot 655 // Substitution commands that consist only of safe characters cannot
656 // have any side effects, therefore they don't need to be quoted. 656 // have any side effects, therefore they don't need to be quoted.
657 if matches(arg, `^([\w,.]|\\[.])+$`) { 657 if matches(arg, `^([\w,.]|\\.)+$`) {
658 return 658 return
659 } 659 }
660 660
661 scc.Warnf("Substitution commands like %q should always be quoted.", arg) 661 scc.Warnf("Substitution commands like %q should always be quoted.", arg)
662 scc.Explain( 662 scc.Explain(
663 "Usually these substitution commands contain characters like '*' or", 663 "Usually these substitution commands contain characters like '*' or",
664 "other shell metacharacters that might lead to lookup of matching", 664 "other shell metacharacters that might lead to lookup of matching",
665 "filenames and then expand to more than one word.") 665 "filenames and then expand to more than one word.")
666 } 666 }
667 667
668 checkArgAfter := func(opt string) { 668 checkArgAfter := func(opt string) {
669 args := scc.strcmd.Args 669 args := scc.strcmd.Args
670 for i, arg := range args { 670 for i, arg := range args {

cvs diff -r1.46 -r1.47 pkgsrc/pkgtools/pkglint/files/Attic/shell_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/shell_test.go 2019/05/06 20:27:17 1.46
+++ pkgsrc/pkgtools/pkglint/files/Attic/shell_test.go 2019/05/26 14:05:57 1.47
@@ -1204,34 +1204,48 @@ func (s *Suite) Test_SimpleCommandChecke @@ -1204,34 +1204,48 @@ func (s *Suite) Test_SimpleCommandChecke
1204 1204
1205 test("${PAX} -s s,.*,, src dst", 1205 test("${PAX} -s s,.*,, src dst",
1206 "WARN: Makefile:3: Substitution commands like \"s,.*,,\" should always be quoted.") 1206 "WARN: Makefile:3: Substitution commands like \"s,.*,,\" should always be quoted.")
1207 1207
1208 test("pax -s s,.*,, src dst", 1208 test("pax -s s,.*,, src dst",
1209 "WARN: Makefile:3: Substitution commands like \"s,.*,,\" should always be quoted.") 1209 "WARN: Makefile:3: Substitution commands like \"s,.*,,\" should always be quoted.")
1210 1210
1211 test("${SED} -e s,.*,, src dst", 1211 test("${SED} -e s,.*,, src dst",
1212 "WARN: Makefile:3: Substitution commands like \"s,.*,,\" should always be quoted.") 1212 "WARN: Makefile:3: Substitution commands like \"s,.*,,\" should always be quoted.")
1213 1213
1214 test("sed -e s,.*,, src dst", 1214 test("sed -e s,.*,, src dst",
1215 "WARN: Makefile:3: Substitution commands like \"s,.*,,\" should always be quoted.") 1215 "WARN: Makefile:3: Substitution commands like \"s,.*,,\" should always be quoted.")
1216 1216
 1217 // The * is properly enclosed in quotes.
 1218 test("sed -e 's,.*,,' -e \"s,-*,,\"",
 1219 nil...)
 1220
 1221 // The * is properly escaped.
 1222 test("sed -e s,.\\*,,",
 1223 nil...)
 1224
1217 test("pax -s s,\\.orig,, src dst", 1225 test("pax -s s,\\.orig,, src dst",
1218 nil...) 1226 nil...)
1219 1227
1220 test("sed -e s,a,b,g src dst", 1228 test("sed -e s,a,b,g src dst",
1221 nil...) 1229 nil...)
1222 1230
1223 // TODO: Merge the code with BtSedCommands. 1231 // TODO: Merge the code with BtSedCommands.
 1232
1224 // TODO: Finally, remove the G.Testing from the main code. 1233 // TODO: Finally, remove the G.Testing from the main code.
 1234 // Then, remove this test case.
 1235 G.Testing = false
 1236 test("sed -e s,.*,match,",
 1237 nil...)
 1238 G.Testing = true
1225} 1239}
1226 1240
1227func (s *Suite) Test_ShellProgramChecker_checkSetE__simple_commands(c *check.C) { 1241func (s *Suite) Test_ShellProgramChecker_checkSetE__simple_commands(c *check.C) {
1228 t := s.Init(c) 1242 t := s.Init(c)
1229 1243
1230 t.SetUpTool("echo", "", AtRunTime) 1244 t.SetUpTool("echo", "", AtRunTime)
1231 t.SetUpTool("rm", "", AtRunTime) 1245 t.SetUpTool("rm", "", AtRunTime)
1232 t.SetUpTool("touch", "", AtRunTime) 1246 t.SetUpTool("touch", "", AtRunTime)
1233 mklines := t.NewMkLines("Makefile", 1247 mklines := t.NewMkLines("Makefile",
1234 MkRcsID, 1248 MkRcsID,
1235 "pre-configure:", 1249 "pre-configure:",
1236 "\techo 1; echo 2; echo 3", 1250 "\techo 1; echo 2; echo 3",
1237 "\techo 1; touch file; rm file", 1251 "\techo 1; touch file; rm file",

cvs diff -r1.24 -r1.25 pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go 2019/04/20 17:43:25 1.24
+++ pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go 2019/05/26 14:05:57 1.25
@@ -1,276 +1,401 @@ @@ -1,276 +1,401 @@
1package pkglint 1package pkglint
2 2
3import ( 3import (
4 "fmt" 4 "fmt"
5 "gopkg.in/check.v1" 5 "gopkg.in/check.v1"
6) 6)
7 7
8func (s *Suite) Test_SubstContext__incomplete(c *check.C) { 8func (s *Suite) Test_SubstContext__incomplete(c *check.C) {
9 t := s.Init(c) 9 t := s.Init(c)
10 10
11 t.SetUpCommandLine("-Wextra") 11 t.SetUpCommandLine("-Wextra")
12 ctx := NewSubstContext() 12 ctx := NewSubstContext()
13 13
14 ctx.Varassign(newSubstLine(t, 10, "PKGNAME=pkgname-1.0")) 14 ctx.Varassign(t.NewMkLine("Makefile", 10, "PKGNAME=pkgname-1.0"))
15 15
16 c.Check(ctx.id, equals, "") 16 c.Check(ctx.id, equals, "")
17 17
18 ctx.Varassign(newSubstLine(t, 11, "SUBST_CLASSES+=interp")) 18 ctx.Varassign(t.NewMkLine("Makefile", 11, "SUBST_CLASSES+=interp"))
19 19
20 c.Check(ctx.id, equals, "interp") 20 c.Check(ctx.id, equals, "interp")
21 21
22 ctx.Varassign(newSubstLine(t, 12, "SUBST_FILES.interp=Makefile")) 22 ctx.Varassign(t.NewMkLine("Makefile", 12, "SUBST_FILES.interp=Makefile"))
23 23
24 c.Check(ctx.IsComplete(), equals, false) 24 c.Check(ctx.IsComplete(), equals, false)
25 25
26 ctx.Varassign(newSubstLine(t, 13, "SUBST_SED.interp=s,@PREFIX@,${PREFIX},g")) 26 ctx.Varassign(t.NewMkLine("Makefile", 13, "SUBST_SED.interp=s,@PREFIX@,${PREFIX},g"))
27 27
28 c.Check(ctx.IsComplete(), equals, false) 28 c.Check(ctx.IsComplete(), equals, false)
29 29
30 ctx.Finish(newSubstLine(t, 14, "")) 30 ctx.Finish(t.NewMkLine("Makefile", 14, ""))
31 31
32 t.CheckOutputLines( 32 t.CheckOutputLines(
33 "NOTE: Makefile:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" "+ 33 "NOTE: Makefile:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" "+
34 "can be replaced with \"SUBST_VARS.interp= PREFIX\".", 34 "can be replaced with \"SUBST_VARS.interp= PREFIX\".",
35 "WARN: Makefile:14: Incomplete SUBST block: SUBST_STAGE.interp missing.") 35 "WARN: Makefile:14: Incomplete SUBST block: SUBST_STAGE.interp missing.")
36} 36}
37 37
38func (s *Suite) Test_SubstContext__complete(c *check.C) { 38func (s *Suite) Test_SubstContext__complete(c *check.C) {
39 t := s.Init(c) 39 t := s.Init(c)
40 40
41 t.SetUpCommandLine("-Wextra") 41 t.SetUpCommandLine("-Wextra")
42 ctx := NewSubstContext() 42 ctx := NewSubstContext()
43 43
44 ctx.Varassign(newSubstLine(t, 10, "PKGNAME=pkgname-1.0")) 44 ctx.Varassign(t.NewMkLine("Makefile", 10, "PKGNAME=pkgname-1.0"))
45 ctx.Varassign(newSubstLine(t, 11, "SUBST_CLASSES+=p")) 45 ctx.Varassign(t.NewMkLine("Makefile", 11, "SUBST_CLASSES+=p"))
46 ctx.Varassign(newSubstLine(t, 12, "SUBST_FILES.p=Makefile")) 46 ctx.Varassign(t.NewMkLine("Makefile", 12, "SUBST_FILES.p=Makefile"))
47 ctx.Varassign(newSubstLine(t, 13, "SUBST_SED.p=s,@PREFIX@,${PREFIX},g")) 47 ctx.Varassign(t.NewMkLine("Makefile", 13, "SUBST_SED.p=s,@PREFIX@,${PREFIX},g"))
48 48
49 c.Check(ctx.IsComplete(), equals, false) 49 c.Check(ctx.IsComplete(), equals, false)
50 50
51 ctx.Varassign(newSubstLine(t, 14, "SUBST_STAGE.p=post-configure")) 51 ctx.Varassign(t.NewMkLine("Makefile", 14, "SUBST_STAGE.p=post-configure"))
52 52
53 c.Check(ctx.IsComplete(), equals, true) 53 c.Check(ctx.IsComplete(), equals, true)
54 54
55 ctx.Finish(newSubstLine(t, 15, "")) 55 ctx.Finish(t.NewMkLine("Makefile", 15, ""))
56 56
57 t.CheckOutputLines( 57 t.CheckOutputLines(
58 "NOTE: Makefile:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" " + 58 "NOTE: Makefile:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" " +
59 "can be replaced with \"SUBST_VARS.p= PREFIX\".") 59 "can be replaced with \"SUBST_VARS.p= PREFIX\".")
60} 60}
61 61
62func (s *Suite) Test_SubstContext__OPSYSVARS(c *check.C) { 62func (s *Suite) Test_SubstContext__OPSYSVARS(c *check.C) {
63 t := s.Init(c) 63 t := s.Init(c)
64 64
65 G.Opts.WarnExtra = true 65 G.Opts.WarnExtra = true
66 ctx := NewSubstContext() 66 ctx := NewSubstContext()
67 67
68 // SUBST_CLASSES is added to OPSYSVARS in mk/bsd.pkg.mk. 68 // SUBST_CLASSES is added to OPSYSVARS in mk/bsd.pkg.mk.
69 ctx.Varassign(newSubstLine(t, 11, "SUBST_CLASSES.SunOS+=prefix")) 69 ctx.Varassign(t.NewMkLine("Makefile", 11, "SUBST_CLASSES.SunOS+=prefix"))
70 ctx.Varassign(newSubstLine(t, 12, "SUBST_CLASSES.NetBSD+=prefix")) 70 ctx.Varassign(t.NewMkLine("Makefile", 12, "SUBST_CLASSES.NetBSD+=prefix"))
71 ctx.Varassign(newSubstLine(t, 13, "SUBST_FILES.prefix=Makefile")) 71 ctx.Varassign(t.NewMkLine("Makefile", 13, "SUBST_FILES.prefix=Makefile"))
72 ctx.Varassign(newSubstLine(t, 14, "SUBST_SED.prefix=s,@PREFIX@,${PREFIX},g")) 72 ctx.Varassign(t.NewMkLine("Makefile", 14, "SUBST_SED.prefix=s,@PREFIX@,${PREFIX},g"))
73 ctx.Varassign(newSubstLine(t, 15, "SUBST_STAGE.prefix=post-configure")) 73 ctx.Varassign(t.NewMkLine("Makefile", 15, "SUBST_STAGE.prefix=post-configure"))
74 74
75 c.Check(ctx.IsComplete(), equals, true) 75 c.Check(ctx.IsComplete(), equals, true)
76 76
77 ctx.Finish(newSubstLine(t, 15, "")) 77 ctx.Finish(t.NewMkLine("Makefile", 15, ""))
78 78
79 t.CheckOutputLines( 79 t.CheckOutputLines(
80 "NOTE: Makefile:14: The substitution command \"s,@PREFIX@,${PREFIX},g\" " + 80 "NOTE: Makefile:14: The substitution command \"s,@PREFIX@,${PREFIX},g\" " +
81 "can be replaced with \"SUBST_VARS.prefix= PREFIX\".") 81 "can be replaced with \"SUBST_VARS.prefix= PREFIX\".")
82} 82}
83 83
84func (s *Suite) Test_SubstContext__no_class(c *check.C) { 84func (s *Suite) Test_SubstContext__no_class(c *check.C) {
85 t := s.Init(c) 85 t := s.Init(c)
86 86
87 t.SetUpCommandLine("-Wextra") 87 t.SetUpCommandLine("-Wextra")
88 ctx := NewSubstContext() 88 ctx := NewSubstContext()
89 89
90 ctx.Varassign(newSubstLine(t, 10, "UNRELATED=anything")) 90 ctx.Varassign(t.NewMkLine("Makefile", 10, "UNRELATED=anything"))
91 ctx.Varassign(newSubstLine(t, 11, "SUBST_FILES.repl+=Makefile.in")) 91 ctx.Varassign(t.NewMkLine("Makefile", 11, "SUBST_FILES.repl+=Makefile.in"))
92 ctx.Varassign(newSubstLine(t, 12, "SUBST_SED.repl+=-e s,from,to,g")) 92 ctx.Varassign(t.NewMkLine("Makefile", 12, "SUBST_SED.repl+=-e s,from,to,g"))
93 ctx.Finish(newSubstLine(t, 13, "")) 93 ctx.Finish(t.NewMkLine("Makefile", 13, ""))
94 94
95 t.CheckOutputLines( 95 t.CheckOutputLines(
96 "WARN: Makefile:11: SUBST_CLASSES should come before the definition of \"SUBST_FILES.repl\".", 96 "WARN: Makefile:11: SUBST_CLASSES should come before the definition of \"SUBST_FILES.repl\".",
97 "WARN: Makefile:13: Incomplete SUBST block: SUBST_STAGE.repl missing.") 97 "WARN: Makefile:13: Incomplete SUBST block: SUBST_STAGE.repl missing.")
98} 98}
99 99
100func (s *Suite) Test_SubstContext__multiple_classes_in_one_line(c *check.C) { 100func (s *Suite) Test_SubstContext__multiple_classes_in_one_line(c *check.C) {
101 t := s.Init(c) 101 t := s.Init(c)
102 102
103 t.SetUpCommandLine("-Wextra") 103 t.SetUpCommandLine("-Wextra")
104 104
105 simulateSubstLines(t, 105 simulateSubstLines(t,
106 "10: SUBST_CLASSES+= one two", 106 "10: SUBST_CLASSES+= one two",
107 "11: SUBST_STAGE.one= post-configure", 107 "11: SUBST_STAGE.one= post-configure",
108 "12: SUBST_FILES.one= one.txt", 108 "12: SUBST_FILES.one= one.txt",
109 "13: SUBST_SED.one= s,one,1,g", 109 "13: SUBST_SED.one= s,one,1,g",
110 "14: SUBST_STAGE.two= post-configure", 110 "14: SUBST_STAGE.two= post-configure",
111 "15: SUBST_FILES.two= two.txt", 111 "15: SUBST_FILES.two= two.txt")
112 "17: ") 
113 112
114 t.CheckOutputLines( 113 t.CheckOutputLines(
115 "WARN: Makefile:10: Please add only one class at a time to SUBST_CLASSES.", 114 "WARN: Makefile:10: Please add only one class at a time to SUBST_CLASSES.",
116 "WARN: Makefile:17: Incomplete SUBST block: SUBST_SED.two, SUBST_VARS.two or SUBST_FILTER_CMD.two missing.") 115 "WARN: Makefile:16: Incomplete SUBST block: SUBST_SED.two, SUBST_VARS.two or SUBST_FILTER_CMD.two missing.")
117} 116}
118 117
119func (s *Suite) Test_SubstContext__multiple_classes_in_one_block(c *check.C) { 118func (s *Suite) Test_SubstContext__multiple_classes_in_one_block(c *check.C) {
120 t := s.Init(c) 119 t := s.Init(c)
121 120
122 t.SetUpCommandLine("-Wextra") 121 t.SetUpCommandLine("-Wextra")
123 122
124 simulateSubstLines(t, 123 simulateSubstLines(t,
125 "10: SUBST_CLASSES+= one", 124 "10: SUBST_CLASSES+= one",
126 "11: SUBST_STAGE.one= post-configure", 125 "11: SUBST_STAGE.one= post-configure",
127 "12: SUBST_STAGE.one= post-configure", 126 "12: SUBST_STAGE.one= post-configure",
128 "13: SUBST_FILES.one= one.txt", 127 "13: SUBST_FILES.one= one.txt",
129 "14: SUBST_CLASSES+= two", // The block "one" is not finished yet. 128 "14: SUBST_CLASSES+= two", // The block "one" is not finished yet.
130 "15: SUBST_SED.one= s,one,1,g", 129 "15: SUBST_SED.one= s,one,1,g",
131 "16: SUBST_STAGE.two= post-configure", 130 "16: SUBST_STAGE.two= post-configure",
132 "17: SUBST_FILES.two= two.txt", 131 "17: SUBST_FILES.two= two.txt",
133 "18: SUBST_SED.two= s,two,2,g", 132 "18: SUBST_SED.two= s,two,2,g")
134 "19: ") 
135 133
136 t.CheckOutputLines( 134 t.CheckOutputLines(
137 "WARN: Makefile:12: Duplicate definition of \"SUBST_STAGE.one\".", 135 "WARN: Makefile:12: Duplicate definition of \"SUBST_STAGE.one\".",
138 "WARN: Makefile:14: Incomplete SUBST block: SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.", 136 "WARN: Makefile:14: Incomplete SUBST block: SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.",
139 "WARN: Makefile:14: Subst block \"one\" should be finished before adding the next class to SUBST_CLASSES.", 137 "WARN: Makefile:14: Subst block \"one\" should be finished before adding the next class to SUBST_CLASSES.",
140 "WARN: Makefile:15: Variable \"SUBST_SED.one\" does not match SUBST class \"two\".") 138 "WARN: Makefile:15: Variable \"SUBST_SED.one\" does not match SUBST class \"two\".")
141} 139}
142 140
 141func (s *Suite) Test_SubstContext__files_missing(c *check.C) {
 142 t := s.Init(c)
 143
 144 simulateSubstLines(t,
 145 "10: SUBST_CLASSES+= one",
 146 "11: SUBST_STAGE.one= pre-configure",
 147 "12: SUBST_CLASSES+= two",
 148 "13: SUBST_STAGE.two= pre-configure",
 149 "14: SUBST_FILES.two= two.txt",
 150 "15: SUBST_SED.two= s,two,2,g")
 151
 152 t.CheckOutputLines(
 153 "WARN: Makefile:12: Incomplete SUBST block: SUBST_FILES.one missing.",
 154 "WARN: Makefile:12: Incomplete SUBST block: "+
 155 "SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.",
 156 "WARN: Makefile:12: Subst block \"one\" should be finished "+
 157 "before adding the next class to SUBST_CLASSES.")
 158}
 159
143func (s *Suite) Test_SubstContext__directives(c *check.C) { 160func (s *Suite) Test_SubstContext__directives(c *check.C) {
144 t := s.Init(c) 161 t := s.Init(c)
145 162
146 t.SetUpCommandLine("-Wextra") 163 t.SetUpCommandLine("-Wextra")
147 164
148 simulateSubstLines(t, 165 simulateSubstLines(t,
149 "10: SUBST_CLASSES+= os", 166 "10: SUBST_CLASSES+= os",
150 "11: SUBST_STAGE.os= post-configure", 167 "11: SUBST_STAGE.os= post-configure",
151 "12: SUBST_MESSAGE.os= Guessing operating system", 168 "12: SUBST_MESSAGE.os= Guessing operating system",
152 "13: SUBST_FILES.os= guess-os.h", 169 "13: SUBST_FILES.os= guess-os.h",
153 "14: .if ${OPSYS} == NetBSD", 170 "14: .if ${OPSYS} == NetBSD",
154 "15: SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS@,NetBSD,", 171 "15: SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS@,NetBSD,",
155 "16: .elif ${OPSYS} == Darwin", 172 "16: .elif ${OPSYS} == Darwin",
156 "17: SUBST_SED.os= -e s,@OPSYS@,Darwin1,", 173 "17: SUBST_SED.os= -e s,@OPSYS@,Darwin1,",
157 "18: SUBST_SED.os= -e s,@OPSYS@,Darwin2,", 174 "18: SUBST_SED.os= -e s,@OPSYS@,Darwin2,",
158 "19: .elif ${OPSYS} == Linux", 175 "19: .elif ${OPSYS} == Linux",
159 "18: SUBST_SED.os= -e s,@OPSYS@,Linux,", 176 "20: SUBST_SED.os= -e s,@OPSYS@,Linux,",
160 "19: .else", 177 "21: .else",
161 "20: SUBST_VARS.os= OPSYS", 178 "22: SUBST_VARS.os= OPSYS",
162 "21: .endif", 179 "23: .endif")
163 "22: ") 
164 180
165 // All the other lines are correctly determined as being alternatives 181 // All the other lines are correctly determined as being alternatives
166 // to each other. And since every branch contains some transformation 182 // to each other. And since every branch contains some transformation
167 // (SED, VARS, FILTER_CMD), everything is fine. 183 // (SED, VARS, FILTER_CMD), everything is fine.
168 t.CheckOutputLines( 184 t.CheckOutputLines(
169 "WARN: Makefile:18: All but the first \"SUBST_SED.os\" lines should use the \"+=\" operator.") 185 "WARN: Makefile:18: All but the first \"SUBST_SED.os\" lines should use the \"+=\" operator.")
170} 186}
171 187
 188func (s *Suite) Test_SubstContext__directives_around_everything_then(c *check.C) {
 189 t := s.Init(c)
 190
 191 t.SetUpCommandLine("-Wextra")
 192
 193 simulateSubstLines(t,
 194 "10: SUBST_CLASSES+= os",
 195 "11: .if ${OPSYS} == NetBSD",
 196 "12: SUBST_VARS.os= OPSYS",
 197 "13: SUBST_SED.os= -e s,@OPSYS@,NetBSD,",
 198 "14: SUBST_STAGE.os= post-configure",
 199 "15: SUBST_MESSAGE.os= Guessing operating system",
 200 "16: SUBST_FILES.os= guess-os.h",
 201 "17: .endif")
 202
 203 // TODO: The SUBST variables are not guaranteed to be defined in all cases.
 204 t.CheckOutputEmpty()
 205}
 206
 207func (s *Suite) Test_SubstContext__directives_around_everything_else(c *check.C) {
 208 t := s.Init(c)
 209
 210 t.SetUpCommandLine("-Wextra")
 211
 212 simulateSubstLines(t,
 213 "10: SUBST_CLASSES+= os",
 214 "11: .if ${OPSYS} == NetBSD",
 215 "12: .else",
 216 "13: SUBST_VARS.os= OPSYS",
 217 "14: SUBST_SED.os= -e s,@OPSYS@,NetBSD,",
 218 "15: SUBST_STAGE.os= post-configure",
 219 "16: SUBST_MESSAGE.os= Guessing operating system",
 220 "17: SUBST_FILES.os= guess-os.h",
 221 "18: .endif")
 222
 223 // FIXME: The warnings must be the same as in the "then" test case.
 224 t.CheckOutputLines(
 225 "WARN: Makefile:19: Incomplete SUBST block: SUBST_FILES.os missing.",
 226 "WARN: Makefile:19: Incomplete SUBST block: SUBST_SED.os, SUBST_VARS.os or "+
 227 "SUBST_FILTER_CMD.os missing.")
 228}
 229
 230func (s *Suite) Test_SubstContext__empty_directive(c *check.C) {
 231 t := s.Init(c)
 232
 233 t.SetUpCommandLine("-Wextra")
 234
 235 simulateSubstLines(t,
 236 "10: SUBST_CLASSES+= os",
 237 "11: SUBST_VARS.os= OPSYS",
 238 "12: SUBST_SED.os= -e s,@OPSYS@,NetBSD,",
 239 "13: SUBST_STAGE.os= post-configure",
 240 "14: SUBST_MESSAGE.os= Guessing operating system",
 241 "15: SUBST_FILES.os= guess-os.h",
 242 "16: .if ${OPSYS} == NetBSD",
 243 "17: .else",
 244 "18: .endif")
 245
 246 t.CheckOutputEmpty()
 247}
 248
172func (s *Suite) Test_SubstContext__missing_transformation_in_one_branch(c *check.C) { 249func (s *Suite) Test_SubstContext__missing_transformation_in_one_branch(c *check.C) {
173 t := s.Init(c) 250 t := s.Init(c)
174 251
175 t.SetUpCommandLine("-Wextra") 252 t.SetUpCommandLine("-Wextra")
176 253
177 simulateSubstLines(t, 254 simulateSubstLines(t,
178 "10: SUBST_CLASSES+= os", 255 "10: SUBST_CLASSES+= os",
179 "11: SUBST_STAGE.os= post-configure", 256 "11: SUBST_STAGE.os= post-configure",
180 "12: SUBST_MESSAGE.os= Guessing operating system", 257 "12: SUBST_MESSAGE.os= Guessing operating system",
181 "13: SUBST_FILES.os= guess-os.h", 258 "13: SUBST_FILES.os= guess-os.h",
182 "14: .if ${OPSYS} == NetBSD", 259 "14: .if ${OPSYS} == NetBSD",
183 "15: SUBST_FILES.os= -e s,@OpSYS@,NetBSD,", // A simple typo, this should be SUBST_SED. 260 "15: SUBST_FILES.os= -e s,@OpSYS@,NetBSD,", // A simple typo, this should be SUBST_SED.
184 "16: .elif ${OPSYS} == Darwin", 261 "16: .elif ${OPSYS} == Darwin",
185 "17: SUBST_SED.os= -e s,@OPSYS@,Darwin1,", 262 "17: SUBST_SED.os= -e s,@OPSYS@,Darwin1,",
186 "18: SUBST_SED.os= -e s,@OPSYS@,Darwin2,", 263 "18: SUBST_SED.os= -e s,@OPSYS@,Darwin2,",
187 "19: .else", 264 "19: .else",
188 "20: SUBST_VARS.os= OPSYS", 265 "20: SUBST_VARS.os= OPSYS",
189 "21: .endif", 266 "21: .endif")
190 "22: ") 
191 267
192 t.CheckOutputLines( 268 t.CheckOutputLines(
193 "WARN: Makefile:15: All but the first \"SUBST_FILES.os\" lines should use the \"+=\" operator.", 269 "WARN: Makefile:15: All but the first \"SUBST_FILES.os\" lines should use the \"+=\" operator.",
194 "WARN: Makefile:18: All but the first \"SUBST_SED.os\" lines should use the \"+=\" operator.", 270 "WARN: Makefile:18: All but the first \"SUBST_SED.os\" lines should use the \"+=\" operator.",
195 "WARN: Makefile:22: Incomplete SUBST block: SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.") 271 "WARN: Makefile:22: Incomplete SUBST block: SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.")
196} 272}
197 273
198func (s *Suite) Test_SubstContext__nested_conditionals(c *check.C) { 274func (s *Suite) Test_SubstContext__nested_conditionals(c *check.C) {
199 t := s.Init(c) 275 t := s.Init(c)
200 276
201 t.SetUpCommandLine("-Wextra") 277 t.SetUpCommandLine("-Wextra")
202 278
203 simulateSubstLines(t, 279 simulateSubstLines(t,
204 "10: SUBST_CLASSES+= os", 280 "10: SUBST_CLASSES+= os",
205 "11: SUBST_STAGE.os= post-configure", 281 "11: SUBST_STAGE.os= post-configure",
206 "12: SUBST_MESSAGE.os= Guessing operating system", 282 "12: SUBST_MESSAGE.os= Guessing operating system",
207 "14: .if ${OPSYS} == NetBSD", 283 "13: .if ${OPSYS} == NetBSD",
208 "13: SUBST_FILES.os= guess-netbsd.h", 284 "14: SUBST_FILES.os= guess-netbsd.h",
209 "15: . if ${ARCH} == i386", 285 "15: . if ${ARCH} == i386",
210 "16: SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS,NetBSD-i386,", 286 "16: SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS,NetBSD-i386,",
211 "17: . elif ${ARCH} == x86_64", 287 "17: . elif ${ARCH} == x86_64",
212 "18: SUBST_VARS.os= OPSYS", 288 "18: SUBST_VARS.os= OPSYS",
213 "19: . else", 289 "19: . else",
214 "20: SUBST_SED.os= -e s,@OPSYS,NetBSD-unknown", 290 "20: SUBST_SED.os= -e s,@OPSYS,NetBSD-unknown",
215 "21: . endif", 291 "21: . endif",
216 "22: .else", 292 "22: .else",
217 "23: SUBST_SED.os= -e s,@OPSYS@,unknown,", 293 "23: SUBST_SED.os= -e s,@OPSYS@,unknown,",
218 "24: .endif", 294 "24: .endif")
219 "25: ") 
220 295
221 // The branch in line 23 omits SUBST_FILES. 296 // The branch in line 23 omits SUBST_FILES.
222 t.CheckOutputLines( 297 t.CheckOutputLines(
223 "WARN: Makefile:25: Incomplete SUBST block: SUBST_FILES.os missing.") 298 "WARN: Makefile:25: Incomplete SUBST block: SUBST_FILES.os missing.")
224} 299}
225 300
 301func (s *Suite) Test_SubstContext__pre_patch(c *check.C) {
 302 t := s.Init(c)
 303
 304 t.SetUpCommandLine("-Wextra,no-space", "--show-autofix")
 305 t.SetUpVartypes()
 306
 307 mklines := t.NewMkLines("os.mk",
 308 MkRcsID,
 309 "",
 310 "SUBST_CLASSES+= os",
 311 "SUBST_STAGE.os= pre-patch",
 312 "SUBST_FILES.os= guess-os.h",
 313 "SUBST_SED.os= -e s,@OPSYS@,Darwin,")
 314
 315 mklines.Check()
 316
 317 t.CheckOutputLines(
 318 "WARN: os.mk:4: Substitutions should not happen in the patch phase.",
 319 "AUTOFIX: os.mk:4: Replacing \"pre-patch\" with \"post-extract\".")
 320}
 321
226func (s *Suite) Test_SubstContext__post_patch(c *check.C) { 322func (s *Suite) Test_SubstContext__post_patch(c *check.C) {
227 t := s.Init(c) 323 t := s.Init(c)
228 324
229 t.SetUpCommandLine("-Wextra,no-space", "--show-autofix") 325 t.SetUpCommandLine("-Wextra,no-space", "--show-autofix")
230 t.SetUpVartypes() 326 t.SetUpVartypes()
231 327
232 mklines := t.NewMkLines("os.mk", 328 mklines := t.NewMkLines("os.mk",
233 MkRcsID, 329 MkRcsID,
234 "", 330 "",
235 "SUBST_CLASSES+= os", 331 "SUBST_CLASSES+= os",
236 "SUBST_STAGE.os= post-patch", 332 "SUBST_STAGE.os= post-patch",
237 "SUBST_FILES.os= guess-os.h", 333 "SUBST_FILES.os= guess-os.h",
238 "SUBST_SED.os= -e s,@OPSYS@,Darwin,") 334 "SUBST_SED.os= -e s,@OPSYS@,Darwin,")
239 335
240 mklines.Check() 336 mklines.Check()
241 337
242 t.CheckOutputLines( 338 t.CheckOutputLines(
243 "WARN: os.mk:4: Substitutions should not happen in the patch phase.", 339 "WARN: os.mk:4: Substitutions should not happen in the patch phase.",
244 "AUTOFIX: os.mk:4: Replacing \"post-patch\" with \"pre-configure\".") 340 "AUTOFIX: os.mk:4: Replacing \"post-patch\" with \"pre-configure\".")
245} 341}
246 342
247func (s *Suite) Test_SubstContext__pre_configure_with_NO_CONFIGURE(c *check.C) { 343func (s *Suite) Test_SubstContext__with_NO_CONFIGURE(c *check.C) {
248 t := s.Init(c) 344 t := s.Init(c)
249 345
250 t.SetUpCommandLine("-Wall,no-space") 346 t.SetUpCommandLine("-Wall,no-space")
251 pkg := t.SetUpPackage("category/package", 347 pkg := t.SetUpPackage("category/package",
252 "SUBST_CLASSES+= os", 348 "SUBST_CLASSES+= pre",
253 "SUBST_STAGE.os= pre-configure", 349 "SUBST_STAGE.pre= pre-configure",
254 "SUBST_FILES.os= guess-os.h", 350 "SUBST_FILES.pre= guess-os.h",
255 "SUBST_SED.os= -e s,@OPSYS@,Darwin,", 351 "SUBST_SED.pre= -e s,@OPSYS@,Darwin,",
 352 "",
 353 "SUBST_CLASSES+= post",
 354 "SUBST_STAGE.post= post-configure",
 355 "SUBST_FILES.post= guess-os.h",
 356 "SUBST_SED.post= -e s,@OPSYS@,Darwin,",
 357 "",
 358 "SUBST_CLASSES+= e",
 359 "SUBST_STAGE.e= post-extract",
 360 "SUBST_FILES.e= guess-os.h",
 361 "SUBST_SED.e= -e s,@OPSYS@,Darwin,",
256 "", 362 "",
257 "NO_CONFIGURE= yes") 363 "NO_CONFIGURE= yes")
258 t.FinishSetUp() 364 t.FinishSetUp()
259 365
260 G.Check(pkg) 366 G.Check(pkg)
261 367
262 t.CheckOutputLines( 368 t.CheckOutputLines(
263 "WARN: ~/category/package/Makefile:21: SUBST_STAGE pre-configure has no effect when NO_CONFIGURE is set (in line 25).") 369 "WARN: ~/category/package/Makefile:21: SUBST_STAGE pre-configure has no effect "+
 370 "when NO_CONFIGURE is set (in line 35).",
 371 "WARN: ~/category/package/Makefile:26: SUBST_STAGE post-configure has no effect "+
 372 "when NO_CONFIGURE is set (in line 35).")
 373}
 374
 375func (s *Suite) Test_SubstContext__without_NO_CONFIGURE(c *check.C) {
 376 t := s.Init(c)
 377
 378 t.SetUpCommandLine("-Wall,no-space")
 379 pkg := t.SetUpPackage("category/package",
 380 "SUBST_CLASSES+= pre",
 381 "SUBST_STAGE.pre= pre-configure",
 382 "SUBST_FILES.pre= guess-os.h",
 383 "SUBST_SED.pre= -e s,@OPSYS@,Darwin,")
 384 t.FinishSetUp()
 385
 386 G.Check(pkg)
 387
 388 t.CheckOutputEmpty()
264} 389}
265 390
266func (s *Suite) Test_SubstContext__adjacent(c *check.C) { 391func (s *Suite) Test_SubstContext__adjacent(c *check.C) {
267 t := s.Init(c) 392 t := s.Init(c)
268 393
269 t.SetUpCommandLine("-Wextra") 394 t.SetUpCommandLine("-Wextra")
270 t.SetUpVartypes() 395 t.SetUpVartypes()
271 396
272 mklines := t.NewMkLines("os.mk", 397 mklines := t.NewMkLines("os.mk",
273 MkRcsID, 398 MkRcsID,
274 "", 399 "",
275 "SUBST_CLASSES+= 1", 400 "SUBST_CLASSES+= 1",
276 "SUBST_STAGE.1= pre-configure", 401 "SUBST_STAGE.1= pre-configure",
@@ -349,26 +474,71 @@ func (s *Suite) Test_SubstContext__SUBST @@ -349,26 +474,71 @@ func (s *Suite) Test_SubstContext__SUBST
349 "SUBST_STAGE.os= pre-configure", 474 "SUBST_STAGE.os= pre-configure",
350 "SUBST_FILES.os= guess-os.h", 475 "SUBST_FILES.os= guess-os.h",
351 "SUBST_VARS.os= TODAY1", 476 "SUBST_VARS.os= TODAY1",
352 "", 477 "",
353 "TODAY1!= date", 478 "TODAY1!= date",
354 "TODAY2!= date") 479 "TODAY2!= date")
355 480
356 mklines.Check() 481 mklines.Check()
357 482
358 t.CheckOutputLines( 483 t.CheckOutputLines(
359 "WARN: os.mk:9: TODAY2 is defined but not used.") 484 "WARN: os.mk:9: TODAY2 is defined but not used.")
360} 485}
361 486
 487func (s *Suite) Test_SubstContext__multiple_SUBST_VARS(c *check.C) {
 488 t := s.Init(c)
 489
 490 t.SetUpCommandLine("-Wextra,no-space")
 491 t.SetUpVartypes()
 492
 493 mklines := t.NewMkLines("os.mk",
 494 MkRcsID,
 495 "",
 496 "SUBST_CLASSES+= os",
 497 "SUBST_STAGE.os= pre-configure",
 498 "SUBST_FILES.os= guess-os.h",
 499 "SUBST_VARS.os= PREFIX VARBASE")
 500
 501 mklines.Check()
 502
 503 t.CheckOutputEmpty()
 504}
 505
 506// Since the SUBST_CLASSES definition starts the SUBST block, all
 507// directives above it are ignored by the SUBST context.
 508func (s *Suite) Test_SubstContext_Directive__before_SUBST_CLASSES(c *check.C) {
 509 t := s.Init(c)
 510
 511 t.SetUpCommandLine("-Wextra,no-space")
 512 t.SetUpVartypes()
 513 t.DisableTracing() // Just for branch coverage.
 514
 515 mklines := t.NewMkLines("os.mk",
 516 MkRcsID,
 517 "",
 518 ".if 0",
 519 ".endif",
 520 "SUBST_CLASSES+= os",
 521 ".elif 0") // Just for branch coverage.
 522
 523 mklines.Check()
 524
 525 t.CheckOutputLines(
 526 "WARN: os.mk:EOF: Incomplete SUBST block: SUBST_STAGE.os missing.",
 527 "WARN: os.mk:EOF: Incomplete SUBST block: SUBST_FILES.os missing.",
 528 "WARN: os.mk:EOF: Incomplete SUBST block: "+
 529 "SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.")
 530}
 531
362func (s *Suite) Test_SubstContext_suggestSubstVars(c *check.C) { 532func (s *Suite) Test_SubstContext_suggestSubstVars(c *check.C) {
363 t := s.Init(c) 533 t := s.Init(c)
364 534
365 t.SetUpVartypes() 535 t.SetUpVartypes()
366 t.SetUpTool("sh", "SH", AtRunTime) 536 t.SetUpTool("sh", "SH", AtRunTime)
367 537
368 mklines := t.NewMkLines("subst.mk", 538 mklines := t.NewMkLines("subst.mk",
369 MkRcsID, 539 MkRcsID,
370 "", 540 "",
371 "SUBST_CLASSES+=\t\ttest", 541 "SUBST_CLASSES+=\t\ttest",
372 "SUBST_STAGE.test=\tpre-configure", 542 "SUBST_STAGE.test=\tpre-configure",
373 "SUBST_FILES.test=\tfilename", 543 "SUBST_FILES.test=\tfilename",
374 "SUBST_SED.test+=\t-e s,@SH@,${SH},g", // Can be replaced. 544 "SUBST_SED.test+=\t-e s,@SH@,${SH},g", // Can be replaced.
@@ -472,38 +642,219 @@ func (s *Suite) Test_SubstContext_sugges @@ -472,38 +642,219 @@ func (s *Suite) Test_SubstContext_sugges
472 mklines.Check() 642 mklines.Check()
473 643
474 t.CheckOutputLines( 644 t.CheckOutputLines(
475 "NOTE: subst.mk:6: The substitution command \"s,@SH@,${SH:Q},g\" "+ 645 "NOTE: subst.mk:6: The substitution command \"s,@SH@,${SH:Q},g\" "+
476 "can be replaced with \"SUBST_VARS.gtk+ = SH\".", 646 "can be replaced with \"SUBST_VARS.gtk+ = SH\".",
477 "AUTOFIX: subst.mk:6: Replacing \"SUBST_SED.gtk+ +=\\t-e s,@SH@,${SH:Q},g\" "+ 647 "AUTOFIX: subst.mk:6: Replacing \"SUBST_SED.gtk+ +=\\t-e s,@SH@,${SH:Q},g\" "+
478 "with \"SUBST_VARS.gtk+ =\\tSH\".", 648 "with \"SUBST_VARS.gtk+ =\\tSH\".",
479 "NOTE: subst.mk:7: The substitution command \"s,@SH@,${SH:Q},g\" "+ 649 "NOTE: subst.mk:7: The substitution command \"s,@SH@,${SH:Q},g\" "+
480 "can be replaced with \"SUBST_VARS.gtk+ += SH\".", 650 "can be replaced with \"SUBST_VARS.gtk+ += SH\".",
481 "AUTOFIX: subst.mk:7: Replacing \"SUBST_SED.gtk+ +=\\t-e s,@SH@,${SH:Q},g\" "+ 651 "AUTOFIX: subst.mk:7: Replacing \"SUBST_SED.gtk+ +=\\t-e s,@SH@,${SH:Q},g\" "+
482 "with \"SUBST_VARS.gtk+ +=\\tSH\".") 652 "with \"SUBST_VARS.gtk+ +=\\tSH\".")
483} 653}
484 654
 655// The last of the SUBST_SED variables is 15 characters wide. When SUBST_SED
 656// is replaced with SUBST_VARS, this becomes 16 characters and therefore
 657// requires the whole paragraph to be indented by one more tab.
 658func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_realign_paragraph(c *check.C) {
 659 t := s.Init(c)
 660
 661 t.SetUpVartypes()
 662 t.Chdir(".")
 663
 664 mklines := t.SetUpFileMkLines("subst.mk",
 665 MkRcsID,
 666 "",
 667 "SUBST_CLASSES+=\t\tpfx",
 668 "SUBST_STAGE.pfx=\tpre-configure",
 669 "SUBST_FILES.pfx=\tfilename",
 670 "SUBST_SED.pfx=\t\t-e s,@PREFIX@,${PREFIX},g",
 671 "SUBST_SED.pfx+=\t\t-e s,@PREFIX@,${PREFIX},g")
 672
 673 mklines.Check()
 674
 675 t.CheckOutputLines(
 676 "NOTE: subst.mk:6: The substitution command \"s,@PREFIX@,${PREFIX},g\" "+
 677 "can be replaced with \"SUBST_VARS.pfx= PREFIX\".",
 678 "NOTE: subst.mk:7: The substitution command \"s,@PREFIX@,${PREFIX},g\" "+
 679 "can be replaced with \"SUBST_VARS.pfx+= PREFIX\".")
 680
 681 t.SetUpCommandLine("--autofix")
 682
 683 mklines.Check()
 684
 685 t.CheckOutputLines(
 686 "AUTOFIX: subst.mk:6: Replacing \"SUBST_SED.pfx=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+
 687 "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".",
 688 "AUTOFIX: subst.mk:7: Replacing \"SUBST_SED.pfx+=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+
 689 "with \"SUBST_VARS.pfx+=\\tPREFIX\".")
 690
 691 t.CheckFileLinesDetab("subst.mk",
 692 "# $NetBSD: substcontext_test.go,v 1.25 2019/05/26 14:05:57 rillig Exp $",
 693 "",
 694 "SUBST_CLASSES+= pfx",
 695 "SUBST_STAGE.pfx= pre-configure",
 696 "SUBST_FILES.pfx= filename",
 697 "SUBST_VARS.pfx= PREFIX",
 698 "SUBST_VARS.pfx+= PREFIX")
 699}
 700
 701func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_plus_sed(c *check.C) {
 702 t := s.Init(c)
 703
 704 t.SetUpVartypes()
 705 t.Chdir(".")
 706
 707 mklines := t.SetUpFileMkLines("subst.mk",
 708 MkRcsID,
 709 "",
 710 "SUBST_CLASSES+=\t\tpfx",
 711 "SUBST_STAGE.pfx=\tpre-configure",
 712 "SUBST_FILES.pfx=\tfilename",
 713 "SUBST_SED.pfx=\t\t-e s,@PREFIX@,${PREFIX},g",
 714 "SUBST_SED.pfx+=\t\t-e s,@PREFIX@,other,g")
 715
 716 mklines.Check()
 717
 718 t.CheckOutputLines(
 719 "NOTE: subst.mk:6: The substitution command \"s,@PREFIX@,${PREFIX},g\" " +
 720 "can be replaced with \"SUBST_VARS.pfx= PREFIX\".")
 721
 722 t.SetUpCommandLine("-Wall", "--autofix")
 723
 724 mklines.Check()
 725
 726 t.CheckOutputLines(
 727 "AUTOFIX: subst.mk:6: Replacing \"SUBST_SED.pfx=\\t\\t-e s,@PREFIX@,${PREFIX},g\" " +
 728 "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".")
 729
 730 t.CheckFileLinesDetab("subst.mk",
 731 "# $NetBSD: substcontext_test.go,v 1.25 2019/05/26 14:05:57 rillig Exp $",
 732 "",
 733 "SUBST_CLASSES+= pfx",
 734 "SUBST_STAGE.pfx= pre-configure",
 735 "SUBST_FILES.pfx= filename",
 736 "SUBST_VARS.pfx= PREFIX",
 737 // TODO: If this subst class is used nowhere else, pkglint could
 738 // replace this += with a simple =.
 739 "SUBST_SED.pfx+= -e s,@PREFIX@,other,g")
 740}
 741
 742func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_plus_vars(c *check.C) {
 743 t := s.Init(c)
 744
 745 t.SetUpCommandLine("-Wall", "--autofix")
 746 t.SetUpVartypes()
 747 t.Chdir(".")
 748
 749 mklines := t.SetUpFileMkLines("subst.mk",
 750 MkRcsID,
 751 "",
 752 "SUBST_CLASSES+=\tid",
 753 "SUBST_STAGE.id=\tpre-configure",
 754 "SUBST_FILES.id=\tfilename",
 755 "SUBST_SED.id=\t-e s,@PREFIX@,${PREFIX},g",
 756 "SUBST_VARS.id=\tPKGMANDIR")
 757
 758 mklines.Check()
 759
 760 t.CheckOutputLines(
 761 "AUTOFIX: subst.mk:6: Replacing \"SUBST_SED.id=\\t-e s,@PREFIX@,${PREFIX},g\" " +
 762 "with \"SUBST_VARS.id=\\tPREFIX\".")
 763
 764 t.CheckFileLinesDetab("subst.mk",
 765 "# $NetBSD: substcontext_test.go,v 1.25 2019/05/26 14:05:57 rillig Exp $",
 766 "",
 767 "SUBST_CLASSES+= id",
 768 "SUBST_STAGE.id= pre-configure",
 769 "SUBST_FILES.id= filename",
 770 "SUBST_VARS.id= PREFIX",
 771 // FIXME: This must be += instead of = since the previous line already uses =.
 772 // Luckily the check for redundant assignments catches this already.
 773 "SUBST_VARS.id= PKGMANDIR")
 774}
 775
 776func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_indentation(c *check.C) {
 777 t := s.Init(c)
 778
 779 t.SetUpCommandLine("-Wall", "--autofix")
 780 t.SetUpVartypes()
 781 t.Chdir(".")
 782
 783 mklines := t.SetUpFileMkLines("subst.mk",
 784 MkRcsID,
 785 "",
 786 "SUBST_CLASSES+=\t\t\tfix-paths",
 787 "SUBST_STAGE.fix-paths=\t\tpre-configure",
 788 "SUBST_MESSAGE.fix-paths=\tMessage",
 789 "SUBST_FILES.fix-paths=\t\tfilename",
 790 "SUBST_SED.fix-paths=\t\t-e s,@PREFIX@,${PREFIX},g")
 791
 792 mklines.Check()
 793
 794 t.CheckOutputLines(
 795 "AUTOFIX: subst.mk:7: Replacing \"SUBST_SED.fix-paths=\\t\\t-e s,@PREFIX@,${PREFIX},g\" " +
 796 "with \"SUBST_VARS.fix-paths=\\t\\tPREFIX\".")
 797
 798 t.CheckFileLinesDetab("subst.mk",
 799 "# $NetBSD: substcontext_test.go,v 1.25 2019/05/26 14:05:57 rillig Exp $",
 800 "",
 801 "SUBST_CLASSES+= fix-paths",
 802 "SUBST_STAGE.fix-paths= pre-configure",
 803 "SUBST_MESSAGE.fix-paths= Message",
 804 "SUBST_FILES.fix-paths= filename",
 805 "SUBST_VARS.fix-paths= PREFIX")
 806}
 807
 808// As of May 2019, pkglint does not check the order of the variables in
 809// a SUBST block. Enforcing this order, or at least suggesting it, would
 810// make pkgsrc packages more uniform, which is a good idea, but not urgent.
 811func (s *Suite) Test_SubstContext__unusual_variable_order(c *check.C) {
 812 t := s.Init(c)
 813
 814 t.SetUpVartypes()
 815
 816 mklines := t.NewMkLines("subst.mk",
 817 MkRcsID,
 818 "",
 819 "SUBST_CLASSES+=\t\tid",
 820 "SUBST_SED.id=\t\t-e /deleteme/d",
 821 "SUBST_FILES.id=\t\tfile",
 822 "SUBST_MESSAGE.id=\tMessage",
 823 "SUBST_STAGE.id=\t\tpre-configure")
 824
 825 mklines.Check()
 826
 827 t.CheckOutputEmpty()
 828}
 829
485// simulateSubstLines only tests some of the inner workings of SubstContext. 830// simulateSubstLines only tests some of the inner workings of SubstContext.
486// It is not realistic for all cases. If in doubt, use MkLines.Check. 831// It is not realistic for all cases. If in doubt, use MkLines.Check.
487func simulateSubstLines(t *Tester, texts ...string) { 832func simulateSubstLines(t *Tester, texts ...string) {
488 ctx := NewSubstContext() 833 ctx := NewSubstContext()
 834 lineno := 0
489 for _, lineText := range texts { 835 for _, lineText := range texts {
490 var lineno int 836 var curr int
491 _, err := fmt.Sscanf(lineText[0:4], "%d: ", &lineno) 837 _, err := fmt.Sscanf(lineText[0:4], "%d: ", &curr)
492 G.AssertNil(err, "") 838 G.AssertNil(err, "")
 839
 840 if lineno != 0 {
 841 t.Check(curr, equals, lineno)
 842 }
 843
493 text := lineText[4:] 844 text := lineText[4:]
494 line := newSubstLine(t, lineno, text) 845 line := t.NewMkLine("Makefile", curr, text)
495 846
496 switch { 847 switch {
497 case text == "": 848 case text == "":
498 ctx.Finish(line) 849 ctx.Finish(line)
499 case hasPrefix(text, "."): 850 case hasPrefix(text, "."):
500 ctx.Directive(line) 851 ctx.Directive(line)
501 default: 852 default:
502 ctx.Varassign(line) 853 ctx.Varassign(line)
503 } 854 }
 855
 856 lineno = curr + 1
504 } 857 }
505} 
506 858
507func newSubstLine(t *Tester, lineno int, text string) MkLine { 859 ctx.Finish(t.NewMkLine("Makefile", lineno, ""))
508 return t.NewMkLine("Makefile", lineno, text) 
509} 860}

cvs diff -r1.14 -r1.15 pkgsrc/pkgtools/pkglint/files/Attic/tools.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/tools.go 2019/05/22 16:07:16 1.14
+++ pkgsrc/pkgtools/pkglint/files/Attic/tools.go 2019/05/26 14:05:57 1.15
@@ -282,27 +282,27 @@ func (tr *Tools) ParseToolLine(mklines M @@ -282,27 +282,27 @@ func (tr *Tools) ParseToolLine(mklines M
282 tr.SeenPrefs = true 282 tr.SeenPrefs = true
283 } 283 }
284 } 284 }
285} 285}
286 286
287func (tr *Tools) addAlias(tool *Tool, alias string) { 287func (tr *Tools) addAlias(tool *Tool, alias string) {
288 tool.Aliases = append(tool.Aliases, alias) 288 tool.Aliases = append(tool.Aliases, alias)
289 tr.AliasOf[alias] = tool.Name 289 tr.AliasOf[alias] = tool.Name
290} 290}
291 291
292// parseUseTools interprets a "USE_TOOLS+=" line from a Makefile fragment. 292// parseUseTools interprets a "USE_TOOLS+=" line from a Makefile fragment.
293// It determines the validity of the tool, i.e. in which places it may be used. 293// It determines the validity of the tool, i.e. in which places it may be used.
294// 294//
295// If createIfAbsent is true and the tools is unknown, it is registered. 295// If createIfAbsent is true and the tool is unknown, it is registered.
296// This can be done only in the pkgsrc infrastructure files, where the 296// This can be done only in the pkgsrc infrastructure files, where the
297// actual definition is assumed to be in some other file. In packages 297// actual definition is assumed to be in some other file. In packages
298// though, this assumption cannot be made and pkglint needs to be strict. 298// though, this assumption cannot be made and pkglint needs to be strict.
299func (tr *Tools) parseUseTools(mkline MkLine, createIfAbsent bool, addToUseTools bool) { 299func (tr *Tools) parseUseTools(mkline MkLine, createIfAbsent bool, addToUseTools bool) {
300 value := mkline.Value() 300 value := mkline.Value()
301 if containsVarRef(value) { 301 if containsVarRef(value) {
302 return 302 return
303 } 303 }
304 304
305 validity := tr.validity(mkline.Basename, addToUseTools) 305 validity := tr.validity(mkline.Basename, addToUseTools)
306 for _, dep := range mkline.ValueFields(value) { 306 for _, dep := range mkline.ValueFields(value) {
307 name := strings.Split(dep, ":")[0] 307 name := strings.Split(dep, ":")[0]
308 if createIfAbsent || tr.ByName(name) != nil { 308 if createIfAbsent || tr.ByName(name) != nil {

cvs diff -r1.14 -r1.15 pkgsrc/pkgtools/pkglint/files/Attic/vardefs_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/vardefs_test.go 2019/05/02 08:36:10 1.14
+++ pkgsrc/pkgtools/pkglint/files/Attic/vardefs_test.go 2019/05/26 14:05:57 1.15
@@ -133,31 +133,46 @@ func (s *Suite) Test_VarTypeRegistry_par @@ -133,31 +133,46 @@ func (s *Suite) Test_VarTypeRegistry_par
133 "Pkglint internal error: At least one ACL entry must be given.") 133 "Pkglint internal error: At least one ACL entry must be given.")
134} 134}
135 135
136func (s *Suite) Test_VarTypeRegistry_Init__LP64PLATFORMS(c *check.C) { 136func (s *Suite) Test_VarTypeRegistry_Init__LP64PLATFORMS(c *check.C) {
137 t := s.Init(c) 137 t := s.Init(c)
138 138
139 pkg := t.SetUpPackage("category/package", 139 pkg := t.SetUpPackage("category/package",
140 "BROKEN_ON_PLATFORM=\t${LP64PLATFORMS}") 140 "BROKEN_ON_PLATFORM=\t${LP64PLATFORMS}")
141 t.FinishSetUp() 141 t.FinishSetUp()
142 142
143 G.Check(pkg) 143 G.Check(pkg)
144 144
145 // No warning about a missing :Q operator. 145 // No warning about a missing :Q operator.
146 // All PLATFORM variables must be either lkNone or lkSpace. 
147 t.CheckOutputEmpty() 146 t.CheckOutputEmpty()
148} 147}
149 148
150func (s *Suite) Test_VarTypeRegistry_Init__no_tracing(c *check.C) { 149func (s *Suite) Test_VarTypeRegistry_Init__no_tracing(c *check.C) {
151 t := s.Init(c) 150 t := s.Init(c)
152 151
153 t.CreateFileLines("editors/emacs/modules.mk", 152 t.CreateFileLines("editors/emacs/modules.mk",
154 MkRcsID, 153 MkRcsID,
155 "", 154 "",
156 "_EMACS_VERSIONS_ALL= emacs31", 155 "_EMACS_VERSIONS_ALL= emacs31",
157 "_EMACS_VERSIONS_ALL+= emacs29") 156 "_EMACS_VERSIONS_ALL+= emacs29")
158 t.DisableTracing() 157 t.DisableTracing()
159 158
160 t.SetUpVartypes() // Just for code coverage. 159 t.SetUpVartypes() // Just for code coverage.
161 160
162 t.CheckOutputEmpty() 161 t.CheckOutputEmpty()
163} 162}
 163
 164func (s *Suite) Test_VarTypeRegistry_Init__MASTER_SITES(c *check.C) {
 165 t := s.Init(c)
 166
 167 t.CreateFileLines("mk/fetch/sites.mk",
 168 MkRcsID,
 169 "",
 170 "MASTER_SITE_GITHUB=\thttps://github.com/",
 171 "",
 172 "OTHER=\tvalue") // For branch coverage of hasPrefix.*MASTER_SITE_
 173
 174 t.SetUpVartypes()
 175
 176 vartype := G.Pkgsrc.VariableType(nil, "MASTER_SITE_GITHUB")
 177 t.Check(vartype.String(), equals, "FetchURL (list, system-provided)")
 178}

cvs diff -r1.44 -r1.45 pkgsrc/pkgtools/pkglint/files/Attic/util.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/util.go 2019/05/06 20:27:17 1.44
+++ pkgsrc/pkgtools/pkglint/files/Attic/util.go 2019/05/26 14:05:57 1.45
@@ -255,32 +255,41 @@ func tabWidth(s string) int { @@ -255,32 +255,41 @@ func tabWidth(s string) int {
255 } else { 255 } else {
256 length++ 256 length++
257 } 257 }
258 } 258 }
259 return length 259 return length
260} 260}
261 261
262func detab(s string) string { 262func detab(s string) string {
263 var detabbed strings.Builder 263 var detabbed strings.Builder
264 for _, r := range s { 264 for _, r := range s {
265 if r == '\t' { 265 if r == '\t' {
266 detabbed.WriteString(" "[:8-detabbed.Len()%8]) 266 detabbed.WriteString(" "[:8-detabbed.Len()%8])
267 } else { 267 } else {
268 detabbed.WriteString(string(r)) 268 detabbed.WriteRune(r)
269 } 269 }
270 } 270 }
271 return detabbed.String() 271 return detabbed.String()
272} 272}
273 273
 274// alignWith extends str with as many tabs as needed to reach
 275// the same screen width as the other string.
 276func alignWith(str, other string) string {
 277 alignBefore := (tabWidth(other) + 7) & -8
 278 alignAfter := tabWidth(str) & -8
 279 tabsNeeded := imax((alignBefore-alignAfter)/8, 1)
 280 return str + strings.Repeat("\t", tabsNeeded)
 281}
 282
274func shorten(s string, maxChars int) string { 283func shorten(s string, maxChars int) string {
275 codePoints := 0 284 codePoints := 0
276 for i := range s { 285 for i := range s {
277 if codePoints >= maxChars { 286 if codePoints >= maxChars {
278 return s[:i] + "..." 287 return s[:i] + "..."
279 } 288 }
280 codePoints++ 289 codePoints++
281 } 290 }
282 return s 291 return s
283} 292}
284 293
285func varnameBase(varname string) string { 294func varnameBase(varname string) string {
286 dot := strings.IndexByte(varname, '.') 295 dot := strings.IndexByte(varname, '.')
@@ -358,27 +367,27 @@ func relpath(from, to string) (result st @@ -358,27 +367,27 @@ func relpath(from, to string) (result st
358 367
359 if trace.Tracing { 368 if trace.Tracing {
360 defer trace.Call(from, to, trace.Result(&result))() 369 defer trace.Call(from, to, trace.Result(&result))()
361 } 370 }
362 371
363 cfrom := cleanpath(from) 372 cfrom := cleanpath(from)
364 cto := cleanpath(to) 373 cto := cleanpath(to)
365 374
366 if cfrom == cto { 375 if cfrom == cto {
367 return "." 376 return "."
368 } 377 }
369 378
370 // Take a shortcut for the common case from "dir" to "dir/subdir/...". 379 // Take a shortcut for the common case from "dir" to "dir/subdir/...".
371 if hasPrefix(cto, cfrom) && len(cto) > len(cfrom)+1 && cto[len(cfrom)] == '/' { 380 if hasPrefix(cto, cfrom) && hasPrefix(cto[len(cfrom):], "/") {
372 return cleanpath(cto[len(cfrom)+1:]) 381 return cleanpath(cto[len(cfrom)+1:])
373 } 382 }
374 383
375 // Take a shortcut for the common case from "category/package" to ".". 384 // Take a shortcut for the common case from "category/package" to ".".
376 // This is the most common variant in a complete pkgsrc scan. 385 // This is the most common variant in a complete pkgsrc scan.
377 if cto == "." { 386 if cto == "." {
378 fromParts := strings.FieldsFunc(cfrom, func(r rune) bool { return r == '/' }) 387 fromParts := strings.FieldsFunc(cfrom, func(r rune) bool { return r == '/' })
379 if len(fromParts) == 2 && !hasPrefix(fromParts[0], ".") && !hasPrefix(fromParts[1], ".") { 388 if len(fromParts) == 2 && !hasPrefix(fromParts[0], ".") && !hasPrefix(fromParts[1], ".") {
380 return "../.." 389 return "../.."
381 } 390 }
382 } 391 }
383 392
384 if cfrom == "." && !filepath.IsAbs(cto) { 393 if cfrom == "." && !filepath.IsAbs(cto) {
@@ -673,33 +682,33 @@ func (s *Scope) LastDefinition(varname s @@ -673,33 +682,33 @@ func (s *Scope) LastDefinition(varname s
673// Commented returns whether the variable has only been defined in commented 682// Commented returns whether the variable has only been defined in commented
674// variable assignments. These are ignored by bmake but used heavily in 683// variable assignments. These are ignored by bmake but used heavily in
675// mk/defaults/mk.conf for documentation. 684// mk/defaults/mk.conf for documentation.
676func (s *Scope) Commented(varname string) MkLine { 685func (s *Scope) Commented(varname string) MkLine {
677 var mklines []MkLine 686 var mklines []MkLine
678 if first := s.firstDef[varname]; first != nil { 687 if first := s.firstDef[varname]; first != nil {
679 mklines = append(mklines, first) 688 mklines = append(mklines, first)
680 } 689 }
681 if last := s.lastDef[varname]; last != nil { 690 if last := s.lastDef[varname]; last != nil {
682 mklines = append(mklines, last) 691 mklines = append(mklines, last)
683 } 692 }
684 693
685 for _, mkline := range mklines { 694 for _, mkline := range mklines {
686 if mkline != nil && mkline.IsVarassign() { 695 if mkline.IsVarassign() {
687 return nil 696 return nil
688 } 697 }
689 } 698 }
690 699
691 for _, mkline := range mklines { 700 for _, mkline := range mklines {
692 if mkline != nil && mkline.IsCommentedVarassign() { 701 if mkline.IsCommentedVarassign() {
693 return mkline 702 return mkline
694 } 703 }
695 } 704 }
696 705
697 return nil 706 return nil
698} 707}
699 708
700func (s *Scope) FirstUse(varname string) MkLine { 709func (s *Scope) FirstUse(varname string) MkLine {
701 return s.used[varname] 710 return s.used[varname]
702} 711}
703 712
704// LastValue returns the value from the last variable definition. 713// LastValue returns the value from the last variable definition.
705// 714//
@@ -868,27 +877,29 @@ func (c *FileCache) Put(filename string, @@ -868,27 +877,29 @@ func (c *FileCache) Put(filename string,
868 877
869 entry = new(fileCacheEntry) 878 entry = new(fileCacheEntry)
870 c.table = append(c.table, entry) 879 c.table = append(c.table, entry)
871 c.mapping[key] = entry 880 c.mapping[key] = entry
872 } 881 }
873 882
874 entry.count = 1 883 entry.count = 1
875 entry.key = key 884 entry.key = key
876 entry.options = options 885 entry.options = options
877 entry.lines = lines 886 entry.lines = lines
878} 887}
879 888
880func (c *FileCache) removeOldEntries() { 889func (c *FileCache) removeOldEntries() {
881 sort.Slice(c.table, func(i, j int) bool { return c.table[j].count < c.table[i].count }) 890 sort.Slice(c.table, func(i, j int) bool {
 891 return c.table[j].count < c.table[i].count
 892 })
882 893
883 if G.Testing { 894 if G.Testing {
884 for _, e := range c.table { 895 for _, e := range c.table {
885 if trace.Tracing { 896 if trace.Tracing {
886 trace.Stepf("FileCache %q with count %d.", e.key, e.count) 897 trace.Stepf("FileCache %q with count %d.", e.key, e.count)
887 } 898 }
888 } 899 }
889 } 900 }
890 901
891 minCount := c.table[len(c.table)-1].count 902 minCount := c.table[len(c.table)-1].count
892 newLen := len(c.table) 903 newLen := len(c.table)
893 for newLen > 0 && c.table[newLen-1].count == minCount { 904 for newLen > 0 && c.table[newLen-1].count == minCount {
894 e := c.table[newLen-1] 905 e := c.table[newLen-1]

cvs diff -r1.28 -r1.29 pkgsrc/pkgtools/pkglint/files/Attic/util_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/util_test.go 2019/04/27 19:33:57 1.28
+++ pkgsrc/pkgtools/pkglint/files/Attic/util_test.go 2019/05/26 14:05:57 1.29
@@ -112,35 +112,39 @@ func (s *Suite) Test_cleanpath(c *check. @@ -112,35 +112,39 @@ func (s *Suite) Test_cleanpath(c *check.
112} 112}
113 113
114func (s *Suite) Test_relpath(c *check.C) { 114func (s *Suite) Test_relpath(c *check.C) {
115 t := s.Init(c) 115 t := s.Init(c)
116 116
117 t.Chdir(".") 117 t.Chdir(".")
118 t.Check(G.Pkgsrc.topdir, equals, t.tmpdir) 118 t.Check(G.Pkgsrc.topdir, equals, t.tmpdir)
119 119
120 test := func(from, to, result string) { 120 test := func(from, to, result string) {
121 c.Check(relpath(from, to), equals, result) 121 c.Check(relpath(from, to), equals, result)
122 } 122 }
123 123
124 test("some/dir", "some/directory", "../../some/directory") 124 test("some/dir", "some/directory", "../../some/directory")
 125 test("some/directory", "some/dir", "../../some/dir")
125 126
126 test("category/package/.", ".", "../..") 127 test("category/package/.", ".", "../..")
127 128
128 // This case is handled by one of the shortcuts that avoid file system access. 129 // This case is handled by one of the shortcuts that avoid file system access.
129 test( 130 test(
130 "./.", 131 "./.",
131 "x11/frameworkintegration/../../meta-pkgs/kde/kf5.mk", 132 "x11/frameworkintegration/../../meta-pkgs/kde/kf5.mk",
132 "meta-pkgs/kde/kf5.mk") 133 "meta-pkgs/kde/kf5.mk")
133 134
 135 test(".hidden/dir", ".", "../..")
 136 test("dir/.hidden", ".", "../..")
 137
134 // This happens when "pkglint -r x11" is run. 138 // This happens when "pkglint -r x11" is run.
135 G.Pkgsrc.topdir = "x11/.." 139 G.Pkgsrc.topdir = "x11/.."
136 140
137 test( 141 test(
138 "./.", 142 "./.",
139 "x11/frameworkintegration/../../meta-pkgs/kde/kf5.mk", 143 "x11/frameworkintegration/../../meta-pkgs/kde/kf5.mk",
140 "meta-pkgs/kde/kf5.mk") 144 "meta-pkgs/kde/kf5.mk")
141 test( 145 test(
142 "x11/..", 146 "x11/..",
143 "x11/frameworkintegration/../../meta-pkgs/kde/kf5.mk", 147 "x11/frameworkintegration/../../meta-pkgs/kde/kf5.mk",
144 "meta-pkgs/kde/kf5.mk") 148 "meta-pkgs/kde/kf5.mk")
145} 149}
146 150
@@ -228,26 +232,45 @@ func (s *Suite) Test_getSubdirs(c *check @@ -228,26 +232,45 @@ func (s *Suite) Test_getSubdirs(c *check
228 c.Check(os.Remove(t.File("empty/file")), check.IsNil) 232 c.Check(os.Remove(t.File("empty/file")), check.IsNil)
229 233
230 c.Check(getSubdirs(t.File(".")), deepEquals, []string{"subdir"}) 234 c.Check(getSubdirs(t.File(".")), deepEquals, []string{"subdir"})
231} 235}
232 236
233func (s *Suite) Test_detab(c *check.C) { 237func (s *Suite) Test_detab(c *check.C) {
234 c.Check(detab(""), equals, "") 238 c.Check(detab(""), equals, "")
235 c.Check(detab("\t"), equals, " ") 239 c.Check(detab("\t"), equals, " ")
236 c.Check(detab("1234\t9"), equals, "1234 9") 240 c.Check(detab("1234\t9"), equals, "1234 9")
237 c.Check(detab("1234567\t"), equals, "1234567 ") 241 c.Check(detab("1234567\t"), equals, "1234567 ")
238 c.Check(detab("12345678\t"), equals, "12345678 ") 242 c.Check(detab("12345678\t"), equals, "12345678 ")
239} 243}
240 244
 245func (s *Suite) Test_alignWith(c *check.C) {
 246 t := s.Init(c)
 247
 248 test := func(str, other, expected string) {
 249 t.Check(alignWith(str, other), equals, expected)
 250 }
 251
 252 // At least one tab is _always_ added.
 253 test("", "", "\t")
 254
 255 test("VAR=", "1234567", "VAR=\t")
 256 test("VAR=", "12345678", "VAR=\t")
 257 test("VAR=", "123456789", "VAR=\t\t")
 258
 259 // At least one tab is added in any case,
 260 // even if the other string is shorter.
 261 test("1234567890=", "V=", "1234567890=\t")
 262}
 263
241const reMkIncludeBenchmark = `^\.([\t ]*)(s?include)[\t ]+\"([^\"]+)\"[\t ]*(?:#.*)?$` 264const reMkIncludeBenchmark = `^\.([\t ]*)(s?include)[\t ]+\"([^\"]+)\"[\t ]*(?:#.*)?$`
242const reMkIncludeBenchmarkPositive = `^\.([\t ]*)(s?include)[\t ]+\"(.+)\"[\t ]*(?:#.*)?$` 265const reMkIncludeBenchmarkPositive = `^\.([\t ]*)(s?include)[\t ]+\"(.+)\"[\t ]*(?:#.*)?$`
243 266
244func Benchmark_match3_buildlink3(b *testing.B) { 267func Benchmark_match3_buildlink3(b *testing.B) {
245 for i := 0; i < b.N; i++ { 268 for i := 0; i < b.N; i++ {
246 match3(".include \"../../category/package/buildlink3.mk\"", reMkIncludeBenchmark) 269 match3(".include \"../../category/package/buildlink3.mk\"", reMkIncludeBenchmark)
247 } 270 }
248} 271}
249 272
250func Benchmark_match3_bsd_pkg_mk(b *testing.B) { 273func Benchmark_match3_bsd_pkg_mk(b *testing.B) {
251 for i := 0; i < b.N; i++ { 274 for i := 0; i < b.N; i++ {
252 match3(".include \"../../mk/bsd.pkg.mk\"", reMkIncludeBenchmark) 275 match3(".include \"../../mk/bsd.pkg.mk\"", reMkIncludeBenchmark)
253 } 276 }
@@ -301,26 +324,57 @@ func emptyToNil(slice []string) []string @@ -301,26 +324,57 @@ func emptyToNil(slice []string) []string
301 } 324 }
302 return slice 325 return slice
303} 326}
304 327
305func (s *Suite) Test_trimHspace(c *check.C) { 328func (s *Suite) Test_trimHspace(c *check.C) {
306 t := s.Init(c) 329 t := s.Init(c)
307 330
308 t.Check(trimHspace("a b"), equals, "a b") 331 t.Check(trimHspace("a b"), equals, "a b")
309 t.Check(trimHspace(" a b "), equals, "a b") 332 t.Check(trimHspace(" a b "), equals, "a b")
310 t.Check(trimHspace("\ta b\t"), equals, "a b") 333 t.Check(trimHspace("\ta b\t"), equals, "a b")
311 t.Check(trimHspace(" \t a b\t \t"), equals, "a b") 334 t.Check(trimHspace(" \t a b\t \t"), equals, "a b")
312} 335}
313 336
 337func (s *Suite) Test_trimCommon(c *check.C) {
 338 t := s.Init(c)
 339
 340 test := func(a, b, trimmedA, trimmedB string) {
 341 ta, tb := trimCommon(a, b)
 342 t.Check(ta, equals, trimmedA)
 343 t.Check(tb, equals, trimmedB)
 344 }
 345
 346 test("", "",
 347 "", "")
 348
 349 test("equal", "equal",
 350 "", "")
 351
 352 test("prefixA", "prefixB",
 353 "A", "B")
 354
 355 test("ASuffix", "BSuffix",
 356 "A", "B")
 357
 358 test("PreMiddlePost", "PreCenterPost",
 359 "Middle", "Center")
 360
 361 test("", "b",
 362 "", "b")
 363
 364 test("a", "",
 365 "a", "")
 366}
 367
314func (s *Suite) Test_isLocallyModified(c *check.C) { 368func (s *Suite) Test_isLocallyModified(c *check.C) {
315 t := s.Init(c) 369 t := s.Init(c)
316 370
317 unmodified := t.CreateFileLines("unmodified") 371 unmodified := t.CreateFileLines("unmodified")
318 modTime := time.Unix(1136239445, 0).UTC() 372 modTime := time.Unix(1136239445, 0).UTC()
319 373
320 err := os.Chtimes(unmodified, modTime, modTime) 374 err := os.Chtimes(unmodified, modTime, modTime)
321 c.Check(err, check.IsNil) 375 c.Check(err, check.IsNil)
322 376
323 st, err := os.Lstat(unmodified) 377 st, err := os.Lstat(unmodified)
324 c.Check(err, check.IsNil) 378 c.Check(err, check.IsNil)
325 379
326 // Make sure that the file system has second precision and accuracy. 380 // Make sure that the file system has second precision and accuracy.
@@ -618,26 +672,48 @@ func (s *Suite) Test_FileCache(c *check. @@ -618,26 +672,48 @@ func (s *Suite) Test_FileCache(c *check.
618 c.Check(cache.mapping, check.HasLen, 1) 672 c.Check(cache.mapping, check.HasLen, 1)
619 c.Check(cache.hits, equals, 7) 673 c.Check(cache.hits, equals, 7)
620 c.Check(cache.misses, equals, 5) 674 c.Check(cache.misses, equals, 5)
621 675
622 t.CheckOutputLines( 676 t.CheckOutputLines(
623 "TRACE: FileCache \"Makefile\" with count 4.", 677 "TRACE: FileCache \"Makefile\" with count 4.",
624 "TRACE: FileCache \"file1.mk\" with count 2.", 678 "TRACE: FileCache \"file1.mk\" with count 2.",
625 "TRACE: FileCache \"file2.mk\" with count 2.", 679 "TRACE: FileCache \"file2.mk\" with count 2.",
626 "TRACE: FileCache.Evict \"file2.mk\" with count 2.", 680 "TRACE: FileCache.Evict \"file2.mk\" with count 2.",
627 "TRACE: FileCache.Evict \"file1.mk\" with count 2.", 681 "TRACE: FileCache.Evict \"file1.mk\" with count 2.",
628 "TRACE: FileCache.Halve \"Makefile\" with count 4.") 682 "TRACE: FileCache.Halve \"Makefile\" with count 4.")
629} 683}
630 684
 685func (s *Suite) Test_FileCache_removeOldEntries__branch_coverage(c *check.C) {
 686 t := s.Init(c)
 687
 688 t.EnableTracingToLog()
 689 G.Testing = false
 690
 691 lines := t.NewLines("filename.mk",
 692 MkRcsID)
 693 cache := NewFileCache(3)
 694 cache.Put("filename1.mk", 0, lines)
 695 cache.Put("filename2.mk", 0, lines)
 696 cache.Get("filename2.mk", 0)
 697 cache.Get("filename2.mk", 0)
 698 cache.Put("filename3.mk", 0, lines)
 699 cache.Put("filename4.mk", 0, lines)
 700
 701 t.CheckOutputLines(
 702 "TRACE: FileCache.Evict \"filename3.mk\" with count 1.",
 703 "TRACE: FileCache.Evict \"filename1.mk\" with count 1.",
 704 "TRACE: FileCache.Halve \"filename2.mk\" with count 3.")
 705}
 706
631func (s *Suite) Test_makeHelp(c *check.C) { 707func (s *Suite) Test_makeHelp(c *check.C) {
632 c.Check(makeHelp("subst"), equals, confMake+" help topic=subst") 708 c.Check(makeHelp("subst"), equals, confMake+" help topic=subst")
633} 709}
634 710
635func (s *Suite) Test_hasAlnumPrefix(c *check.C) { 711func (s *Suite) Test_hasAlnumPrefix(c *check.C) {
636 t := s.Init(c) 712 t := s.Init(c)
637 713
638 t.Check(hasAlnumPrefix(""), equals, false) 714 t.Check(hasAlnumPrefix(""), equals, false)
639 t.Check(hasAlnumPrefix("A"), equals, true) 715 t.Check(hasAlnumPrefix("A"), equals, true)
640 t.Check(hasAlnumPrefix(","), equals, false) 716 t.Check(hasAlnumPrefix(","), equals, false)
641} 717}
642 718
643func (s *Suite) Test_Once(c *check.C) { 719func (s *Suite) Test_Once(c *check.C) {

cvs diff -r1.2 -r1.3 pkgsrc/pkgtools/pkglint/files/Attic/var_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/var_test.go 2019/03/10 19:01:50 1.2
+++ pkgsrc/pkgtools/pkglint/files/Attic/var_test.go 2019/05/26 14:05:57 1.3
@@ -202,26 +202,49 @@ func (s *Suite) Test_Var_Value__initial_ @@ -202,26 +202,49 @@ func (s *Suite) Test_Var_Value__initial_
202 202
203 v := NewVar("VARNAME") 203 v := NewVar("VARNAME")
204 204
205 v.Write(t.NewMkLine("write.mk", 124, "VARNAME:=\toverwritten conditionally"), true, "OPSYS") 205 v.Write(t.NewMkLine("write.mk", 124, "VARNAME:=\toverwritten conditionally"), true, "OPSYS")
206 206
207 // Since there is no previous value, the simplest choice is to just 207 // Since there is no previous value, the simplest choice is to just
208 // take the first seen value, no matter if that value is conditional 208 // take the first seen value, no matter if that value is conditional
209 // or not. 209 // or not.
210 t.Check(v.Conditional(), equals, true) 210 t.Check(v.Conditional(), equals, true)
211 t.Check(v.Constant(), equals, false) 211 t.Check(v.Constant(), equals, false)
212 t.Check(v.Value(), equals, "overwritten conditionally") 212 t.Check(v.Value(), equals, "overwritten conditionally")
213} 213}
214 214
 215func (s *Suite) Test_Var_Write__conditional_without_variables(c *check.C) {
 216 t := s.Init(c)
 217
 218 mklines := t.NewMkLines("filename.mk",
 219 MkRcsID,
 220 ".if exists(/usr/bin)",
 221 "VAR=\tvalue",
 222 ".endif")
 223
 224 scope := NewRedundantScope()
 225 mklines.ForEach(func(mkline MkLine) {
 226 if mkline.IsVarassign() {
 227 t.Check(scope.get("VAR").vari.Conditional(), equals, false)
 228 }
 229
 230 scope.checkLine(mklines, mkline)
 231
 232 if mkline.IsVarassign() {
 233 t.Check(scope.get("VAR").vari.Conditional(), equals, true)
 234 }
 235 })
 236}
 237
215func (s *Suite) Test_Var_Value__conditional_write_after_unconditional(c *check.C) { 238func (s *Suite) Test_Var_Value__conditional_write_after_unconditional(c *check.C) {
216 t := s.Init(c) 239 t := s.Init(c)
217 240
218 v := NewVar("VARNAME") 241 v := NewVar("VARNAME")
219 242
220 v.Write(t.NewMkLine("write.mk", 123, "VARNAME=\tvalue"), false) 243 v.Write(t.NewMkLine("write.mk", 123, "VARNAME=\tvalue"), false)
221 244
222 t.Check(v.Value(), equals, "value") 245 t.Check(v.Value(), equals, "value")
223 246
224 v.Write(t.NewMkLine("write.mk", 124, "VARNAME+=\tappended"), false) 247 v.Write(t.NewMkLine("write.mk", 124, "VARNAME+=\tappended"), false)
225 248
226 t.Check(v.Value(), equals, "value appended") 249 t.Check(v.Value(), equals, "value appended")
227 250

cvs diff -r1.65 -r1.66 pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go 2019/05/21 17:59:48 1.65
+++ pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go 2019/05/26 14:05:57 1.66
@@ -50,33 +50,33 @@ func (reg *VarTypeRegistry) DefinedExact @@ -50,33 +50,33 @@ func (reg *VarTypeRegistry) DefinedExact
50 50
51func (reg *VarTypeRegistry) DefinedCanon(varname string) bool { 51func (reg *VarTypeRegistry) DefinedCanon(varname string) bool {
52 return reg.Canon(varname) != nil 52 return reg.Canon(varname) != nil
53} 53}
54 54
55func (reg *VarTypeRegistry) DefineType(varcanon string, vartype *Vartype) { 55func (reg *VarTypeRegistry) DefineType(varcanon string, vartype *Vartype) {
56 reg.types[varcanon] = vartype 56 reg.types[varcanon] = vartype
57} 57}
58 58
59func (reg *VarTypeRegistry) Define(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...ACLEntry) { 59func (reg *VarTypeRegistry) Define(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...ACLEntry) {
60 m, varbase, varparam := match2(varname, `^([A-Z_.][A-Z0-9_]*|@)(|\*|\.\*)$`) 60 m, varbase, varparam := match2(varname, `^([A-Z_.][A-Z0-9_]*|@)(|\*|\.\*)$`)
61 G.Assertf(m, "invalid variable name") 61 G.Assertf(m, "invalid variable name")
62 62
63 vartype := Vartype{basicType, options, aclEntries} 63 vartype := NewVartype(basicType, options, aclEntries...)
64 64
65 if varparam == "" || varparam == "*" { 65 if varparam == "" || varparam == "*" {
66 reg.types[varbase] = &vartype 66 reg.types[varbase] = vartype
67 } 67 }
68 if varparam == "*" || varparam == ".*" { 68 if varparam == "*" || varparam == ".*" {
69 reg.types[varbase+".*"] = &vartype 69 reg.types[varbase+".*"] = vartype
70 } 70 }
71} 71}
72 72
73// DefineParse defines a variable with the given type and permissions. 73// DefineParse defines a variable with the given type and permissions.
74// 74//
75// A permission entry looks like this: 75// A permission entry looks like this:
76// "Makefile, Makefile.*, *.mk: default, set, append, use, use-loadtime" 76// "Makefile, Makefile.*, *.mk: default, set, append, use, use-loadtime"
77// Only certain filenames are allowed in the part before the colon, 77// Only certain filenames are allowed in the part before the colon,
78// to prevent typos. To use arbitrary filenames, prefix them with 78// to prevent typos. To use arbitrary filenames, prefix them with
79// "special:". 79// "special:".
80// 80//
81// TODO: To be implemented: when prefixed with "infra:", the entry only 81// TODO: To be implemented: when prefixed with "infra:", the entry only
82// applies to files within the pkgsrc infrastructure. Without this prefix, 82// applies to files within the pkgsrc infrastructure. Without this prefix,
@@ -1719,27 +1719,27 @@ func (reg *VarTypeRegistry) parseACLEntr @@ -1719,27 +1719,27 @@ func (reg *VarTypeRegistry) parseACLEntr
1719 default: 1719 default:
1720 withoutSpecial := strings.TrimPrefix(glob, "special:") 1720 withoutSpecial := strings.TrimPrefix(glob, "special:")
1721 if withoutSpecial == glob { 1721 if withoutSpecial == glob {
1722 G.Assertf(false, "Invalid ACL glob %q for %q.", glob, varname) 1722 G.Assertf(false, "Invalid ACL glob %q for %q.", glob, varname)
1723 } else { 1723 } else {
1724 glob = withoutSpecial 1724 glob = withoutSpecial
1725 } 1725 }
1726 } 1726 }
1727 for _, prev := range result { 1727 for _, prev := range result {
1728 matched, err := path.Match(prev.glob, glob) 1728 matched, err := path.Match(prev.glob, glob)
1729 G.AssertNil(err, "Invalid ACL pattern %q for %q", glob, varname) 1729 G.AssertNil(err, "Invalid ACL pattern %q for %q", glob, varname)
1730 G.Assertf(!matched, "Unreachable ACL pattern %q for %q.", glob, varname) 1730 G.Assertf(!matched, "Unreachable ACL pattern %q for %q.", glob, varname)
1731 } 1731 }
1732 result = append(result, ACLEntry{glob, permissions}) 1732 result = append(result, NewACLEntry(glob, permissions))
1733 } 1733 }
1734 } 1734 }
1735 1735
1736 return result 1736 return result
1737} 1737}
1738 1738
1739func (reg *VarTypeRegistry) parsePermissions(varname, globs, perms string) ACLPermissions { 1739func (reg *VarTypeRegistry) parsePermissions(varname, globs, perms string) ACLPermissions {
1740 if perms == "none" { 1740 if perms == "none" {
1741 return aclpNone 1741 return aclpNone
1742 } 1742 }
1743 1743
1744 splitPerms := strings.Split(perms, ", ") 1744 splitPerms := strings.Split(perms, ", ")
1745 var permissions ACLPermissions 1745 var permissions ACLPermissions

cvs diff -r1.31 -r1.32 pkgsrc/pkgtools/pkglint/files/Attic/vartype.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/vartype.go 2019/04/28 18:13:53 1.31
+++ pkgsrc/pkgtools/pkglint/files/Attic/vartype.go 2019/05/26 14:05:57 1.32
@@ -3,26 +3,35 @@ package pkglint @@ -3,26 +3,35 @@ package pkglint
3import ( 3import (
4 "path" 4 "path"
5 "strings" 5 "strings"
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.
10type Vartype struct { 10type Vartype struct {
11 basicType *BasicType 11 basicType *BasicType
12 options vartypeOptions 12 options vartypeOptions
13 aclEntries []ACLEntry 13 aclEntries []ACLEntry
14} 14}
15 15
 16func NewVartype(basicType *BasicType, options vartypeOptions, aclEntries ...ACLEntry) *Vartype {
 17 for _, aclEntry := range aclEntries {
 18 _, err := path.Match(aclEntry.glob, "")
 19 G.AssertNil(err, "path.Match")
 20 }
 21
 22 return &Vartype{basicType, options, aclEntries}
 23}
 24
16type vartypeOptions uint8 25type vartypeOptions uint8
17 26
18const ( 27const (
19 // List is a compound type, consisting of several space-separated elements. 28 // List is a compound type, consisting of several space-separated elements.
20 // Elements can have embedded spaces by enclosing them in double or single 29 // Elements can have embedded spaces by enclosing them in double or single
21 // quotes, like in the shell. 30 // quotes, like in the shell.
22 // 31 //
23 // These lists are used in the :M, :S modifiers, in .for loops, 32 // These lists are used in the :M, :S modifiers, in .for loops,
24 // and as lists of arbitrary things. 33 // and as lists of arbitrary things.
25 List vartypeOptions = 1 << iota 34 List vartypeOptions = 1 << iota
26 35
27 Guessed 36 Guessed
28 PackageSettable 37 PackageSettable
@@ -32,26 +41,30 @@ const ( @@ -32,26 +41,30 @@ const (
32 41
33 // NeedsRationale marks variables that should always contain a comment 42 // NeedsRationale marks variables that should always contain a comment
34 // describing why they are set. Typical examples are NOT_FOR_* variables. 43 // describing why they are set. Typical examples are NOT_FOR_* variables.
35 NeedsRationale 44 NeedsRationale
36 45
37 NoVartypeOptions = 0 46 NoVartypeOptions = 0
38) 47)
39 48
40type ACLEntry struct { 49type ACLEntry struct {
41 glob string // Examples: "Makefile", "*.mk" 50 glob string // Examples: "Makefile", "*.mk"
42 permissions ACLPermissions 51 permissions ACLPermissions
43} 52}
44 53
 54func NewACLEntry(glob string, permissions ACLPermissions) ACLEntry {
 55 return ACLEntry{glob, permissions}
 56}
 57
45type ACLPermissions uint8 58type ACLPermissions uint8
46 59
47const ( 60const (
48 aclpSet ACLPermissions = 1 << iota // VAR = value 61 aclpSet ACLPermissions = 1 << iota // VAR = value
49 aclpSetDefault // VAR ?= value 62 aclpSetDefault // VAR ?= value
50 aclpAppend // VAR += value 63 aclpAppend // VAR += value
51 aclpUseLoadtime // OTHER := ${VAR}, OTHER != ${VAR} 64 aclpUseLoadtime // OTHER := ${VAR}, OTHER != ${VAR}
52 aclpUse // OTHER = ${VAR} 65 aclpUse // OTHER = ${VAR}
53 66
54 aclpNone ACLPermissions = 0 67 aclpNone ACLPermissions = 0
55 68
56 aclpAllWrite = aclpSet | aclpSetDefault | aclpAppend 69 aclpAllWrite = aclpSet | aclpSetDefault | aclpAppend
57 aclpAllRead = aclpUseLoadtime | aclpUse 70 aclpAllRead = aclpUseLoadtime | aclpUse
@@ -109,36 +122,37 @@ func (vt *Vartype) EffectivePermissions( @@ -109,36 +122,37 @@ func (vt *Vartype) EffectivePermissions(
109func (vt *Vartype) Union() ACLPermissions { 122func (vt *Vartype) Union() ACLPermissions {
110 var permissions ACLPermissions 123 var permissions ACLPermissions
111 for _, aclEntry := range vt.aclEntries { 124 for _, aclEntry := range vt.aclEntries {
112 permissions |= aclEntry.permissions 125 permissions |= aclEntry.permissions
113 } 126 }
114 return permissions 127 return permissions
115} 128}
116 129
117// AlternativeFiles lists the file patterns in which all of the given 130// AlternativeFiles lists the file patterns in which all of the given
118// permissions are allowed, readily formatted to be used in a diagnostic. 131// permissions are allowed, readily formatted to be used in a diagnostic.
119// 132//
120// If the permission is allowed nowhere, an empty string is returned. 133// If the permission is allowed nowhere, an empty string is returned.
121func (vt *Vartype) AlternativeFiles(perms ACLPermissions) string { 134func (vt *Vartype) AlternativeFiles(perms ACLPermissions) string {
122 pos := make([]string, 0, len(vt.aclEntries)) 135 var pos []string
123 neg := make([]string, 0, len(vt.aclEntries)) 136 var neg []string
124 137
125 merge := func(slice []string) []string { 138 merge := func(slice []string) []string {
126 di := 0 139 di := 0
127 for si, early := range slice { 140 for si, early := range slice {
128 redundant := false 141 redundant := false
129 for _, late := range slice[si+1:] { 142 for _, late := range slice[si+1:] {
130 matched, err := path.Match(late, early) 143 matched, err := path.Match(late, early)
131 if err == nil && matched { 144 G.AssertNil(err, "path.Match")
 145 if matched {
132 redundant = true 146 redundant = true
133 break 147 break
134 } 148 }
135 } 149 }
136 if !redundant { 150 if !redundant {
137 slice[di] = early 151 slice[di] = early
138 di++ 152 di++
139 } 153 }
140 } 154 }
141 return slice[:di] 155 return slice[:di]
142 } 156 }
143 157
144 for _, aclEntry := range vt.aclEntries { 158 for _, aclEntry := range vt.aclEntries {

cvs diff -r1.18 -r1.19 pkgsrc/pkgtools/pkglint/files/Attic/vartype_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/vartype_test.go 2019/04/20 17:43:25 1.18
+++ pkgsrc/pkgtools/pkglint/files/Attic/vartype_test.go 2019/05/26 14:05:57 1.19
@@ -1,44 +1,44 @@ @@ -1,44 +1,44 @@
1package pkglint 1package pkglint
2 2
3import ( 3import (
4 "gopkg.in/check.v1" 4 "gopkg.in/check.v1"
5) 5)
6 6
7func (s *Suite) Test_Vartype_EffectivePermissions(c *check.C) { 7func (s *Suite) Test_Vartype_EffectivePermissions(c *check.C) {
8 t := s.Init(c) 8 t := s.Init(c)
9 9
10 t.SetUpVartypes() 10 t.SetUpVartypes()
11 11
12 if typ := G.Pkgsrc.vartypes.Canon("PREFIX"); c.Check(typ, check.NotNil) { 12 if typ := G.Pkgsrc.vartypes.Canon("PREFIX"); c.Check(typ, check.NotNil) {
13 c.Check(typ.basicType.name, equals, "Pathname") 13 c.Check(typ.basicType.name, equals, "Pathname")
14 c.Check(typ.aclEntries, check.DeepEquals, []ACLEntry{{"*", aclpUse}}) 14 c.Check(typ.aclEntries, deepEquals, []ACLEntry{NewACLEntry("*", aclpUse)})
15 c.Check(typ.EffectivePermissions("Makefile"), equals, aclpUse) 15 c.Check(typ.EffectivePermissions("Makefile"), equals, aclpUse)
16 c.Check(typ.EffectivePermissions("buildlink3.mk"), equals, aclpUse) 16 c.Check(typ.EffectivePermissions("buildlink3.mk"), equals, aclpUse)
17 } 17 }
18 18
19 if typ := G.Pkgsrc.vartypes.Canon("EXTRACT_OPTS"); c.Check(typ, check.NotNil) { 19 if typ := G.Pkgsrc.vartypes.Canon("EXTRACT_OPTS"); c.Check(typ, check.NotNil) {
20 c.Check(typ.basicType.name, equals, "ShellWord") 20 c.Check(typ.basicType.name, equals, "ShellWord")
21 c.Check(typ.EffectivePermissions("Makefile"), equals, aclpAllWrite|aclpUse) 21 c.Check(typ.EffectivePermissions("Makefile"), equals, aclpAllWrite|aclpUse)
22 c.Check(typ.EffectivePermissions("options.mk"), equals, aclpAllWrite|aclpUse) 22 c.Check(typ.EffectivePermissions("options.mk"), equals, aclpAllWrite|aclpUse)
23 } 23 }
24} 24}
25 25
26func (s *Suite) Test_Vartype_AlternativeFiles(c *check.C) { 26func (s *Suite) Test_Vartype_AlternativeFiles(c *check.C) {
27 27
28 // test generates the files description for the "set" permission. 28 // test generates the files description for the "set" permission.
29 test := func(rules []string, alternatives string) { 29 test := func(rules []string, alternatives string) {
30 aclEntries := (*VarTypeRegistry).parseACLEntries(nil, "", rules...) 30 aclEntries := (*VarTypeRegistry).parseACLEntries(nil, "", rules...)
31 vartype := Vartype{BtYesNo, NoVartypeOptions, aclEntries} 31 vartype := NewVartype(BtYesNo, NoVartypeOptions, aclEntries...)
32 32
33 alternativeFiles := vartype.AlternativeFiles(aclpSet) 33 alternativeFiles := vartype.AlternativeFiles(aclpSet)
34 34
35 c.Check(alternativeFiles, equals, alternatives) 35 c.Check(alternativeFiles, equals, alternatives)
36 } 36 }
37 37
38 // rules parses the given permission rules. 38 // rules parses the given permission rules.
39 // 39 //
40 // There is a built-in check that prevents repeated adjacent permissions. 40 // There is a built-in check that prevents repeated adjacent permissions.
41 // The "append" permission can be added alternatively to circumvent this 41 // The "append" permission can be added alternatively to circumvent this
42 // check, since that permission is effectively ignore by this test. 42 // check, since that permission is effectively ignore by this test.
43 rules := func(rules ...string) []string { return rules } 43 rules := func(rules ...string) []string { return rules }
44 44