| @@ -1,443 +1,381 @@ | | | @@ -1,443 +1,381 @@ |
1 | package pkglint | | 1 | package pkglint |
2 | | | 2 | |
3 | import "gopkg.in/check.v1" | | 3 | import "gopkg.in/check.v1" |
4 | | | 4 | |
5 | func (t *Tester) NewSubstAutofixTest(lines ...string) func(bool) { | | 5 | func (t *Tester) NewSubstAutofixTest(lines ...string) func(bool) { |
6 | return func(autofix bool) { | | 6 | return func(autofix bool) { |
7 | mklines := t.NewMkLines("filename.mk", lines...) | | 7 | mklines := t.NewMkLines("filename.mk", lines...) |
| | | 8 | mklines.collectRationale() |
8 | ctx := NewSubstContext() | | 9 | ctx := NewSubstContext() |
9 | | | 10 | |
10 | mklines.ForEach(ctx.Process) | | 11 | mklines.ForEach(ctx.Process) |
11 | ctx.Finish(mklines.EOFLine()) | | 12 | ctx.Finish(mklines.EOFLine()) |
12 | | | 13 | |
13 | mklines.SaveAutofixChanges() | | 14 | mklines.SaveAutofixChanges() |
14 | } | | 15 | } |
15 | } | | 16 | } |
16 | | | 17 | |
17 | func (t *Tester) RunSubst(lines ...string) { | | 18 | func (t *Tester) RunSubst(lines ...string) { |
18 | assert(lines[len(lines)-1] != "") | | 19 | assert(lines[len(lines)-1] != "") |
19 | | | 20 | |
20 | mklines := t.NewMkLines("filename.mk", lines...) | | 21 | mklines := t.NewMkLines("filename.mk", lines...) |
| | | 22 | mklines.collectRationale() |
21 | ctx := NewSubstContext() | | 23 | ctx := NewSubstContext() |
22 | | | 24 | |
23 | mklines.ForEach(ctx.Process) | | 25 | mklines.ForEach(ctx.Process) |
24 | ctx.Finish(mklines.EOFLine()) | | 26 | ctx.Finish(mklines.EOFLine()) |
25 | } | | 27 | } |
26 | | | 28 | |
27 | func (s *Suite) Test_SubstContext__OPSYSVARS(c *check.C) { | | | |
28 | t := s.Init(c) | | | |
29 | | | | |
30 | ctx := NewSubstContext() | | | |
31 | | | | |
32 | // SUBST_CLASSES is added to OPSYSVARS in mk/bsd.pkg.mk. | | | |
33 | ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES.SunOS+=prefix")) | | | |
34 | ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_CLASSES.NetBSD+=prefix")) | | | |
35 | ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_FILES.prefix=Makefile")) | | | |
36 | ctx.varassign(t.NewMkLine("filename.mk", 14, "SUBST_SED.prefix=s,@PREFIX@,${PREFIX},g")) | | | |
37 | ctx.varassign(t.NewMkLine("filename.mk", 15, "SUBST_STAGE.prefix=post-configure")) | | | |
38 | | | | |
39 | t.CheckEquals(ctx.isComplete(), true) | | | |
40 | | | | |
41 | ctx.Finish(t.NewMkLine("filename.mk", 15, "")) | | | |
42 | | | | |
43 | t.CheckOutputLines( | | | |
44 | "NOTE: filename.mk:14: The substitution command \"s,@PREFIX@,${PREFIX},g\" " + | | | |
45 | "can be replaced with \"SUBST_VARS.prefix= PREFIX\".") | | | |
46 | } | | | |
47 | | | | |
48 | func (s *Suite) Test_SubstContext__no_class(c *check.C) { | | | |
49 | t := s.Init(c) | | | |
50 | | | | |
51 | t.RunSubst( | | | |
52 | "UNRELATED=anything", | | | |
53 | "SUBST_FILES.repl+=Makefile.in", | | | |
54 | "SUBST_SED.repl+=-e s,from,to,g") | | | |
55 | | | | |
56 | t.CheckOutputLines( | | | |
57 | "WARN: filename.mk:2: Before defining SUBST_FILES.repl, " + | | | |
58 | "the SUBST class should be declared using \"SUBST_CLASSES+= repl\".") | | | |
59 | } | | | |
60 | | | | |
61 | func (s *Suite) Test_SubstContext__multiple_classes_in_one_line(c *check.C) { | | | |
62 | t := s.Init(c) | | | |
63 | | | | |
64 | t.RunSubst( | | | |
65 | "SUBST_CLASSES+= one two", | | | |
66 | "SUBST_STAGE.one= post-configure", | | | |
67 | "SUBST_FILES.one= one.txt", | | | |
68 | "SUBST_SED.one= s,one,1,g", | | | |
69 | "SUBST_STAGE.two= post-configure", | | | |
70 | "SUBST_FILES.two= two.txt") | | | |
71 | | | | |
72 | t.CheckOutputLines( | | | |
73 | "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", | | | |
74 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_SED.two, SUBST_VARS.two or SUBST_FILTER_CMD.two missing.") | | | |
75 | } | | | |
76 | | | | |
77 | func (s *Suite) Test_SubstContext__multiple_classes_in_one_line_multiple_blocks(c *check.C) { | | | |
78 | t := s.Init(c) | | | |
79 | | | | |
80 | t.RunSubst( | | | |
81 | "SUBST_CLASSES+= one two", | | | |
82 | "SUBST_STAGE.one= post-configure", | | | |
83 | "SUBST_FILES.one= one.txt", | | | |
84 | "SUBST_SED.one= s,one,1,g", | | | |
85 | "", | | | |
86 | "SUBST_STAGE.two= post-configure", | | | |
87 | "SUBST_FILES.two= two.txt", | | | |
88 | "", | | | |
89 | "SUBST_STAGE.three= post-configure", | | | |
90 | "", | | | |
91 | "SUBST_VARS.four= PREFIX", | | | |
92 | "", | | | |
93 | "SUBST_VARS.three= PREFIX") | | | |
94 | | | | |
95 | t.CheckOutputLines( | | | |
96 | "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", | | | |
97 | "WARN: filename.mk:8: Incomplete SUBST block: "+ | | | |
98 | "SUBST_SED.two, SUBST_VARS.two or SUBST_FILTER_CMD.two missing.", | | | |
99 | "WARN: filename.mk:9: Before defining SUBST_STAGE.three, "+ | | | |
100 | "the SUBST class should be declared using \"SUBST_CLASSES+= three\".", | | | |
101 | "WARN: filename.mk:11: Before defining SUBST_VARS.four, "+ | | | |
102 | "the SUBST class should be declared using \"SUBST_CLASSES+= four\".") | | | |
103 | } | | | |
104 | | | | |
105 | func (s *Suite) Test_SubstContext__multiple_classes_in_one_block(c *check.C) { | | | |
106 | t := s.Init(c) | | | |
107 | | | | |
108 | t.RunSubst( | | | |
109 | "SUBST_CLASSES+= one", | | | |
110 | "SUBST_STAGE.one= post-configure", | | | |
111 | "SUBST_STAGE.one= post-configure", | | | |
112 | "SUBST_FILES.one= one.txt", | | | |
113 | "SUBST_CLASSES+= two", // The block "one" is not finished yet. | | | |
114 | "SUBST_SED.one= s,one,1,g", | | | |
115 | "SUBST_STAGE.two= post-configure", | | | |
116 | "SUBST_FILES.two= two.txt", | | | |
117 | "SUBST_SED.two= s,two,2,g") | | | |
118 | | | | |
119 | t.CheckOutputLines( | | | |
120 | "WARN: filename.mk:3: Duplicate definition of \"SUBST_STAGE.one\".", | | | |
121 | "WARN: filename.mk:5: Incomplete SUBST block: SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.", | | | |
122 | "WARN: filename.mk:5: Subst block \"one\" should be finished before adding the next class to SUBST_CLASSES.", | | | |
123 | "WARN: filename.mk:6: Variable \"SUBST_SED.one\" does not match SUBST class \"two\".") | | | |
124 | } | | | |
125 | | | | |
126 | // This is a strange example that probably won't occur in practice. | | 29 | // This is a strange example that probably won't occur in practice. |
127 | // | | 30 | // |
128 | // Continuing a SUBST class in one of the branches and starting | | 31 | // Continuing a SUBST class in one of the branches and starting |
129 | // a fresh one in the other seems far-fetched. | | 32 | // a fresh one in the other seems far-fetched. |
130 | func (s *Suite) Test_SubstContext__partially_continued_class_in_conditional(c *check.C) { | | 33 | func (s *Suite) Test_SubstContext__partially_continued_class_in_conditional(c *check.C) { |
131 | t := s.Init(c) | | 34 | t := s.Init(c) |
132 | | | 35 | |
133 | t.RunSubst( | | 36 | t.RunSubst( |
134 | "SUBST_CLASSES+= outer", | | 37 | "SUBST_CLASSES+= outer", |
135 | "SUBST_STAGE.outer= post-configure", | | 38 | "SUBST_STAGE.outer= post-configure", |
136 | "SUBST_FILES.outer= files", | | 39 | "SUBST_FILES.outer= files", |
137 | "SUBST_VARS.outer= OUTER.first", | | 40 | "SUBST_VARS.outer= OUTER.first", |
138 | ".if ${:Ualways}", | | 41 | ".if ${:Ualways}", |
139 | "SUBST_VARS.outer+= OUTER.second", | | 42 | "SUBST_VARS.outer+= OUTER.second", |
140 | ".else", | | 43 | ".else", |
141 | "SUBST_CLASSES+= inner", | | 44 | "SUBST_CLASSES+= inner", |
142 | "SUBST_STAGE.inner= post-configure", | | 45 | "SUBST_STAGE.inner= post-configure", |
143 | "SUBST_FILES.inner= files", | | 46 | "SUBST_FILES.inner= files", |
144 | "SUBST_VARS.inner= INNER", | | 47 | "SUBST_VARS.inner= INNER", |
145 | ".endif") | | 48 | ".endif") |
146 | | | 49 | |
147 | t.CheckOutputEmpty() | | 50 | t.CheckOutputEmpty() |
148 | } | | 51 | } |
149 | | | 52 | |
150 | func (s *Suite) Test_SubstContext__files_missing(c *check.C) { | | 53 | func (s *Suite) Test_SubstContext__conditionals(c *check.C) { |
151 | t := s.Init(c) | | | |
152 | | | | |
153 | t.RunSubst( | | | |
154 | "SUBST_CLASSES+= one", | | | |
155 | "SUBST_STAGE.one= pre-configure", | | | |
156 | "SUBST_CLASSES+= two", | | | |
157 | "SUBST_STAGE.two= pre-configure", | | | |
158 | "SUBST_FILES.two= two.txt", | | | |
159 | "SUBST_SED.two= s,two,2,g") | | | |
160 | | | | |
161 | t.CheckOutputLines( | | | |
162 | "WARN: filename.mk:3: Incomplete SUBST block: SUBST_FILES.one missing.", | | | |
163 | "WARN: filename.mk:3: Incomplete SUBST block: "+ | | | |
164 | "SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.", | | | |
165 | "WARN: filename.mk:3: Subst block \"one\" should be finished "+ | | | |
166 | "before adding the next class to SUBST_CLASSES.") | | | |
167 | } | | | |
168 | | | | |
169 | func (s *Suite) Test_SubstContext__directives(c *check.C) { | | | |
170 | t := s.Init(c) | | 54 | t := s.Init(c) |
171 | | | 55 | |
172 | t.RunSubst( | | 56 | t.RunSubst( |
173 | "SUBST_CLASSES+= os", | | 57 | "SUBST_CLASSES+= os", |
174 | "SUBST_STAGE.os= post-configure", | | 58 | "SUBST_STAGE.os= post-configure", |
175 | "SUBST_MESSAGE.os= Guessing operating system", | | 59 | "SUBST_MESSAGE.os= Guessing operating system", |
176 | "SUBST_FILES.os= guess-os.h", | | 60 | "SUBST_FILES.os= guess-os.h", |
177 | ".if ${OPSYS} == NetBSD", | | 61 | ".if ${OPSYS} == NetBSD", |
178 | "SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS@,NetBSD,", | | 62 | "SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS@,NetBSD,", |
179 | ".elif ${OPSYS} == Darwin", | | 63 | ".elif ${OPSYS} == Darwin", |
180 | "SUBST_SED.os= -e s,@OPSYS@,Darwin1,", | | 64 | "SUBST_SED.os= -e s,@OPSYS@,Darwin1,", |
181 | "SUBST_SED.os= -e s,@OPSYS@,Darwin2,", | | 65 | "SUBST_SED.os= -e s,@OPSYS@,Darwin2,", |
182 | ".elif ${OPSYS} == Linux", | | 66 | ".elif ${OPSYS} == Linux", |
183 | "SUBST_SED.os= -e s,@OPSYS@,Linux,", | | 67 | "SUBST_SED.os= -e s,@OPSYS@,Linux,", |
184 | ".else", | | 68 | ".else", |
185 | "SUBST_VARS.os= OPSYS", | | 69 | "SUBST_VARS.os= OPSYS", |
186 | ".endif") | | 70 | ".endif") |
187 | | | 71 | |
188 | // All the other lines are correctly determined as being alternatives | | 72 | // All the other lines are correctly determined as being alternatives |
189 | // to each other. And since every branch contains some transformation | | 73 | // to each other. And since every branch contains some transformation |
190 | // (SED, VARS, FILTER_CMD), everything is fine. | | 74 | // (SED, VARS, FILTER_CMD), everything is fine. |
191 | t.CheckOutputLines( | | 75 | t.CheckOutputLines( |
192 | "WARN: filename.mk:9: All but the first assignment " + | | 76 | "WARN: filename.mk:9: All but the first assignment " + |
193 | "to \"SUBST_SED.os\" should use the \"+=\" operator.") | | 77 | "to \"SUBST_SED.os\" should use the \"+=\" operator.") |
194 | } | | 78 | } |
195 | | | 79 | |
196 | func (s *Suite) Test_SubstContext__adjacent(c *check.C) { | | 80 | func (s *Suite) Test_SubstContext_varassign__no_class(c *check.C) { |
197 | t := s.Init(c) | | | |
198 | | | | |
199 | t.RunSubst( | | | |
200 | "SUBST_CLASSES+=\t1", | | | |
201 | "SUBST_STAGE.1=\tpre-configure", | | | |
202 | "SUBST_FILES.1=\tfile1", | | | |
203 | "SUBST_SED.1=\t-e s,subst1,repl1,", | | | |
204 | "SUBST_CLASSES+=\t2", | | | |
205 | "SUBST_SED.1+=\t-e s,subst1b,repl1b,", // Misplaced | | | |
206 | "SUBST_STAGE.2=\tpre-configure", | | | |
207 | "SUBST_FILES.2=\tfile2", | | | |
208 | "SUBST_SED.2=\t-e s,subst2,repl2,") | | | |
209 | | | | |
210 | t.CheckOutputLines( | | | |
211 | "WARN: filename.mk:6: Variable \"SUBST_SED.1\" does not match SUBST class \"2\".") | | | |
212 | } | | | |
213 | | | | |
214 | func (s *Suite) Test_SubstContext__do_patch(c *check.C) { | | | |
215 | t := s.Init(c) | | | |
216 | | | | |
217 | t.RunSubst( | | | |
218 | "SUBST_CLASSES+=\tos", | | | |
219 | "SUBST_STAGE.os=\tdo-patch", | | | |
220 | "SUBST_FILES.os=\tguess-os.h", | | | |
221 | "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,") | | | |
222 | | | | |
223 | // No warning, since there is nothing to fix automatically. | | | |
224 | // This case doesn't occur in practice anyway. | | | |
225 | t.CheckOutputEmpty() | | | |
226 | } | | | |
227 | | | | |
228 | // Variables mentioned in SUBST_VARS are not considered "foreign" | | | |
229 | // in the block and may be mixed with the other SUBST variables. | | | |
230 | func (s *Suite) Test_SubstContext__SUBST_VARS_defined_in_block(c *check.C) { | | | |
231 | t := s.Init(c) | | 81 | t := s.Init(c) |
232 | | | 82 | |
233 | t.RunSubst( | | 83 | t.RunSubst( |
234 | "SUBST_CLASSES+=\tos", | | 84 | "UNRELATED=anything", |
235 | "SUBST_STAGE.os=\tpre-configure", | | 85 | "SUBST_FILES.repl+=Makefile.in", |
236 | "SUBST_FILES.os=\tguess-os.h", | | 86 | "SUBST_SED.repl+=-e s,from,to,g") |
237 | "SUBST_VARS.os=\tTODAY1", | | | |
238 | "TODAY1!=\tdate", | | | |
239 | "TODAY2!=\tdate") | | | |
240 | | | 87 | |
241 | t.CheckOutputLines( | | 88 | t.CheckOutputLines( |
242 | "WARN: filename.mk:6: Foreign variable \"TODAY2\" in SUBST block.") | | 89 | "WARN: filename.mk:2: Before defining SUBST_FILES.repl, " + |
| | | 90 | "the SUBST class should be declared using \"SUBST_CLASSES+= repl\".") |
243 | } | | 91 | } |
244 | | | 92 | |
245 | // Variables mentioned in SUBST_VARS may appear in the same paragraph, | | 93 | func (s *Suite) Test_SubstContext_varassign__multiple_classes_in_one_line_multiple_blocks(c *check.C) { |
246 | // or alternatively anywhere else in the file. | | | |
247 | func (s *Suite) Test_SubstContext__SUBST_VARS_in_next_paragraph(c *check.C) { | | | |
248 | t := s.Init(c) | | 94 | t := s.Init(c) |
249 | | | 95 | |
250 | t.RunSubst( | | 96 | t.RunSubst( |
251 | "SUBST_CLASSES+=\tos", | | 97 | "SUBST_CLASSES+= one two", |
252 | "SUBST_STAGE.os=\tpre-configure", | | 98 | "SUBST_STAGE.one= post-configure", |
253 | "SUBST_FILES.os=\tguess-os.h", | | 99 | "SUBST_FILES.one= one.txt", |
254 | "SUBST_VARS.os=\tTODAY1", | | 100 | "SUBST_SED.one= s,one,1,g", |
255 | "", | | 101 | "", |
256 | "TODAY1!=\tdate", | | 102 | "SUBST_STAGE.two= post-configure", |
257 | "TODAY2!=\tdate") | | 103 | "SUBST_FILES.two= two.txt", |
| | | 104 | "", |
| | | 105 | "SUBST_STAGE.three= post-configure", |
| | | 106 | "", |
| | | 107 | "SUBST_VARS.four= PREFIX", |
| | | 108 | "", |
| | | 109 | "SUBST_VARS.three= PREFIX") |
258 | | | 110 | |
259 | t.CheckOutputEmpty() | | 111 | t.CheckOutputLines( |
| | | 112 | "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", |
| | | 113 | "WARN: filename.mk:9: Variable \"SUBST_STAGE.three\" "+ |
| | | 114 | "does not match SUBST class \"two\".", |
| | | 115 | "WARN: filename.mk:9: Incomplete SUBST block: "+ |
| | | 116 | "SUBST_SED.two, SUBST_VARS.two or SUBST_FILTER_CMD.two missing.", |
| | | 117 | "WARN: filename.mk:9: Before defining SUBST_STAGE.three, "+ |
| | | 118 | "the SUBST class should be declared using \"SUBST_CLASSES+= three\".", |
| | | 119 | "WARN: filename.mk:11: Before defining SUBST_VARS.four, "+ |
| | | 120 | "the SUBST class should be declared using \"SUBST_CLASSES+= four\".") |
260 | } | | 121 | } |
261 | | | 122 | |
262 | func (s *Suite) Test_SubstContext__multiple_SUBST_VARS(c *check.C) { | | 123 | func (s *Suite) Test_SubstContext_varassign__multiple_classes_in_one_block(c *check.C) { |
263 | t := s.Init(c) | | 124 | t := s.Init(c) |
264 | | | 125 | |
265 | t.RunSubst( | | 126 | t.RunSubst( |
266 | "SUBST_CLASSES+=\tos", | | 127 | "SUBST_CLASSES+= one", |
267 | "SUBST_STAGE.os=\tpre-configure", | | 128 | "SUBST_STAGE.one= post-configure", |
268 | "SUBST_FILES.os=\tguess-os.h", | | 129 | "SUBST_STAGE.one= post-configure", |
269 | "SUBST_VARS.os=\tPREFIX VARBASE") | | 130 | "SUBST_FILES.one= one.txt", |
| | | 131 | "SUBST_CLASSES+= two", // The block "one" is not finished yet. |
| | | 132 | "SUBST_SED.one= s,one,1,g", |
| | | 133 | "SUBST_STAGE.two= post-configure", |
| | | 134 | "SUBST_FILES.two= two.txt", |
| | | 135 | "SUBST_SED.two= s,two,2,g") |
270 | | | 136 | |
271 | t.CheckOutputEmpty() | | 137 | t.CheckOutputLines( |
| | | 138 | "WARN: filename.mk:3: Duplicate definition of \"SUBST_STAGE.one\".", |
| | | 139 | "WARN: filename.mk:5: Subst block \"one\" should be finished "+ |
| | | 140 | "before adding the next class to SUBST_CLASSES.", |
| | | 141 | "WARN: filename.mk:5: Incomplete SUBST block: SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.", |
| | | 142 | "WARN: filename.mk:6: Late additions to a SUBST variable should use the += operator.") |
272 | } | | 143 | } |
273 | | | 144 | |
274 | // As of May 2019, pkglint does not check the order of the variables in | | 145 | // As of December 2019, pkglint does not check the order of the variables in |
275 | // a SUBST block. Enforcing this order, or at least suggesting it, would | | 146 | // a SUBST block. Enforcing this order, or at least suggesting it, would |
276 | // make pkgsrc packages more uniform, which is a good idea, but not urgent. | | 147 | // make pkgsrc packages more uniform, which is a good idea, but not urgent. |
277 | func (s *Suite) Test_SubstContext__unusual_variable_order(c *check.C) { | | 148 | func (s *Suite) Test_SubstContext_varassign__unusual_order(c *check.C) { |
278 | t := s.Init(c) | | 149 | t := s.Init(c) |
279 | | | 150 | |
280 | t.RunSubst( | | 151 | t.RunSubst( |
281 | "SUBST_CLASSES+=\t\tid", | | 152 | "SUBST_CLASSES+=\t\tid", |
282 | "SUBST_SED.id=\t\t-e /deleteme/d", | | 153 | "SUBST_SED.id=\t\t-e /deleteme/d", |
283 | "SUBST_FILES.id=\t\tfile", | | 154 | "SUBST_FILES.id=\t\tfile", |
284 | "SUBST_MESSAGE.id=\tMessage", | | 155 | "SUBST_MESSAGE.id=\tMessage", |
285 | "SUBST_STAGE.id=\t\tpre-configure") | | 156 | "SUBST_STAGE.id=\t\tpre-configure") |
286 | | | 157 | |
287 | t.CheckOutputEmpty() | | 158 | t.CheckOutputEmpty() |
288 | } | | 159 | } |
289 | | | 160 | |
290 | func (s *Suite) Test_SubstContext__completely_conditional_then(c *check.C) { | | 161 | func (s *Suite) Test_SubstContext_varassign__blocks_in_separate_paragraphs(c *check.C) { |
291 | t := s.Init(c) | | | |
292 | | | | |
293 | t.RunSubst( | | | |
294 | ".if ${OPSYS} == Linux", | | | |
295 | "SUBST_CLASSES+=\tid", | | | |
296 | "SUBST_STAGE.id=\tpre-configure", | | | |
297 | "SUBST_SED.id=\t-e sahara", | | | |
298 | ".else", | | | |
299 | ".endif") | | | |
300 | | | | |
301 | // The block already ends at the .else, not at the end of the file, | | | |
302 | // since that is the scope where the SUBST id is defined. | | | |
303 | t.CheckOutputLines( | | | |
304 | "WARN: filename.mk:5: Incomplete SUBST block: SUBST_FILES.id missing.") | | | |
305 | } | | | |
306 | | | | |
307 | func (s *Suite) Test_SubstContext__completely_conditional_else(c *check.C) { | | | |
308 | t := s.Init(c) | | | |
309 | | | | |
310 | t.RunSubst( | | | |
311 | ".if ${OPSYS} == Linux", | | | |
312 | ".else", | | | |
313 | "SUBST_CLASSES+=\tid", | | | |
314 | "SUBST_STAGE.id=\tpre-configure", | | | |
315 | "SUBST_SED.id=\t-e sahara", | | | |
316 | ".endif") | | | |
317 | | | | |
318 | // The block already ends at the .endif, not at the end of the file, | | | |
319 | // since that is the scope where the SUBST id is defined. | | | |
320 | t.CheckOutputLines( | | | |
321 | "WARN: filename.mk:6: Incomplete SUBST block: SUBST_FILES.id missing.") | | | |
322 | } | | | |
323 | | | | |
324 | func (s *Suite) Test_SubstContext__SUBST_CLASSES_in_separate_paragraph(c *check.C) { | | | |
325 | t := s.Init(c) | | 162 | t := s.Init(c) |
326 | | | 163 | |
327 | t.RunSubst( | | 164 | t.RunSubst( |
328 | "SUBST_CLASSES+= 1 2 3 4", | | 165 | "SUBST_CLASSES+= 1 2 3 4", |
329 | "", | | 166 | "", |
330 | "SUBST_STAGE.1= post-configure", | | 167 | "SUBST_STAGE.1= post-configure", |
331 | "SUBST_FILES.1= files", | | 168 | "SUBST_FILES.1= files", |
332 | "SUBST_VARS.1= VAR1", | | 169 | "SUBST_VARS.1= VAR1", |
333 | "", | | 170 | "", |
334 | "SUBST_STAGE.2= post-configure", | | 171 | "SUBST_STAGE.2= post-configure", |
335 | "SUBST_FILES.2= files", | | 172 | "SUBST_FILES.2= files", |
336 | "SUBST_VARS.2= VAR1", | | 173 | "SUBST_VARS.2= VAR1", |
337 | "", | | 174 | "", |
338 | "SUBST_STAGE.3= post-configure", | | 175 | "SUBST_STAGE.3= post-configure", |
339 | "SUBST_FILES.3= files", | | 176 | "SUBST_FILES.3= files", |
340 | "SUBST_VARS.3= VAR1") | | 177 | "SUBST_VARS.3= VAR1") |
341 | | | 178 | |
342 | t.CheckOutputLines( | | 179 | t.CheckOutputLines( |
343 | "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", | | 180 | "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", |
344 | "WARN: filename.mk:EOF: Missing SUBST block for \"4\".") | | 181 | "WARN: filename.mk:EOF: Missing SUBST block for \"4\".") |
345 | } | | 182 | } |
346 | | | 183 | |
347 | func (s *Suite) Test_SubstContext__wrong_class(c *check.C) { | | 184 | func (s *Suite) Test_SubstContext_varassign__typo_in_id(c *check.C) { |
348 | t := s.Init(c) | | 185 | t := s.Init(c) |
349 | | | 186 | |
350 | t.RunSubst( | | 187 | t.RunSubst( |
351 | "SUBST_CLASSES+= 1 2", | | 188 | "SUBST_CLASSES+= 1 2", |
352 | "SUBST_STAGE.x= post-configure", | | 189 | "SUBST_STAGE.x= post-configure", |
353 | "SUBST_FILES.x= files", | | 190 | "SUBST_FILES.x= files", |
354 | "SUBST_VARS.x= VAR1", | | 191 | "SUBST_VARS.x= VAR1", |
355 | "SUBST_STAGE.2= post-configure", | | 192 | "SUBST_STAGE.2= post-configure", |
356 | "SUBST_FILES.2= files", | | 193 | "SUBST_FILES.2= files", |
357 | "SUBST_VARS.2= VAR1") | | 194 | "SUBST_VARS.2= VAR1") |
358 | | | 195 | |
359 | t.CheckOutputLines( | | 196 | t.CheckOutputLines( |
360 | "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", | | 197 | "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", |
361 | "WARN: filename.mk:2: Variable \"SUBST_STAGE.x\" does not match SUBST class \"1\".", | | 198 | "WARN: filename.mk:2: Before defining SUBST_STAGE.x, "+ |
362 | "WARN: filename.mk:3: Variable \"SUBST_FILES.x\" does not match SUBST class \"1\".", | | 199 | "the SUBST class should be declared using \"SUBST_CLASSES+= x\".", |
363 | "WARN: filename.mk:4: Variable \"SUBST_VARS.x\" does not match SUBST class \"1\".", | | 200 | "WARN: filename.mk:EOF: Missing SUBST block for \"1\".") |
364 | // XXX: This line could change to 2, since that is already in the queue. | | | |
365 | "WARN: filename.mk:5: Variable \"SUBST_STAGE.2\" does not match SUBST class \"1\".", | | | |
366 | "WARN: filename.mk:6: Variable \"SUBST_FILES.2\" does not match SUBST class \"1\".", | | | |
367 | "WARN: filename.mk:7: Variable \"SUBST_VARS.2\" does not match SUBST class \"1\".", | | | |
368 | "WARN: filename.mk:EOF: Missing SUBST block for \"1\".", | | | |
369 | "WARN: filename.mk:EOF: Missing SUBST block for \"2\".") | | | |
370 | } | | | |
371 | | | | |
372 | func (s *Suite) Test_SubstContext_varassign__late_addition(c *check.C) { | | | |
373 | t := s.Init(c) | | | |
374 | | | | |
375 | t.RunSubst( | | | |
376 | "SUBST_CLASSES+=\tid", | | | |
377 | "SUBST_STAGE.id=\tpost-configure", | | | |
378 | "SUBST_FILES.id=\tfiles", | | | |
379 | "SUBST_VARS.id=\tPREFIX", | | | |
380 | "", | | | |
381 | ".if ${OPSYS} == NetBSD", | | | |
382 | "SUBST_VARS.id=\tOPSYS", | | | |
383 | ".endif") | | | |
384 | | | | |
385 | t.CheckOutputLines( | | | |
386 | "WARN: filename.mk:7: Late additions to a SUBST variable " + | | | |
387 | "should use the += operator.") | | | |
388 | } | | 201 | } |
389 | | | 202 | |
390 | func (s *Suite) Test_SubstContext_varassign__late_addition_to_unknown_class(c *check.C) { | | 203 | func (s *Suite) Test_SubstContext_varassign__late_addition_to_unknown_class(c *check.C) { |
391 | t := s.Init(c) | | 204 | t := s.Init(c) |
392 | | | 205 | |
393 | mklines := t.NewMkLines("filename.mk", | | 206 | mklines := t.NewMkLines("filename.mk", |
394 | "SUBST_VARS.id=\tOPSYS", | | 207 | "SUBST_VARS.id=\tOPSYS", |
395 | "") | | 208 | "") |
396 | ctx := NewSubstContext() | | 209 | ctx := NewSubstContext() |
397 | mklines.collectRationale() | | 210 | mklines.collectRationale() |
398 | | | 211 | |
399 | mklines.ForEach(ctx.Process) | | 212 | mklines.ForEach(ctx.Process) |
400 | | | 213 | |
401 | t.CheckOutputLines( | | 214 | t.CheckOutputLines( |
402 | "WARN: filename.mk:1: Before defining SUBST_VARS.id, " + | | 215 | "WARN: filename.mk:1: Before defining SUBST_VARS.id, " + |
403 | "the SUBST class should be declared using \"SUBST_CLASSES+= id\".") | | 216 | "the SUBST class should be declared using \"SUBST_CLASSES+= id\".") |
404 | } | | 217 | } |
405 | | | 218 | |
406 | func (s *Suite) Test_SubstContext_varassignClasses__none(c *check.C) { | | 219 | func (s *Suite) Test_SubstContext_varassign__rationale(c *check.C) { |
407 | t := s.Init(c) | | 220 | t := s.Init(c) |
408 | | | 221 | |
409 | t.RunSubst( | | 222 | t.RunSubst( |
410 | "SUBST_CLASSES+=\t# none") | | 223 | "# Adjust setup.py", |
| | | 224 | "SUBST_CLASSES+= setup", |
| | | 225 | "SUBST_STAGE.setup= post-configure", |
| | | 226 | "SUBST_FILES.setup= setup.py", |
| | | 227 | "SUBST_VARS.setup= VAR") |
411 | | | 228 | |
| | | 229 | // The rationale in line 1 is supposed to suppress warnings, |
| | | 230 | // not add new ones. |
412 | t.CheckOutputEmpty() | | 231 | t.CheckOutputEmpty() |
413 | } | | 232 | } |
414 | | | 233 | |
415 | func (s *Suite) Test_SubstContext_varassignClasses__indirect(c *check.C) { | | 234 | func (s *Suite) Test_SubstContext_varassign__interleaved(c *check.C) { |
416 | t := s.Init(c) | | 235 | t := s.Init(c) |
417 | | | 236 | |
418 | t.SetUpVartypes() | | 237 | t.RunSubst( |
419 | mklines := t.NewMkLines("filename.mk", | | 238 | "SUBST_CLASSES+= 1 2 3", |
420 | MkCvsID, | | 239 | "SUBST_STAGE.1= post-configure", |
421 | "SUBST_CLASSES+=\t${VAR}") | | 240 | "SUBST_STAGE.2= post-configure", |
422 | | | 241 | "SUBST_STAGE.3= post-configure", |
423 | mklines.Check() | | 242 | "SUBST_FILES.1= setup.py", |
424 | | | 243 | "SUBST_FILES.2= setup.py", |
| | | 244 | "SUBST_FILES.3= setup.py", |
| | | 245 | "SUBST_VARS.1= VAR", |
| | | 246 | "SUBST_VARS.2= VAR", |
| | | 247 | "SUBST_VARS.3= VAR") |
| | | 248 | |
| | | 249 | // The above does not follow the common pattern of defining |
| | | 250 | // each block on its own. |
| | | 251 | // It technically works but is not easy to read for humans. |
| | | 252 | t.CheckOutputLines( |
| | | 253 | "NOTE: filename.mk:1: " + |
| | | 254 | "Please add only one class at a time to SUBST_CLASSES.") |
| | | 255 | } |
| | | 256 | |
| | | 257 | func (s *Suite) Test_SubstContext_varassignClasses__OPSYSVARS(c *check.C) { |
| | | 258 | t := s.Init(c) |
| | | 259 | |
| | | 260 | ctx := NewSubstContext() |
| | | 261 | |
| | | 262 | // SUBST_CLASSES is added to OPSYSVARS in mk/bsd.pkg.mk. |
| | | 263 | ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES.SunOS+=prefix")) |
| | | 264 | ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_CLASSES.NetBSD+=prefix")) |
| | | 265 | ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_FILES.prefix=Makefile")) |
| | | 266 | ctx.varassign(t.NewMkLine("filename.mk", 14, "SUBST_SED.prefix=s,@PREFIX@,${PREFIX},g")) |
| | | 267 | ctx.varassign(t.NewMkLine("filename.mk", 15, "SUBST_STAGE.prefix=post-configure")) |
| | | 268 | |
| | | 269 | t.CheckEquals(ctx.block().isComplete(), true) |
| | | 270 | |
| | | 271 | ctx.Finish(t.NewMkLine("filename.mk", 15, "")) |
| | | 272 | |
| | | 273 | t.CheckOutputLines( |
| | | 274 | "NOTE: filename.mk:14: The substitution command \"s,@PREFIX@,${PREFIX},g\" " + |
| | | 275 | "can be replaced with \"SUBST_VARS.prefix= PREFIX\".") |
| | | 276 | } |
| | | 277 | |
| | | 278 | func (s *Suite) Test_SubstContext_varassignClasses__duplicate_id(c *check.C) { |
| | | 279 | t := s.Init(c) |
| | | 280 | |
| | | 281 | t.RunSubst( |
| | | 282 | "SUBST_CLASSES+= id", |
| | | 283 | "SUBST_CLASSES+= id") |
| | | 284 | |
| | | 285 | t.CheckOutputLines( |
| | | 286 | "ERROR: filename.mk:2: Duplicate SUBST class \"id\".", |
| | | 287 | "WARN: filename.mk:EOF: Missing SUBST block for \"id\".") |
| | | 288 | } |
| | | 289 | |
| | | 290 | func (s *Suite) Test_SubstContext_varassignClasses__multiple_classes_in_one_line(c *check.C) { |
| | | 291 | t := s.Init(c) |
| | | 292 | |
| | | 293 | t.RunSubst( |
| | | 294 | "SUBST_CLASSES+= one two", |
| | | 295 | "SUBST_STAGE.one= post-configure", |
| | | 296 | "SUBST_FILES.one= one.txt", |
| | | 297 | "SUBST_SED.one= s,one,1,g", |
| | | 298 | "SUBST_STAGE.two= post-configure", |
| | | 299 | "SUBST_FILES.two= two.txt") |
| | | 300 | |
| | | 301 | t.CheckOutputLines( |
| | | 302 | "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", |
| | | 303 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_SED.two, SUBST_VARS.two or SUBST_FILTER_CMD.two missing.") |
| | | 304 | } |
| | | 305 | |
| | | 306 | func (s *Suite) Test_SubstContext_varassignClasses__none(c *check.C) { |
| | | 307 | t := s.Init(c) |
| | | 308 | |
| | | 309 | t.RunSubst( |
| | | 310 | "SUBST_CLASSES+=\t# none") |
| | | 311 | |
| | | 312 | t.CheckOutputEmpty() |
| | | 313 | } |
| | | 314 | |
| | | 315 | func (s *Suite) Test_SubstContext_varassignClasses__indirect(c *check.C) { |
| | | 316 | t := s.Init(c) |
| | | 317 | |
| | | 318 | t.SetUpVartypes() |
| | | 319 | mklines := t.NewMkLines("filename.mk", |
| | | 320 | MkCvsID, |
| | | 321 | "SUBST_CLASSES+=\t${VAR}") |
| | | 322 | |
| | | 323 | mklines.Check() |
| | | 324 | |
425 | t.CheckOutputLines( | | 325 | t.CheckOutputLines( |
426 | "ERROR: filename.mk:2: Identifiers for SUBST_CLASSES "+ | | 326 | "ERROR: filename.mk:2: Identifiers for SUBST_CLASSES "+ |
427 | "must not refer to other variables.", | | 327 | "must not refer to other variables.", |
428 | "WARN: filename.mk:2: VAR is used but not defined.") | | 328 | "WARN: filename.mk:2: VAR is used but not defined.") |
429 | } | | 329 | } |
430 | | | 330 | |
| | | 331 | func (s *Suite) Test_SubstContext_varassignOutsideBlock__assign(c *check.C) { |
| | | 332 | t := s.Init(c) |
| | | 333 | |
| | | 334 | t.RunSubst( |
| | | 335 | "SUBST_CLASSES+=\t1", |
| | | 336 | "SUBST_STAGE.1=\tpre-configure", |
| | | 337 | "SUBST_FILES.1=\tfile1", |
| | | 338 | "SUBST_SED.1=\t-e s,subst1,repl1,", |
| | | 339 | "SUBST_CLASSES+=\t2", |
| | | 340 | "SUBST_SED.1=\t-e s,subst1b,repl1b,", // Misplaced |
| | | 341 | "SUBST_STAGE.2=\tpre-configure", |
| | | 342 | "SUBST_FILES.2=\tfile2", |
| | | 343 | "SUBST_SED.2=\t-e s,subst2,repl2,") |
| | | 344 | |
| | | 345 | t.CheckOutputLines( |
| | | 346 | "WARN: filename.mk:6: Late additions to a SUBST variable " + |
| | | 347 | "should use the += operator.") |
| | | 348 | } |
| | | 349 | |
| | | 350 | func (s *Suite) Test_SubstContext_varassignOutsideBlock__append(c *check.C) { |
| | | 351 | t := s.Init(c) |
| | | 352 | |
| | | 353 | t.RunSubst( |
| | | 354 | "SUBST_CLASSES+=\t1", |
| | | 355 | "SUBST_STAGE.1=\tpre-configure", |
| | | 356 | "SUBST_FILES.1=\tfile1", |
| | | 357 | "SUBST_SED.1=\t-e s,subst1,repl1,", |
| | | 358 | "SUBST_CLASSES+=\t2", |
| | | 359 | "SUBST_SED.1+=\t-e s,subst1b,repl1b,", // Misplaced |
| | | 360 | "SUBST_STAGE.2=\tpre-configure", |
| | | 361 | "SUBST_FILES.2=\tfile2", |
| | | 362 | "SUBST_SED.2=\t-e s,subst2,repl2,") |
| | | 363 | |
| | | 364 | // The SUBST_SED.1 line is misplaced. It uses the += operator, |
| | | 365 | // which makes it unclear whether this is a typo or not. |
| | | 366 | t.CheckOutputEmpty() |
| | | 367 | } |
| | | 368 | |
431 | // The rationale for the stray SUBST variables has to be specific. | | 369 | // The rationale for the stray SUBST variables has to be specific. |
432 | // | | 370 | // |
433 | // For example, in the following snippet from mail/dkim-milter/options.mk | | 371 | // For example, in the following snippet from mail/dkim-milter/options.mk |
434 | // revision 1.9, there is a comment, but that is not a rationale and also | | 372 | // revision 1.9, there is a comment, but that is not a rationale and also |
435 | // not related to the SUBST_CLASS variable at all: | | 373 | // not related to the SUBST_CLASS variable at all: |
436 | // ### IPv6 support. | | 374 | // ### IPv6 support. |
437 | // .if !empty(PKG_OPTIONS:Minet6) | | 375 | // .if !empty(PKG_OPTIONS:Minet6) |
438 | // SUBST_SED.libs+= -e 's|@INET6@||g' | | 376 | // SUBST_SED.libs+= -e 's|@INET6@||g' |
439 | // .endif | | 377 | // .endif |
440 | func (s *Suite) Test_SubstContext_varassignOutsideBlock__rationale(c *check.C) { | | 378 | func (s *Suite) Test_SubstContext_varassignOutsideBlock__rationale(c *check.C) { |
441 | t := s.Init(c) | | 379 | t := s.Init(c) |
442 | | | 380 | |
443 | mklines := t.NewMkLines("filename.mk", | | 381 | mklines := t.NewMkLines("filename.mk", |
| @@ -450,108 +388,540 @@ func (s *Suite) Test_SubstContext_varass | | | @@ -450,108 +388,540 @@ func (s *Suite) Test_SubstContext_varass |
450 | "# The two class is defined somewhere else.", | | 388 | "# The two class is defined somewhere else.", |
451 | "SUBST_VARS.two=\tOPSYS", | | 389 | "SUBST_VARS.two=\tOPSYS", |
452 | "", | | 390 | "", |
453 | // The word "defined" doesn't match the subst class "def". | | 391 | // The word "defined" doesn't match the subst class "def". |
454 | "# This subst class is defined somewhere else.", | | 392 | "# This subst class is defined somewhere else.", |
455 | "SUBST_VARS.def=\tOPSYS", | | 393 | "SUBST_VARS.def=\tOPSYS", |
456 | "", | | 394 | "", |
457 | "# Rationale that is completely irrelevant.", | | 395 | "# Rationale that is completely irrelevant.", |
458 | "SUBST_SED.libs+=\t-e sahara", | | 396 | "SUBST_SED.libs+=\t-e sahara", |
459 | "") | | 397 | "") |
460 | ctx := NewSubstContext() | | 398 | ctx := NewSubstContext() |
461 | mklines.collectRationale() | | 399 | mklines.collectRationale() |
462 | | | 400 | |
463 | mklines.ForEach(ctx.Process) | | 401 | mklines.ForEach(ctx.Process) |
| | | 402 | |
| | | 403 | t.CheckOutputLines( |
| | | 404 | "WARN: filename.mk:2: Before defining SUBST_VARS.one, "+ |
| | | 405 | "the SUBST class should be declared using \"SUBST_CLASSES+= one\".", |
| | | 406 | // In filename.mk:5 there is a proper rationale, thus no warning. |
| | | 407 | "WARN: filename.mk:8: Before defining SUBST_VARS.def, "+ |
| | | 408 | "the SUBST class should be declared using \"SUBST_CLASSES+= def\".", |
| | | 409 | "WARN: filename.mk:11: Before defining SUBST_SED.libs, "+ |
| | | 410 | "the SUBST class should be declared using \"SUBST_CLASSES+= libs\".") |
| | | 411 | } |
| | | 412 | |
| | | 413 | // Unbalanced conditionals must not lead to a panic. |
| | | 414 | func (s *Suite) Test_SubstContext_directive__before_SUBST_CLASSES(c *check.C) { |
| | | 415 | t := s.Init(c) |
| | | 416 | |
| | | 417 | t.RunSubst( |
| | | 418 | ".if 0", |
| | | 419 | ".endif", |
| | | 420 | "SUBST_CLASSES+=\tos", |
| | | 421 | ".elif 0") |
| | | 422 | |
| | | 423 | t.CheckOutputLines( |
| | | 424 | "WARN: filename.mk:4: Missing SUBST block for \"os\".") |
| | | 425 | } |
| | | 426 | |
| | | 427 | func (s *Suite) Test_SubstContext_directive__conditional_blocks_complete(c *check.C) { |
| | | 428 | t := s.Init(c) |
| | | 429 | |
| | | 430 | t.RunSubst( |
| | | 431 | ".if ${OPSYS} == NetBSD", |
| | | 432 | "SUBST_CLASSES+= nb", |
| | | 433 | "SUBST_STAGE.nb= post-configure", |
| | | 434 | "SUBST_FILES.nb= guess-netbsd.h", |
| | | 435 | "SUBST_VARS.nb= HAVE_NETBSD", |
| | | 436 | ".else", |
| | | 437 | "SUBST_CLASSES+= os", |
| | | 438 | "SUBST_STAGE.os= post-configure", |
| | | 439 | "SUBST_FILES.os= guess-netbsd.h", |
| | | 440 | "SUBST_VARS.os= HAVE_OTHER", |
| | | 441 | ".endif") |
| | | 442 | |
| | | 443 | t.CheckOutputEmpty() |
| | | 444 | } |
| | | 445 | |
| | | 446 | func (s *Suite) Test_SubstContext_directive__conditional_blocks_incomplete(c *check.C) { |
| | | 447 | t := s.Init(c) |
| | | 448 | |
| | | 449 | t.RunSubst( |
| | | 450 | ".if ${OPSYS} == NetBSD", |
| | | 451 | "SUBST_CLASSES+= nb", |
| | | 452 | "SUBST_STAGE.nb= post-configure", |
| | | 453 | "SUBST_VARS.nb= HAVE_NETBSD", |
| | | 454 | ".else", |
| | | 455 | "SUBST_CLASSES+= os", |
| | | 456 | "SUBST_STAGE.os= post-configure", |
| | | 457 | "SUBST_FILES.os= guess-netbsd.h", |
| | | 458 | ".endif") |
| | | 459 | |
| | | 460 | t.CheckOutputLines( |
| | | 461 | "WARN: filename.mk:5: Incomplete SUBST block: SUBST_FILES.nb missing.", |
| | | 462 | "WARN: filename.mk:6: Subst block \"nb\" should be finished "+ |
| | | 463 | "before adding the next class to SUBST_CLASSES.", |
| | | 464 | "WARN: filename.mk:9: Incomplete SUBST block: "+ |
| | | 465 | "SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.") |
| | | 466 | } |
| | | 467 | |
| | | 468 | func (s *Suite) Test_SubstContext_directive__conditional_complete(c *check.C) { |
| | | 469 | t := s.Init(c) |
| | | 470 | |
| | | 471 | t.RunSubst( |
| | | 472 | "SUBST_CLASSES+= id", |
| | | 473 | ".if ${OPSYS} == NetBSD", |
| | | 474 | "SUBST_STAGE.id=\t\tpost-configure", |
| | | 475 | "SUBST_MESSAGE.id=\tpost-configure", |
| | | 476 | "SUBST_FILES.id=\t\tguess-netbsd.h", |
| | | 477 | "SUBST_SED.id=\t\t-e s,from,to,", |
| | | 478 | "SUBST_VARS.id=\t\tHAVE_OTHER", |
| | | 479 | "SUBST_FILTER_CMD.id=\tHAVE_OTHER", |
| | | 480 | ".else", |
| | | 481 | "SUBST_STAGE.id=\t\tpost-configure", |
| | | 482 | "SUBST_MESSAGE.id=\tpost-configure", |
| | | 483 | "SUBST_FILES.id=\t\tguess-netbsd.h", |
| | | 484 | "SUBST_SED.id=\t\t-e s,from,to,", |
| | | 485 | "SUBST_VARS.id=\t\tHAVE_OTHER", |
| | | 486 | "SUBST_FILTER_CMD.id=\tHAVE_OTHER", |
| | | 487 | ".endif") |
| | | 488 | |
| | | 489 | t.CheckOutputLines( |
| | | 490 | "WARN: filename.mk:3: SUBST_STAGE.id should not be defined conditionally.", |
| | | 491 | "WARN: filename.mk:4: SUBST_MESSAGE.id should not be defined conditionally.", |
| | | 492 | "WARN: filename.mk:10: SUBST_STAGE.id should not be defined conditionally.", |
| | | 493 | "WARN: filename.mk:11: SUBST_MESSAGE.id should not be defined conditionally.") |
| | | 494 | } |
| | | 495 | |
| | | 496 | func (s *Suite) Test_SubstContext_directive__conditionally_overwritten_filter(c *check.C) { |
| | | 497 | t := s.Init(c) |
| | | 498 | |
| | | 499 | t.RunSubst( |
| | | 500 | "SUBST_CLASSES+= id", |
| | | 501 | "SUBST_STAGE.id=\t\tpost-configure", |
| | | 502 | "SUBST_MESSAGE.id=\tpost-configure", |
| | | 503 | "SUBST_FILES.id=\t\tguess-netbsd.h", |
| | | 504 | "SUBST_FILTER_CMD.id=\tHAVE_OTHER", |
| | | 505 | ".if ${OPSYS} == NetBSD", |
| | | 506 | "SUBST_FILTER_CMD.id=\tHAVE_OTHER", |
| | | 507 | ".endif") |
| | | 508 | |
| | | 509 | t.CheckOutputLines( |
| | | 510 | "WARN: filename.mk:7: Duplicate definition of \"SUBST_FILTER_CMD.id\".") |
| | | 511 | } |
| | | 512 | |
| | | 513 | // Hopefully nobody will ever trigger this case in real pkgsrc. |
| | | 514 | // It's plain confusing to a casual reader to nest a complete |
| | | 515 | // SUBST block into another SUBST block. |
| | | 516 | func (s *Suite) Test_SubstContext_directive__conditionally_nested_block(c *check.C) { |
| | | 517 | t := s.Init(c) |
| | | 518 | |
| | | 519 | t.RunSubst( |
| | | 520 | "SUBST_CLASSES+= outer", |
| | | 521 | "SUBST_STAGE.outer= post-configure", |
| | | 522 | "SUBST_FILES.outer= outer.txt", |
| | | 523 | ".if ${OPSYS} == NetBSD", |
| | | 524 | "SUBST_CLASSES+= inner", |
| | | 525 | "SUBST_STAGE.inner= post-configure", |
| | | 526 | "SUBST_FILES.inner= inner.txt", |
| | | 527 | "SUBST_VARS.inner= INNER", |
| | | 528 | ".endif", |
| | | 529 | "SUBST_VARS.outer= OUTER") |
| | | 530 | |
| | | 531 | t.CheckOutputLines( |
| | | 532 | "WARN: filename.mk:5: Subst block \"outer\" should be finished " + |
| | | 533 | "before adding the next class to SUBST_CLASSES.") |
| | | 534 | } |
| | | 535 | |
| | | 536 | // It's completely valid to have several SUBST blocks in a single paragraph. |
| | | 537 | // As soon as a SUBST_CLASSES line appears, pkglint assumes that all previous |
| | | 538 | // SUBST blocks are finished. That's exactly the case here. |
| | | 539 | func (s *Suite) Test_SubstContext_directive__conditionally_following_block(c *check.C) { |
| | | 540 | t := s.Init(c) |
| | | 541 | |
| | | 542 | t.RunSubst( |
| | | 543 | "SUBST_CLASSES+= outer", |
| | | 544 | "SUBST_STAGE.outer= post-configure", |
| | | 545 | "SUBST_FILES.outer= outer.txt", |
| | | 546 | "SUBST_VARS.outer= OUTER", |
| | | 547 | ".if ${OPSYS} == NetBSD", |
| | | 548 | "SUBST_CLASSES+= middle", |
| | | 549 | "SUBST_STAGE.middle= post-configure", |
| | | 550 | "SUBST_FILES.middle= inner.txt", |
| | | 551 | "SUBST_VARS.middle= INNER", |
| | | 552 | ". if ${MACHINE_ARCH} == amd64", |
| | | 553 | "SUBST_CLASSES+= inner", |
| | | 554 | "SUBST_STAGE.inner= post-configure", |
| | | 555 | "SUBST_FILES.inner= inner.txt", |
| | | 556 | "SUBST_VARS.inner= INNER", |
| | | 557 | ". endif", |
| | | 558 | ".endif") |
| | | 559 | |
| | | 560 | t.CheckOutputEmpty() |
| | | 561 | } |
| | | 562 | |
| | | 563 | func (s *Suite) Test_SubstContext_directive__two_blocks_in_condition(c *check.C) { |
| | | 564 | t := s.Init(c) |
| | | 565 | |
| | | 566 | t.RunSubst( |
| | | 567 | ".if ${OPSYS} == NetBSD", |
| | | 568 | "SUBST_CLASSES+= a", |
| | | 569 | "SUBST_STAGE.a= post-configure", |
| | | 570 | "SUBST_FILES.a= outer.txt", |
| | | 571 | "SUBST_VARS.a= OUTER", |
| | | 572 | "SUBST_CLASSES+= b", |
| | | 573 | "SUBST_STAGE.b= post-configure", |
| | | 574 | "SUBST_FILES.b= inner.txt", |
| | | 575 | "SUBST_VARS.b= INNER", |
| | | 576 | ".endif") |
| | | 577 | |
| | | 578 | // Up to 2019-12-12, pkglint wrongly warned in filename.mk:6: |
| | | 579 | // Subst block "a" should be finished before adding |
| | | 580 | // the next class to SUBST_CLASSES. |
| | | 581 | // The warning was wrong since block "a" has all required fields set. |
| | | 582 | // The warning was caused by an inconsistent check whether the current |
| | | 583 | // block had any conditional variables. |
| | | 584 | t.CheckOutputEmpty() |
| | | 585 | } |
| | | 586 | |
| | | 587 | func (s *Suite) Test_SubstContext_directive__nested_conditional_incomplete_block(c *check.C) { |
| | | 588 | t := s.Init(c) |
| | | 589 | |
| | | 590 | t.RunSubst( |
| | | 591 | "SUBST_CLASSES+= outer", |
| | | 592 | "SUBST_STAGE.outer= post-configure", |
| | | 593 | "SUBST_FILES.outer= outer.txt", |
| | | 594 | "SUBST_VARS.outer= OUTER", |
| | | 595 | ".if ${OPSYS} == NetBSD", |
| | | 596 | "SUBST_CLASSES+= inner1", |
| | | 597 | "SUBST_STAGE.inner1= post-configure", |
| | | 598 | "SUBST_VARS.inner1= INNER", |
| | | 599 | "SUBST_CLASSES+= inner2", |
| | | 600 | "SUBST_STAGE.inner2= post-configure", |
| | | 601 | "SUBST_FILES.inner2= inner.txt", |
| | | 602 | "SUBST_VARS.inner2= INNER", |
| | | 603 | ".endif") |
| | | 604 | |
| | | 605 | t.CheckOutputLines( |
| | | 606 | "WARN: filename.mk:9: Subst block \"inner1\" should be finished "+ |
| | | 607 | "before adding the next class to SUBST_CLASSES.", |
| | | 608 | "WARN: filename.mk:9: Incomplete SUBST block: SUBST_FILES.inner1 missing.") |
| | | 609 | } |
| | | 610 | |
| | | 611 | func (s *Suite) Test_SubstContext_leave__details_in_then_branch(c *check.C) { |
| | | 612 | t := s.Init(c) |
| | | 613 | |
| | | 614 | t.RunSubst( |
| | | 615 | "SUBST_CLASSES+= os", |
| | | 616 | ".if ${OPSYS} == NetBSD", |
| | | 617 | "SUBST_VARS.os= OPSYS", |
| | | 618 | "SUBST_SED.os= -e s,@OPSYS@,NetBSD,", |
| | | 619 | "SUBST_STAGE.os= post-configure", |
| | | 620 | "SUBST_MESSAGE.os= Guessing operating system", |
| | | 621 | "SUBST_FILES.os= guess-os.h", |
| | | 622 | ".endif") |
| | | 623 | |
| | | 624 | t.CheckOutputLines( |
| | | 625 | "WARN: filename.mk:5: SUBST_STAGE.os should not be defined conditionally.", |
| | | 626 | "WARN: filename.mk:6: SUBST_MESSAGE.os should not be defined conditionally.", |
| | | 627 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_STAGE.os missing.", |
| | | 628 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.os missing.", |
| | | 629 | "WARN: filename.mk:EOF: Incomplete SUBST block: "+ |
| | | 630 | "SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.") |
| | | 631 | } |
| | | 632 | |
| | | 633 | func (s *Suite) Test_SubstContext_leave__details_in_else_branch(c *check.C) { |
| | | 634 | t := s.Init(c) |
| | | 635 | |
| | | 636 | t.RunSubst( |
| | | 637 | "SUBST_CLASSES+= os", |
| | | 638 | ".if ${OPSYS} == NetBSD", |
| | | 639 | ".else", |
| | | 640 | "SUBST_VARS.os= OPSYS", |
| | | 641 | "SUBST_SED.os= -e s,@OPSYS@,NetBSD,", |
| | | 642 | "SUBST_STAGE.os= post-configure", |
| | | 643 | "SUBST_MESSAGE.os= Guessing operating system", |
| | | 644 | "SUBST_FILES.os= guess-os.h", |
| | | 645 | ".endif") |
| | | 646 | |
| | | 647 | t.CheckOutputLines( |
| | | 648 | "WARN: filename.mk:6: SUBST_STAGE.os should not be defined conditionally.", |
| | | 649 | "WARN: filename.mk:7: SUBST_MESSAGE.os should not be defined conditionally.", |
| | | 650 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_STAGE.os missing.", |
| | | 651 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.os missing.", |
| | | 652 | "WARN: filename.mk:EOF: Incomplete SUBST block: "+ |
| | | 653 | "SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.") |
| | | 654 | } |
| | | 655 | |
| | | 656 | func (s *Suite) Test_SubstContext_leave__empty_conditional_at_end(c *check.C) { |
| | | 657 | t := s.Init(c) |
| | | 658 | |
| | | 659 | t.RunSubst( |
| | | 660 | "SUBST_CLASSES+= os", |
| | | 661 | "SUBST_VARS.os= OPSYS", |
| | | 662 | "SUBST_SED.os= -e s,@OPSYS@,NetBSD,", |
| | | 663 | "SUBST_STAGE.os= post-configure", |
| | | 664 | "SUBST_MESSAGE.os= Guessing operating system", |
| | | 665 | "SUBST_FILES.os= guess-os.h", |
| | | 666 | ".if ${OPSYS} == NetBSD", |
| | | 667 | ".else", |
| | | 668 | ".endif") |
| | | 669 | |
| | | 670 | t.CheckOutputEmpty() |
| | | 671 | } |
| | | 672 | |
| | | 673 | func (s *Suite) Test_SubstContext_leave__missing_transformation_in_one_branch(c *check.C) { |
| | | 674 | t := s.Init(c) |
| | | 675 | |
| | | 676 | t.RunSubst( |
| | | 677 | "SUBST_CLASSES+= os", |
| | | 678 | "SUBST_STAGE.os= post-configure", |
| | | 679 | "SUBST_MESSAGE.os= Guessing operating system", |
| | | 680 | "SUBST_FILES.os= guess-os.h", |
| | | 681 | ".if ${OPSYS} == NetBSD", |
| | | 682 | "SUBST_FILES.os= -e s,@OpSYS@,NetBSD,", // A simple typo, this should be SUBST_SED. |
| | | 683 | ".elif ${OPSYS} == Darwin", |
| | | 684 | "SUBST_SED.os= -e s,@OPSYS@,Darwin1,", |
| | | 685 | "SUBST_SED.os= -e s,@OPSYS@,Darwin2,", |
| | | 686 | ".else", |
| | | 687 | "SUBST_VARS.os= OPSYS", |
| | | 688 | ".endif") |
| | | 689 | |
| | | 690 | t.CheckOutputLines( |
| | | 691 | "WARN: filename.mk:6: All but the first assignment "+ |
| | | 692 | "to \"SUBST_FILES.os\" should use the \"+=\" operator.", |
| | | 693 | "WARN: filename.mk:9: All but the first assignment "+ |
| | | 694 | "to \"SUBST_SED.os\" should use the \"+=\" operator.", |
| | | 695 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_SED.os, "+ |
| | | 696 | "SUBST_VARS.os or SUBST_FILTER_CMD.os missing.") |
| | | 697 | } |
| | | 698 | |
| | | 699 | func (s *Suite) Test_SubstContext_leave__nested_conditionals(c *check.C) { |
| | | 700 | t := s.Init(c) |
| | | 701 | |
| | | 702 | t.RunSubst( |
| | | 703 | "SUBST_CLASSES+= os", |
| | | 704 | "SUBST_STAGE.os= post-configure", |
| | | 705 | "SUBST_MESSAGE.os= Guessing operating system", |
| | | 706 | ".if ${OPSYS} == NetBSD", |
| | | 707 | "SUBST_FILES.os= guess-netbsd.h", |
| | | 708 | ". if ${ARCH} == i386", |
| | | 709 | "SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS,NetBSD-i386,", |
| | | 710 | ". elif ${ARCH} == x86_64", |
| | | 711 | "SUBST_VARS.os= OPSYS", |
| | | 712 | ". else", |
| | | 713 | "SUBST_SED.os= -e s,@OPSYS,NetBSD-unknown", |
| | | 714 | ". endif", |
| | | 715 | ".else", |
| | | 716 | // This branch omits SUBST_FILES. |
| | | 717 | "SUBST_SED.os= -e s,@OPSYS@,unknown,", |
| | | 718 | ".endif") |
| | | 719 | |
| | | 720 | t.CheckOutputLines( |
| | | 721 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.os missing.") |
| | | 722 | } |
| | | 723 | |
| | | 724 | // With every .if directive, a new scope is created, to properly |
| | | 725 | // keep track of the conditional level at which the SUBST classes |
| | | 726 | // are declared. |
| | | 727 | // |
| | | 728 | // The scopes are even created when there is no SUBST variable |
| | | 729 | // anywhere close. The conditionals must be tracked for determining |
| | | 730 | // the end of the scope for the SUBST_CLASSES IDs. |
| | | 731 | func (s *Suite) Test_substScope__conditionals(c *check.C) { |
| | | 732 | t := s.Init(c) |
| | | 733 | |
| | | 734 | ctx := NewSubstContext() |
| | | 735 | |
| | | 736 | line := func(text string) { |
| | | 737 | mkline := t.NewMkLine("filename.mk", 123, text) |
| | | 738 | ctx.Process(mkline) |
| | | 739 | } |
| | | 740 | verifyScopes := func(n int) { |
| | | 741 | t.CheckEquals(len(ctx.scopes), n) |
| | | 742 | } |
| | | 743 | |
| | | 744 | verifyScopes(1) |
| | | 745 | |
| | | 746 | line(".if 1") |
| | | 747 | verifyScopes(2) |
| | | 748 | |
| | | 749 | line(". if 1") |
| | | 750 | verifyScopes(3) |
| | | 751 | |
| | | 752 | line(". if 1") |
| | | 753 | verifyScopes(4) |
| | | 754 | |
| | | 755 | line(". elif 1") |
| | | 756 | verifyScopes(4) |
| | | 757 | |
| | | 758 | line(". else") |
| | | 759 | verifyScopes(4) |
| | | 760 | |
| | | 761 | line(". endif") |
| | | 762 | verifyScopes(3) |
| | | 763 | |
| | | 764 | line(". endif") |
| | | 765 | verifyScopes(2) |
| | | 766 | |
| | | 767 | line(".endif") |
| | | 768 | verifyScopes(1) |
| | | 769 | |
| | | 770 | // An unbalanced .endif must not lead to a panic. |
| | | 771 | line(".endif") |
| | | 772 | verifyScopes(1) |
| | | 773 | |
| | | 774 | ctx.Finish(NewLineEOF("filename.mk")) |
| | | 775 | } |
| | | 776 | |
| | | 777 | func (s *Suite) Test_substScope_prepareSubstClasses(c *check.C) { |
| | | 778 | t := s.Init(c) |
464 | | | 779 | |
| | | 780 | t.RunSubst( |
| | | 781 | "SUBST_CLASSES+= 1", |
| | | 782 | "SUBST_STAGE.1= post-configure", |
| | | 783 | ".if 0") |
| | | 784 | |
| | | 785 | // There's no need to warn about unbalanced conditionals |
| | | 786 | // since that is already done by MkLines.Check. |
465 | t.CheckOutputLines( | | 787 | t.CheckOutputLines( |
466 | "WARN: filename.mk:2: Before defining SUBST_VARS.one, "+ | | 788 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.1 missing.", |
467 | "the SUBST class should be declared using \"SUBST_CLASSES+= one\".", | | 789 | "WARN: filename.mk:EOF: Incomplete SUBST block: "+ |
468 | // In filename.mk:5 there is a proper rationale, thus no warning. | | 790 | "SUBST_SED.1, SUBST_VARS.1 or SUBST_FILTER_CMD.1 missing.") |
469 | "WARN: filename.mk:8: Before defining SUBST_VARS.def, "+ | | 791 | } |
470 | "the SUBST class should be declared using \"SUBST_CLASSES+= def\".", | | 792 | |
471 | "WARN: filename.mk:11: Before defining SUBST_SED.libs, "+ | | 793 | func (s *Suite) Test_substScope_prepareSubstClasses__nested(c *check.C) { |
472 | "the SUBST class should be declared using \"SUBST_CLASSES+= libs\".") | | 794 | t := s.Init(c) |
| | | 795 | |
| | | 796 | t.RunSubst( |
| | | 797 | "SUBST_CLASSES+= 1", |
| | | 798 | "SUBST_STAGE.1= post-configure", |
| | | 799 | ".if 0", |
| | | 800 | ".if 0", |
| | | 801 | "SUBST_CLASSES+= 2") |
| | | 802 | |
| | | 803 | t.CheckOutputLines( |
| | | 804 | "WARN: filename.mk:5: Subst block \"1\" should be finished "+ |
| | | 805 | "before adding the next class to SUBST_CLASSES.", |
| | | 806 | "WARN: filename.mk:EOF: Missing SUBST block for \"2\".", |
| | | 807 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.1 missing.", |
| | | 808 | "WARN: filename.mk:EOF: Incomplete SUBST block: "+ |
| | | 809 | "SUBST_SED.1, SUBST_VARS.1 or SUBST_FILTER_CMD.1 missing.") |
| | | 810 | } |
| | | 811 | |
| | | 812 | func (s *Suite) Test_substBlock_varassignStage__do_patch(c *check.C) { |
| | | 813 | t := s.Init(c) |
| | | 814 | |
| | | 815 | t.RunSubst( |
| | | 816 | "SUBST_CLASSES+=\tos", |
| | | 817 | "SUBST_STAGE.os=\tdo-patch", |
| | | 818 | "SUBST_FILES.os=\tguess-os.h", |
| | | 819 | "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,") |
| | | 820 | |
| | | 821 | // No warning, since there is nothing to fix automatically. |
| | | 822 | // This case doesn't occur in practice anyway. |
| | | 823 | t.CheckOutputEmpty() |
473 | } | | 824 | } |
474 | | | 825 | |
475 | func (s *Suite) Test_SubstContext_varassignStage__pre_patch(c *check.C) { | | 826 | func (s *Suite) Test_substBlock_varassignStage__pre_patch(c *check.C) { |
476 | t := s.Init(c) | | 827 | t := s.Init(c) |
477 | | | 828 | |
478 | t.Chdir(".") | | 829 | t.Chdir(".") |
479 | | | 830 | |
480 | doTest := t.NewSubstAutofixTest( | | 831 | doTest := t.NewSubstAutofixTest( |
481 | "SUBST_CLASSES+=\tos", | | 832 | "SUBST_CLASSES+=\tos", |
482 | "SUBST_STAGE.os=\tpre-patch", | | 833 | "SUBST_STAGE.os=\tpre-patch", |
483 | "SUBST_FILES.os=\tguess-os.h", | | 834 | "SUBST_FILES.os=\tguess-os.h", |
484 | "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,") | | 835 | "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,") |
485 | | | 836 | |
486 | t.ExpectDiagnosticsAutofix( | | 837 | t.ExpectDiagnosticsAutofix( |
487 | doTest, | | 838 | doTest, |
488 | "WARN: filename.mk:2: Substitutions should not happen in the patch phase.", | | 839 | "WARN: filename.mk:2: Substitutions should not happen in the patch phase.", |
489 | "AUTOFIX: filename.mk:2: Replacing \"pre-patch\" with \"post-extract\".") | | 840 | "AUTOFIX: filename.mk:2: Replacing \"pre-patch\" with \"post-extract\".") |
490 | | | 841 | |
491 | t.CheckFileLinesDetab("filename.mk", | | 842 | t.CheckFileLinesDetab("filename.mk", |
492 | "SUBST_CLASSES+= os", | | 843 | "SUBST_CLASSES+= os", |
493 | "SUBST_STAGE.os= post-extract", | | 844 | "SUBST_STAGE.os= post-extract", |
494 | "SUBST_FILES.os= guess-os.h", | | 845 | "SUBST_FILES.os= guess-os.h", |
495 | "SUBST_SED.os= -e s,@OPSYS@,Darwin,") | | 846 | "SUBST_SED.os= -e s,@OPSYS@,Darwin,") |
496 | } | | 847 | } |
497 | | | 848 | |
498 | func (s *Suite) Test_SubstContext_varassignStage__post_patch(c *check.C) { | | 849 | func (s *Suite) Test_substBlock_varassignStage__post_patch(c *check.C) { |
499 | t := s.Init(c) | | 850 | t := s.Init(c) |
500 | | | 851 | |
501 | t.Chdir(".") | | 852 | t.Chdir(".") |
502 | | | 853 | |
503 | doTest := t.NewSubstAutofixTest( | | 854 | doTest := t.NewSubstAutofixTest( |
504 | "SUBST_CLASSES+=\tos", | | 855 | "SUBST_CLASSES+=\tos", |
505 | "SUBST_STAGE.os=\tpost-patch", | | 856 | "SUBST_STAGE.os=\tpost-patch", |
506 | "SUBST_FILES.os=\tguess-os.h", | | 857 | "SUBST_FILES.os=\tguess-os.h", |
507 | "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,") | | 858 | "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,") |
508 | | | 859 | |
509 | t.ExpectDiagnosticsAutofix( | | 860 | t.ExpectDiagnosticsAutofix( |
510 | doTest, | | 861 | doTest, |
511 | "WARN: filename.mk:2: Substitutions should not happen in the patch phase.", | | 862 | "WARN: filename.mk:2: Substitutions should not happen in the patch phase.", |
512 | "AUTOFIX: filename.mk:2: Replacing \"post-patch\" with \"pre-configure\".") | | 863 | "AUTOFIX: filename.mk:2: Replacing \"post-patch\" with \"pre-configure\".") |
513 | | | 864 | |
514 | t.CheckFileLinesDetab("filename.mk", | | 865 | t.CheckFileLinesDetab("filename.mk", |
515 | "SUBST_CLASSES+= os", | | 866 | "SUBST_CLASSES+= os", |
516 | "SUBST_STAGE.os= pre-configure", | | 867 | "SUBST_STAGE.os= pre-configure", |
517 | "SUBST_FILES.os= guess-os.h", | | 868 | "SUBST_FILES.os= guess-os.h", |
518 | "SUBST_SED.os= -e s,@OPSYS@,Darwin,") | | 869 | "SUBST_SED.os= -e s,@OPSYS@,Darwin,") |
519 | } | | 870 | } |
520 | | | 871 | |
| | | 872 | func (s *Suite) Test_substBlock_varassignStage__empty_class(c *check.C) { |
| | | 873 | t := s.Init(c) |
| | | 874 | |
| | | 875 | t.Chdir(".") |
| | | 876 | |
| | | 877 | t.RunSubst( |
| | | 878 | "SUBST_CLASSES+= id", |
| | | 879 | "", |
| | | 880 | "SUBST_STAGE.= post-patch", |
| | | 881 | "SUBST_STAGE.id= post-configure", |
| | | 882 | "SUBST_FILES.id= files", |
| | | 883 | "SUBST_VARS.id= VAR", |
| | | 884 | "SUBST_VARS.= VAR") |
| | | 885 | |
| | | 886 | t.CheckOutputLines( |
| | | 887 | "ERROR: filename.mk:3: Invalid SUBST class \"\" in variable name.", |
| | | 888 | "ERROR: filename.mk:7: Invalid SUBST class \"\" in variable name.") |
| | | 889 | } |
| | | 890 | |
521 | // As of December 2019, pkglint does not use token positions internally. | | 891 | // As of December 2019, pkglint does not use token positions internally. |
522 | // Instead it only does simple string replacement when autofixing things. | | 892 | // Instead it only does simple string replacement when autofixing things. |
523 | // To avoid damaging anything, replacements are only done if they are | | 893 | // To avoid damaging anything, replacements are only done if they are |
524 | // unambiguous. This is not the case here, since line 4 contains the | | 894 | // unambiguous. This is not the case here, since line 4 contains the |
525 | // string "pre-patch" twice. | | 895 | // string "pre-patch" twice. |
526 | func (s *Suite) Test_SubstContext_varassignStage__ambiguous_replacement(c *check.C) { | | 896 | func (s *Suite) Test_substBlock_varassignStage__ambiguous_replacement(c *check.C) { |
527 | t := s.Init(c) | | 897 | t := s.Init(c) |
528 | | | 898 | |
529 | t.Chdir(".") | | 899 | t.Chdir(".") |
530 | | | 900 | |
531 | doTest := t.NewSubstAutofixTest( | | 901 | doTest := t.NewSubstAutofixTest( |
532 | "SUBST_CLASSES+= pre-patch", | | 902 | "SUBST_CLASSES+= pre-patch", |
533 | "SUBST_STAGE.pre-patch= pre-patch", | | 903 | "SUBST_STAGE.pre-patch= pre-patch", |
534 | "SUBST_FILES.pre-patch= files", | | 904 | "SUBST_FILES.pre-patch= files", |
535 | "SUBST_VARS.pre-patch= VARNAME") | | 905 | "SUBST_VARS.pre-patch= VARNAME") |
536 | | | 906 | |
537 | t.ExpectDiagnosticsAutofix( | | 907 | t.ExpectDiagnosticsAutofix( |
538 | doTest, | | 908 | doTest, |
539 | "WARN: filename.mk:2: Substitutions should not happen in the patch phase.") | | 909 | "WARN: filename.mk:2: Substitutions should not happen in the patch phase.") |
540 | | | 910 | |
541 | t.CheckEquals(t.File("filename.mk").IsFile(), false) | | 911 | t.CheckEquals(t.File("filename.mk").IsFile(), false) |
542 | } | | 912 | } |
543 | | | 913 | |
544 | func (s *Suite) Test_SubstContext_varassignStage__with_NO_CONFIGURE(c *check.C) { | | 914 | func (s *Suite) Test_substBlock_varassignStage__with_NO_CONFIGURE(c *check.C) { |
545 | t := s.Init(c) | | 915 | t := s.Init(c) |
546 | | | 916 | |
547 | pkg := t.SetUpPackage("category/package", | | 917 | pkg := t.SetUpPackage("category/package", |
548 | "SUBST_CLASSES+=\t\tpre", | | 918 | "SUBST_CLASSES+=\t\tpre", |
549 | "SUBST_STAGE.pre=\tpre-configure", | | 919 | "SUBST_STAGE.pre=\tpre-configure", |
550 | "SUBST_FILES.pre=\tguess-os.h", | | 920 | "SUBST_FILES.pre=\tguess-os.h", |
551 | "SUBST_SED.pre=\t\t-e s,@OPSYS@,Darwin,", | | 921 | "SUBST_SED.pre=\t\t-e s,@OPSYS@,Darwin,", |
552 | "", | | 922 | "", |
553 | "SUBST_CLASSES+=\t\tpost", | | 923 | "SUBST_CLASSES+=\t\tpost", |
554 | "SUBST_STAGE.post=\tpost-configure", | | 924 | "SUBST_STAGE.post=\tpost-configure", |
555 | "SUBST_FILES.post=\tguess-os.h", | | 925 | "SUBST_FILES.post=\tguess-os.h", |
556 | "SUBST_SED.post=\t\t-e s,@OPSYS@,Darwin,", | | 926 | "SUBST_SED.post=\t\t-e s,@OPSYS@,Darwin,", |
557 | "", | | 927 | "", |
| @@ -562,83 +932,79 @@ func (s *Suite) Test_SubstContext_varass | | | @@ -562,83 +932,79 @@ func (s *Suite) Test_SubstContext_varass |
562 | "", | | 932 | "", |
563 | "NO_CONFIGURE=\tyes") | | 933 | "NO_CONFIGURE=\tyes") |
564 | t.FinishSetUp() | | 934 | t.FinishSetUp() |
565 | | | 935 | |
566 | G.Check(pkg) | | 936 | G.Check(pkg) |
567 | | | 937 | |
568 | t.CheckOutputLines( | | 938 | t.CheckOutputLines( |
569 | "WARN: ~/category/package/Makefile:21: SUBST_STAGE pre-configure has no effect "+ | | 939 | "WARN: ~/category/package/Makefile:21: SUBST_STAGE pre-configure has no effect "+ |
570 | "when NO_CONFIGURE is set (in line 35).", | | 940 | "when NO_CONFIGURE is set (in line 35).", |
571 | "WARN: ~/category/package/Makefile:26: SUBST_STAGE post-configure has no effect "+ | | 941 | "WARN: ~/category/package/Makefile:26: SUBST_STAGE post-configure has no effect "+ |
572 | "when NO_CONFIGURE is set (in line 35).") | | 942 | "when NO_CONFIGURE is set (in line 35).") |
573 | } | | 943 | } |
574 | | | 944 | |
575 | func (s *Suite) Test_SubstContext_varassignStage__without_NO_CONFIGURE(c *check.C) { | | 945 | func (s *Suite) Test_substBlock_varassignStage__without_NO_CONFIGURE(c *check.C) { |
576 | t := s.Init(c) | | 946 | t := s.Init(c) |
577 | | | 947 | |
578 | pkg := t.SetUpPackage("category/package", | | 948 | pkg := t.SetUpPackage("category/package", |
579 | "SUBST_CLASSES+=\t\tpre", | | 949 | "SUBST_CLASSES+=\t\tpre", |
580 | "SUBST_STAGE.pre=\tpre-configure", | | 950 | "SUBST_STAGE.pre=\tpre-configure", |
581 | "SUBST_FILES.pre=\tguess-os.h", | | 951 | "SUBST_FILES.pre=\tguess-os.h", |
582 | "SUBST_SED.pre=\t\t-e s,@OPSYS@,Darwin,") | | 952 | "SUBST_SED.pre=\t\t-e s,@OPSYS@,Darwin,") |
583 | t.FinishSetUp() | | 953 | t.FinishSetUp() |
584 | | | 954 | |
585 | G.Check(pkg) | | 955 | G.Check(pkg) |
586 | | | 956 | |
587 | t.CheckOutputEmpty() | | 957 | t.CheckOutputEmpty() |
588 | } | | 958 | } |
589 | | | 959 | |
590 | // Before 2019-12-12, pkglint wrongly warned about variables that were | | 960 | // Before 2019-12-12, pkglint wrongly warned about variables that were |
591 | // not obviously SUBST variables, even if they were used later in SUBST_VARS. | | 961 | // not obviously SUBST variables, even if they were used later in SUBST_VARS. |
592 | func (s *Suite) Test_SubstContext_varassignVars__var_before_SUBST_VARS(c *check.C) { | | 962 | func (s *Suite) Test_substBlock_varassignVars__var_before_SUBST_VARS(c *check.C) { |
593 | t := s.Init(c) | | 963 | t := s.Init(c) |
594 | | | 964 | |
595 | t.RunSubst( | | 965 | t.RunSubst( |
596 | "SUBST_CLASSES+= id", | | 966 | "SUBST_CLASSES+= id", |
597 | "SUBST_STAGE.id= post-configure", | | 967 | "SUBST_STAGE.id= post-configure", |
598 | "SUBST_FILES.id= files", | | 968 | "SUBST_FILES.id= files", |
599 | "FOREIGN= not mentioned in SUBST_VARS", | | 969 | "FOREIGN= not mentioned in SUBST_VARS", |
600 | "VAR= ok", | | 970 | "VAR= ok", |
601 | "SUBST_VARS.id= VAR", | | 971 | "SUBST_VARS.id= VAR", |
602 | "", | | 972 | "", |
603 | // This second block makes sure that the list of foreign variables | | 973 | // This second block makes sure that the list of foreign variables |
604 | // is properly reset at the end of a SUBST block. | | 974 | // is properly reset at the end of a SUBST block. |
605 | // If it weren't, there would be additional warnings. | | 975 | // If it weren't, there would be additional warnings. |
606 | "SUBST_CLASSES+= 2", | | 976 | "SUBST_CLASSES+= 2", |
607 | "SUBST_STAGE.2= post-configure", | | 977 | "SUBST_STAGE.2= post-configure", |
608 | "SUBST_FILES.2= files", | | 978 | "SUBST_FILES.2= files", |
609 | "SUBST_VARS.2= OTHER") | | 979 | "SUBST_VARS.2= OTHER") |
610 | | | 980 | |
611 | t.CheckOutputLines( | | 981 | t.CheckOutputLines( |
612 | "WARN: filename.mk:4: Foreign variable \"FOREIGN\" in SUBST block.") | | 982 | "WARN: filename.mk:4: Foreign variable \"FOREIGN\" in SUBST block.") |
613 | } | | 983 | } |
614 | | | 984 | |
615 | func (s *Suite) Test_SubstContext_dupList__conditional_before_unconditional(c *check.C) { | | 985 | func (s *Suite) Test_substBlock_varassignVars(c *check.C) { |
616 | t := s.Init(c) | | 986 | t := s.Init(c) |
617 | | | 987 | |
618 | t.RunSubst( | | 988 | t.RunSubst( |
619 | "SUBST_CLASSES+= os", | | 989 | "SUBST_CLASSES+=\tos", |
620 | "SUBST_STAGE.os= post-configure", | | 990 | "SUBST_STAGE.os=\tpre-configure", |
621 | ".if 1", | | 991 | "SUBST_FILES.os=\tguess-os.h", |
622 | "SUBST_FILES.os= conditional", | | 992 | "SUBST_VARS.os=\tPREFIX VARBASE") |
623 | ".endif", | | | |
624 | "SUBST_FILES.os= unconditional", | | | |
625 | "SUBST_VARS.os= OPSYS") | | | |
626 | | | 993 | |
627 | // TODO: Warn that the conditional line is overwritten. | | | |
628 | t.CheckOutputEmpty() | | 994 | t.CheckOutputEmpty() |
629 | } | | 995 | } |
630 | | | 996 | |
631 | func (s *Suite) Test_SubstContext_suggestSubstVars(c *check.C) { | | 997 | func (s *Suite) Test_substBlock_suggestSubstVars(c *check.C) { |
632 | t := s.Init(c) | | 998 | t := s.Init(c) |
633 | | | 999 | |
634 | t.Chdir(".") | | 1000 | t.Chdir(".") |
635 | | | 1001 | |
636 | test := func(line string, diagnostics ...string) { | | 1002 | test := func(line string, diagnostics ...string) { |
637 | doTest := t.NewSubstAutofixTest( | | 1003 | doTest := t.NewSubstAutofixTest( |
638 | "SUBST_CLASSES+=\t\ttest", | | 1004 | "SUBST_CLASSES+=\t\ttest", |
639 | "SUBST_STAGE.test=\tpre-configure", | | 1005 | "SUBST_STAGE.test=\tpre-configure", |
640 | "SUBST_FILES.test=\tfilename", | | 1006 | "SUBST_FILES.test=\tfilename", |
641 | line) | | 1007 | line) |
642 | | | 1008 | |
643 | t.ExpectDiagnosticsAutofix( | | 1009 | t.ExpectDiagnosticsAutofix( |
644 | doTest, | | 1010 | doTest, |
| @@ -742,27 +1108,27 @@ func (s *Suite) Test_SubstContext_sugges | | | @@ -742,27 +1108,27 @@ func (s *Suite) Test_SubstContext_sugges |
742 | "NOTE: filename.mk:4: The substitution command \"s,@SH@,${SH:Q},\" "+ | | 1108 | "NOTE: filename.mk:4: The substitution command \"s,@SH@,${SH:Q},\" "+ |
743 | "can be replaced with \"SUBST_VARS.test= SH\".") | | 1109 | "can be replaced with \"SUBST_VARS.test= SH\".") |
744 | | | 1110 | |
745 | // Just a note; not fixed because of the -n. | | 1111 | // Just a note; not fixed because of the -n. |
746 | test( | | 1112 | test( |
747 | "SUBST_SED.test+=\t-n s,@SH@,${SH:Q},", | | 1113 | "SUBST_SED.test+=\t-n s,@SH@,${SH:Q},", |
748 | | | 1114 | |
749 | "NOTE: filename.mk:4: The substitution command \"s,@SH@,${SH:Q},\" "+ | | 1115 | "NOTE: filename.mk:4: The substitution command \"s,@SH@,${SH:Q},\" "+ |
750 | "can be replaced with \"SUBST_VARS.test= SH\".") | | 1116 | "can be replaced with \"SUBST_VARS.test= SH\".") |
751 | } | | 1117 | } |
752 | | | 1118 | |
753 | // If the SUBST_CLASS identifier ends with a plus, the generated code must | | 1119 | // If the SUBST_CLASS identifier ends with a plus, the generated code must |
754 | // use the correct assignment operator and be nicely formatted. | | 1120 | // use the correct assignment operator and be nicely formatted. |
755 | func (s *Suite) Test_SubstContext_suggestSubstVars__plus(c *check.C) { | | 1121 | func (s *Suite) Test_substBlock_suggestSubstVars__plus(c *check.C) { |
756 | t := s.Init(c) | | 1122 | t := s.Init(c) |
757 | | | 1123 | |
758 | t.Chdir(".") | | 1124 | t.Chdir(".") |
759 | | | 1125 | |
760 | doTest := t.NewSubstAutofixTest( | | 1126 | doTest := t.NewSubstAutofixTest( |
761 | "SUBST_CLASSES+=\t\tgtk+", | | 1127 | "SUBST_CLASSES+=\t\tgtk+", |
762 | "SUBST_STAGE.gtk+ =\tpre-configure", | | 1128 | "SUBST_STAGE.gtk+ =\tpre-configure", |
763 | "SUBST_FILES.gtk+ =\tfilename", | | 1129 | "SUBST_FILES.gtk+ =\tfilename", |
764 | "SUBST_SED.gtk+ +=\t-e s,@SH@,${SH:Q},g", | | 1130 | "SUBST_SED.gtk+ +=\t-e s,@SH@,${SH:Q},g", |
765 | "SUBST_SED.gtk+ +=\t-e s,@SH@,${SH:Q},g") | | 1131 | "SUBST_SED.gtk+ +=\t-e s,@SH@,${SH:Q},g") |
766 | | | 1132 | |
767 | t.ExpectDiagnosticsAutofix( | | 1133 | t.ExpectDiagnosticsAutofix( |
768 | doTest, | | 1134 | doTest, |
| @@ -776,27 +1142,27 @@ func (s *Suite) Test_SubstContext_sugges | | | @@ -776,27 +1142,27 @@ func (s *Suite) Test_SubstContext_sugges |
776 | "with \"SUBST_VARS.gtk+ +=\\tSH\".") | | 1142 | "with \"SUBST_VARS.gtk+ +=\\tSH\".") |
777 | | | 1143 | |
778 | t.CheckFileLinesDetab("filename.mk", | | 1144 | t.CheckFileLinesDetab("filename.mk", |
779 | "SUBST_CLASSES+= gtk+", | | 1145 | "SUBST_CLASSES+= gtk+", |
780 | "SUBST_STAGE.gtk+ = pre-configure", | | 1146 | "SUBST_STAGE.gtk+ = pre-configure", |
781 | "SUBST_FILES.gtk+ = filename", | | 1147 | "SUBST_FILES.gtk+ = filename", |
782 | "SUBST_VARS.gtk+ = SH", | | 1148 | "SUBST_VARS.gtk+ = SH", |
783 | "SUBST_VARS.gtk+ += SH") | | 1149 | "SUBST_VARS.gtk+ += SH") |
784 | } | | 1150 | } |
785 | | | 1151 | |
786 | // The last of the SUBST_SED variables is 15 characters wide. When SUBST_SED | | 1152 | // The last of the SUBST_SED variables is 15 characters wide. When SUBST_SED |
787 | // is replaced with SUBST_VARS, this becomes 16 characters and therefore | | 1153 | // is replaced with SUBST_VARS, this becomes 16 characters and therefore |
788 | // requires the whole paragraph to be indented by one more tab. | | 1154 | // requires the whole paragraph to be indented by one more tab. |
789 | func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_realign_paragraph(c *check.C) { | | 1155 | func (s *Suite) Test_substBlock_suggestSubstVars__autofix_realign_paragraph(c *check.C) { |
790 | t := s.Init(c) | | 1156 | t := s.Init(c) |
791 | | | 1157 | |
792 | t.Chdir(".") | | 1158 | t.Chdir(".") |
793 | | | 1159 | |
794 | doTest := t.NewSubstAutofixTest( | | 1160 | doTest := t.NewSubstAutofixTest( |
795 | "SUBST_CLASSES+=\t\tpfx", | | 1161 | "SUBST_CLASSES+=\t\tpfx", |
796 | "SUBST_STAGE.pfx=\tpre-configure", | | 1162 | "SUBST_STAGE.pfx=\tpre-configure", |
797 | "SUBST_FILES.pfx=\tfilename", | | 1163 | "SUBST_FILES.pfx=\tfilename", |
798 | "SUBST_SED.pfx=\t\t-e s,@PREFIX@,${PREFIX},g", | | 1164 | "SUBST_SED.pfx=\t\t-e s,@PREFIX@,${PREFIX},g", |
799 | "SUBST_SED.pfx+=\t\t-e s,@PREFIX@,${PREFIX},g") | | 1165 | "SUBST_SED.pfx+=\t\t-e s,@PREFIX@,${PREFIX},g") |
800 | | | 1166 | |
801 | t.ExpectDiagnosticsAutofix( | | 1167 | t.ExpectDiagnosticsAutofix( |
802 | doTest, | | 1168 | doTest, |
| @@ -808,27 +1174,27 @@ func (s *Suite) Test_SubstContext_sugges | | | @@ -808,27 +1174,27 @@ func (s *Suite) Test_SubstContext_sugges |
808 | "AUTOFIX: filename.mk:4: Replacing \"SUBST_SED.pfx=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ | | 1174 | "AUTOFIX: filename.mk:4: Replacing \"SUBST_SED.pfx=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ |
809 | "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".", | | 1175 | "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".", |
810 | "AUTOFIX: filename.mk:5: Replacing \"SUBST_SED.pfx+=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ | | 1176 | "AUTOFIX: filename.mk:5: Replacing \"SUBST_SED.pfx+=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ |
811 | "with \"SUBST_VARS.pfx+=\\tPREFIX\".") | | 1177 | "with \"SUBST_VARS.pfx+=\\tPREFIX\".") |
812 | | | 1178 | |
813 | t.CheckFileLinesDetab("filename.mk", | | 1179 | t.CheckFileLinesDetab("filename.mk", |
814 | "SUBST_CLASSES+= pfx", | | 1180 | "SUBST_CLASSES+= pfx", |
815 | "SUBST_STAGE.pfx= pre-configure", | | 1181 | "SUBST_STAGE.pfx= pre-configure", |
816 | "SUBST_FILES.pfx= filename", | | 1182 | "SUBST_FILES.pfx= filename", |
817 | "SUBST_VARS.pfx= PREFIX", | | 1183 | "SUBST_VARS.pfx= PREFIX", |
818 | "SUBST_VARS.pfx+= PREFIX") | | 1184 | "SUBST_VARS.pfx+= PREFIX") |
819 | } | | 1185 | } |
820 | | | 1186 | |
821 | func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_plus_sed(c *check.C) { | | 1187 | func (s *Suite) Test_substBlock_suggestSubstVars__autofix_plus_sed(c *check.C) { |
822 | t := s.Init(c) | | 1188 | t := s.Init(c) |
823 | | | 1189 | |
824 | t.Chdir(".") | | 1190 | t.Chdir(".") |
825 | | | 1191 | |
826 | doTest := t.NewSubstAutofixTest( | | 1192 | doTest := t.NewSubstAutofixTest( |
827 | "SUBST_CLASSES+=\t\tpfx", | | 1193 | "SUBST_CLASSES+=\t\tpfx", |
828 | "SUBST_STAGE.pfx=\tpre-configure", | | 1194 | "SUBST_STAGE.pfx=\tpre-configure", |
829 | "SUBST_FILES.pfx=\tfilename", | | 1195 | "SUBST_FILES.pfx=\tfilename", |
830 | "SUBST_SED.pfx=\t\t-e s,@PREFIX@,${PREFIX},g", | | 1196 | "SUBST_SED.pfx=\t\t-e s,@PREFIX@,${PREFIX},g", |
831 | "SUBST_SED.pfx+=\t\t-e s,@PREFIX@,other,g") | | 1197 | "SUBST_SED.pfx+=\t\t-e s,@PREFIX@,other,g") |
832 | | | 1198 | |
833 | t.ExpectDiagnosticsAutofix( | | 1199 | t.ExpectDiagnosticsAutofix( |
834 | doTest, | | 1200 | doTest, |
| @@ -839,27 +1205,27 @@ func (s *Suite) Test_SubstContext_sugges | | | @@ -839,27 +1205,27 @@ func (s *Suite) Test_SubstContext_sugges |
839 | "Replacing \"SUBST_SED.pfx=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ | | 1205 | "Replacing \"SUBST_SED.pfx=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ |
840 | "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".") | | 1206 | "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".") |
841 | | | 1207 | |
842 | t.CheckFileLinesDetab("filename.mk", | | 1208 | t.CheckFileLinesDetab("filename.mk", |
843 | "SUBST_CLASSES+= pfx", | | 1209 | "SUBST_CLASSES+= pfx", |
844 | "SUBST_STAGE.pfx= pre-configure", | | 1210 | "SUBST_STAGE.pfx= pre-configure", |
845 | "SUBST_FILES.pfx= filename", | | 1211 | "SUBST_FILES.pfx= filename", |
846 | "SUBST_VARS.pfx= PREFIX", | | 1212 | "SUBST_VARS.pfx= PREFIX", |
847 | // Since the SUBST_SED that was previously here used the = operator, | | 1213 | // Since the SUBST_SED that was previously here used the = operator, |
848 | // this += might be replaced with a simple =. | | 1214 | // this += might be replaced with a simple =. |
849 | "SUBST_SED.pfx+= -e s,@PREFIX@,other,g") | | 1215 | "SUBST_SED.pfx+= -e s,@PREFIX@,other,g") |
850 | } | | 1216 | } |
851 | | | 1217 | |
852 | func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_plus_vars(c *check.C) { | | 1218 | func (s *Suite) Test_substBlock_suggestSubstVars__autofix_plus_vars(c *check.C) { |
853 | t := s.Init(c) | | 1219 | t := s.Init(c) |
854 | | | 1220 | |
855 | t.Chdir(".") | | 1221 | t.Chdir(".") |
856 | | | 1222 | |
857 | doTest := t.NewSubstAutofixTest( | | 1223 | doTest := t.NewSubstAutofixTest( |
858 | "SUBST_CLASSES+=\tid", | | 1224 | "SUBST_CLASSES+=\tid", |
859 | "SUBST_STAGE.id=\tpre-configure", | | 1225 | "SUBST_STAGE.id=\tpre-configure", |
860 | "SUBST_FILES.id=\tfilename", | | 1226 | "SUBST_FILES.id=\tfilename", |
861 | "SUBST_SED.id=\t-e s,@PREFIX@,${PREFIX},g", | | 1227 | "SUBST_SED.id=\t-e s,@PREFIX@,${PREFIX},g", |
862 | "SUBST_VARS.id=\tPKGMANDIR") | | 1228 | "SUBST_VARS.id=\tPKGMANDIR") |
863 | | | 1229 | |
864 | t.ExpectDiagnosticsAutofix( | | 1230 | t.ExpectDiagnosticsAutofix( |
865 | doTest, | | 1231 | doTest, |
| @@ -871,27 +1237,27 @@ func (s *Suite) Test_SubstContext_sugges | | | @@ -871,27 +1237,27 @@ func (s *Suite) Test_SubstContext_sugges |
871 | "with \"SUBST_VARS.id=\\tPREFIX\".", | | 1237 | "with \"SUBST_VARS.id=\\tPREFIX\".", |
872 | "AUTOFIX: filename.mk:5: "+ | | 1238 | "AUTOFIX: filename.mk:5: "+ |
873 | "Replacing \"SUBST_VARS.id=\\t\" "+ | | 1239 | "Replacing \"SUBST_VARS.id=\\t\" "+ |
874 | "with \"SUBST_VARS.id+=\\t\".") | | 1240 | "with \"SUBST_VARS.id+=\\t\".") |
875 | | | 1241 | |
876 | t.CheckFileLinesDetab("filename.mk", | | 1242 | t.CheckFileLinesDetab("filename.mk", |
877 | "SUBST_CLASSES+= id", | | 1243 | "SUBST_CLASSES+= id", |
878 | "SUBST_STAGE.id= pre-configure", | | 1244 | "SUBST_STAGE.id= pre-configure", |
879 | "SUBST_FILES.id= filename", | | 1245 | "SUBST_FILES.id= filename", |
880 | "SUBST_VARS.id= PREFIX", | | 1246 | "SUBST_VARS.id= PREFIX", |
881 | "SUBST_VARS.id+= PKGMANDIR") | | 1247 | "SUBST_VARS.id+= PKGMANDIR") |
882 | } | | 1248 | } |
883 | | | 1249 | |
884 | func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_indentation(c *check.C) { | | 1250 | func (s *Suite) Test_substBlock_suggestSubstVars__autofix_indentation(c *check.C) { |
885 | t := s.Init(c) | | 1251 | t := s.Init(c) |
886 | | | 1252 | |
887 | t.Chdir(".") | | 1253 | t.Chdir(".") |
888 | | | 1254 | |
889 | doTest := t.NewSubstAutofixTest( | | 1255 | doTest := t.NewSubstAutofixTest( |
890 | "SUBST_CLASSES+=\t\t\tfix-paths", | | 1256 | "SUBST_CLASSES+=\t\t\tfix-paths", |
891 | "SUBST_STAGE.fix-paths=\t\tpre-configure", | | 1257 | "SUBST_STAGE.fix-paths=\t\tpre-configure", |
892 | "SUBST_MESSAGE.fix-paths=\tMessage", | | 1258 | "SUBST_MESSAGE.fix-paths=\tMessage", |
893 | "SUBST_FILES.fix-paths=\t\tfilename", | | 1259 | "SUBST_FILES.fix-paths=\t\tfilename", |
894 | "SUBST_SED.fix-paths=\t\t-e s,@PREFIX@,${PREFIX},g") | | 1260 | "SUBST_SED.fix-paths=\t\t-e s,@PREFIX@,${PREFIX},g") |
895 | | | 1261 | |
896 | t.ExpectDiagnosticsAutofix( | | 1262 | t.ExpectDiagnosticsAutofix( |
897 | doTest, | | 1263 | doTest, |
| @@ -901,454 +1267,286 @@ func (s *Suite) Test_SubstContext_sugges | | | @@ -901,454 +1267,286 @@ func (s *Suite) Test_SubstContext_sugges |
901 | "can be replaced with \"SUBST_VARS.fix-paths= PREFIX\".", | | 1267 | "can be replaced with \"SUBST_VARS.fix-paths= PREFIX\".", |
902 | "AUTOFIX: filename.mk:5: Replacing "+ | | 1268 | "AUTOFIX: filename.mk:5: Replacing "+ |
903 | "\"SUBST_SED.fix-paths=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ | | 1269 | "\"SUBST_SED.fix-paths=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ |
904 | "with \"SUBST_VARS.fix-paths=\\t\\tPREFIX\".") | | 1270 | "with \"SUBST_VARS.fix-paths=\\t\\tPREFIX\".") |
905 | | | 1271 | |
906 | t.CheckFileLinesDetab("filename.mk", | | 1272 | t.CheckFileLinesDetab("filename.mk", |
907 | "SUBST_CLASSES+= fix-paths", | | 1273 | "SUBST_CLASSES+= fix-paths", |
908 | "SUBST_STAGE.fix-paths= pre-configure", | | 1274 | "SUBST_STAGE.fix-paths= pre-configure", |
909 | "SUBST_MESSAGE.fix-paths= Message", | | 1275 | "SUBST_MESSAGE.fix-paths= Message", |
910 | "SUBST_FILES.fix-paths= filename", | | 1276 | "SUBST_FILES.fix-paths= filename", |
911 | "SUBST_VARS.fix-paths= PREFIX") | | 1277 | "SUBST_VARS.fix-paths= PREFIX") |
912 | } | | 1278 | } |
913 | | | 1279 | |
914 | func (s *Suite) Test_SubstContext_suggestSubstVars__conditional(c *check.C) { | | 1280 | func (s *Suite) Test_substBlock_suggestSubstVars__conditional(c *check.C) { |
915 | t := s.Init(c) | | 1281 | t := s.Init(c) |
916 | | | 1282 | |
917 | t.Chdir(".") | | 1283 | t.Chdir(".") |
918 | | | 1284 | |
919 | doTest := t.NewSubstAutofixTest( | | 1285 | doTest := t.NewSubstAutofixTest( |
920 | "SUBST_CLASSES+= id", | | 1286 | "SUBST_CLASSES+= id", |
921 | "SUBST_STAGE.id= pre-configure", | | 1287 | "SUBST_STAGE.id= pre-configure", |
922 | "SUBST_FILES.id= files", | | 1288 | "SUBST_FILES.id= files", |
923 | "SUBST_SED.id= -e s,@VAR@,${VAR},", | | 1289 | "SUBST_SED.id= -e s,@VAR@,${VAR},", |
924 | ".if 1", | | 1290 | ".if 1", |
925 | "SUBST_SED.id+= -e s,@VAR2@,${VAR2},", | | 1291 | "SUBST_SED.id+= -e s,@VAR2@,${VAR2},", |
926 | ".endif") | | 1292 | ".endif") |
927 | | | 1293 | |
928 | t.ExpectDiagnosticsAutofix( | | 1294 | t.ExpectDiagnosticsAutofix( |
929 | doTest, | | 1295 | doTest, |
930 | | | 1296 | |
931 | "NOTE: filename.mk:4: The substitution command \"s,@VAR@,${VAR},\" "+ | | 1297 | "NOTE: filename.mk:4: The substitution command \"s,@VAR@,${VAR},\" "+ |
932 | "can be replaced with \"SUBST_VARS.id= VAR\".", | | 1298 | "can be replaced with \"SUBST_VARS.id= VAR\".", |
933 | "NOTE: filename.mk:6: The substitution command \"s,@VAR2@,${VAR2},\" "+ | | 1299 | "NOTE: filename.mk:6: The substitution command \"s,@VAR2@,${VAR2},\" "+ |
934 | "can be replaced with \"SUBST_VARS.id+= VAR2\".", | | 1300 | "can be replaced with \"SUBST_VARS.id+= VAR2\".", |
935 | "AUTOFIX: filename.mk:4: Replacing \"SUBST_SED.id= -e s,@VAR@,${VAR},\" "+ | | 1301 | "AUTOFIX: filename.mk:4: Replacing \"SUBST_SED.id= -e s,@VAR@,${VAR},\" "+ |
936 | "with \"SUBST_VARS.id=\\tVAR\".", | | 1302 | "with \"SUBST_VARS.id=\\tVAR\".", |
937 | "AUTOFIX: filename.mk:6: Replacing \"SUBST_SED.id+= -e s,@VAR2@,${VAR2},\" "+ | | 1303 | "AUTOFIX: filename.mk:6: Replacing \"SUBST_SED.id+= -e s,@VAR2@,${VAR2},\" "+ |
938 | "with \"SUBST_VARS.id+=\\tVAR2\".") | | 1304 | "with \"SUBST_VARS.id+=\\tVAR2\".") |
939 | | | 1305 | |
940 | t.CheckFileLinesDetab("filename.mk", | | 1306 | t.CheckFileLinesDetab("filename.mk", |
941 | "SUBST_CLASSES+= id", | | 1307 | "SUBST_CLASSES+= id", |
942 | "SUBST_STAGE.id= pre-configure", | | 1308 | "SUBST_STAGE.id= pre-configure", |
943 | "SUBST_FILES.id= files", | | 1309 | "SUBST_FILES.id= files", |
944 | "SUBST_VARS.id= VAR", | | 1310 | "SUBST_VARS.id= VAR", |
945 | ".if 1", | | 1311 | ".if 1", |
946 | "SUBST_VARS.id+= VAR2", | | 1312 | "SUBST_VARS.id+= VAR2", |
947 | ".endif") | | | |
948 | } | | | |
949 | | | | |
950 | func (s *Suite) Test_SubstContext_extractVarname(c *check.C) { | | | |
951 | t := s.Init(c) | | | |
952 | | | | |
953 | test := func(input, expected string) { | | | |
954 | t.CheckEquals((*SubstContext).extractVarname(nil, input), expected) | | | |
955 | } | | | |
956 | | | | |
957 | // A simple variable name. | | | |
958 | test("s,@VAR@,${VAR},", "VAR") | | | |
959 | | | | |
960 | // A parameterized variable name. | | | |
961 | test("s,@VAR.param@,${VAR.param},", "VAR.param") | | | |
962 | | | | |
963 | // Only substitution commands can be replaced with SUBST_VARS. | | | |
964 | test("/pattern/d", "") | | | |
965 | | | | |
966 | // An incomplete substitution command. | | | |
967 | test("s", "") | | | |
968 | | | | |
969 | // Wrong placeholder character, only @ works. | | | |
970 | test("s,!VAR!,${VAR},", "") | | | |
971 | | | | |
972 | // The placeholder must have exactly 1 @ on each side. | | | |
973 | test("s,@@VAR@@,${VAR},", "") | | | |
974 | | | | |
975 | // Malformed because the comma is the separator. | | | |
976 | test("s,@VAR,VAR@,${VAR},", "") | | | |
977 | | | | |
978 | // The replacement pattern is not a simple variable name enclosed in @. | | | |
979 | test("s,@VAR!VAR@,${VAR},", "") | | | |
980 | | | | |
981 | // The replacement may only contain the :Q modifier. | | | |
982 | test("s,@VAR@,${VAR:Mpattern},", "") | | | |
983 | | | | |
984 | // The :Q modifier is allowed in the replacement. | | | |
985 | test("s,@VAR@,${VAR:Q},", "VAR") | | | |
986 | | | | |
987 | // The replacement may contain the :Q modifier only once. | | | |
988 | test("s,@VAR@,${VAR:Q:Q},", "") | | | |
989 | | | | |
990 | // The replacement must be a plain variable expression, without prefix. | | | |
991 | test("s,@VAR@,prefix${VAR},", "") | | | |
992 | | | | |
993 | // The replacement must be a plain variable expression, without suffix. | | | |
994 | test("s,@VAR@,${VAR}suffix,", "") | | | |
995 | } | | | |
996 | | | | |
997 | func (s *Suite) Test_SubstContext_directive__before_SUBST_CLASSES(c *check.C) { | | | |
998 | t := s.Init(c) | | | |
999 | | | | |
1000 | t.RunSubst( | | | |
1001 | ".if 0", | | | |
1002 | ".endif", | | | |
1003 | "SUBST_CLASSES+=\tos", | | | |
1004 | ".elif 0") // Just for branch coverage. | | | |
1005 | | | | |
1006 | t.CheckOutputLines( | | | |
1007 | "WARN: filename.mk:EOF: Missing SUBST block for \"os\".") | | | |
1008 | } | | | |
1009 | | | | |
1010 | func (s *Suite) Test_SubstContext_directive__conditional_blocks_complete(c *check.C) { | | | |
1011 | t := s.Init(c) | | | |
1012 | | | | |
1013 | t.RunSubst( | | | |
1014 | ".if ${OPSYS} == NetBSD", | | | |
1015 | "SUBST_CLASSES+= nb", | | | |
1016 | "SUBST_STAGE.nb= post-configure", | | | |
1017 | "SUBST_FILES.nb= guess-netbsd.h", | | | |
1018 | "SUBST_VARS.nb= HAVE_NETBSD", | | | |
1019 | ".else", | | | |
1020 | "SUBST_CLASSES+= os", | | | |
1021 | "SUBST_STAGE.os= post-configure", | | | |
1022 | "SUBST_FILES.os= guess-netbsd.h", | | | |
1023 | "SUBST_VARS.os= HAVE_OTHER", | | | |
1024 | ".endif") | | | |
1025 | | | | |
1026 | t.CheckOutputEmpty() | | | |
1027 | } | | | |
1028 | | | | |
1029 | func (s *Suite) Test_SubstContext_directive__conditional_blocks_incomplete(c *check.C) { | | | |
1030 | t := s.Init(c) | | | |
1031 | | | | |
1032 | t.RunSubst( | | | |
1033 | ".if ${OPSYS} == NetBSD", | | | |
1034 | "SUBST_CLASSES+= nb", | | | |
1035 | "SUBST_STAGE.nb= post-configure", | | | |
1036 | "SUBST_VARS.nb= HAVE_NETBSD", | | | |
1037 | ".else", | | | |
1038 | "SUBST_CLASSES+= os", | | | |
1039 | "SUBST_STAGE.os= post-configure", | | | |
1040 | "SUBST_FILES.os= guess-netbsd.h", | | | |
1041 | ".endif") | | | |
1042 | | | | |
1043 | t.CheckOutputLines( | | | |
1044 | "WARN: filename.mk:5: Incomplete SUBST block: SUBST_FILES.nb missing.", | | | |
1045 | "WARN: filename.mk:9: Incomplete SUBST block: "+ | | | |
1046 | "SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.") | | | |
1047 | } | | | |
1048 | | | | |
1049 | func (s *Suite) Test_SubstContext_directive__conditional_complete(c *check.C) { | | | |
1050 | t := s.Init(c) | | | |
1051 | | | | |
1052 | t.RunSubst( | | | |
1053 | "SUBST_CLASSES+= id", | | | |
1054 | ".if ${OPSYS} == NetBSD", | | | |
1055 | "SUBST_STAGE.id=\t\tpost-configure", | | | |
1056 | "SUBST_MESSAGE.id=\tpost-configure", | | | |
1057 | "SUBST_FILES.id=\t\tguess-netbsd.h", | | | |
1058 | "SUBST_SED.id=\t\t-e s,from,to,", | | | |
1059 | "SUBST_VARS.id=\t\tHAVE_OTHER", | | | |
1060 | "SUBST_FILTER_CMD.id=\tHAVE_OTHER", | | | |
1061 | ".else", | | | |
1062 | "SUBST_STAGE.id=\t\tpost-configure", | | | |
1063 | "SUBST_MESSAGE.id=\tpost-configure", | | | |
1064 | "SUBST_FILES.id=\t\tguess-netbsd.h", | | | |
1065 | "SUBST_SED.id=\t\t-e s,from,to,", | | | |
1066 | "SUBST_VARS.id=\t\tHAVE_OTHER", | | | |
1067 | "SUBST_FILTER_CMD.id=\tHAVE_OTHER", | | | |
1068 | ".endif") | | | |
1069 | | | | |
1070 | t.CheckOutputLines( | | | |
1071 | "WARN: filename.mk:3: SUBST_STAGE.id should not be defined conditionally.", | | | |
1072 | "WARN: filename.mk:4: SUBST_MESSAGE.id should not be defined conditionally.", | | | |
1073 | "WARN: filename.mk:10: SUBST_STAGE.id should not be defined conditionally.", | | | |
1074 | "WARN: filename.mk:11: SUBST_MESSAGE.id should not be defined conditionally.") | | | |
1075 | } | | | |
1076 | | | | |
1077 | func (s *Suite) Test_SubstContext_directive__conditionally_overwritten_filter(c *check.C) { | | | |
1078 | t := s.Init(c) | | | |
1079 | | | | |
1080 | t.RunSubst( | | | |
1081 | "SUBST_CLASSES+= id", | | | |
1082 | "SUBST_STAGE.id=\t\tpost-configure", | | | |
1083 | "SUBST_MESSAGE.id=\tpost-configure", | | | |
1084 | "SUBST_FILES.id=\t\tguess-netbsd.h", | | | |
1085 | "SUBST_FILTER_CMD.id=\tHAVE_OTHER", | | | |
1086 | ".if ${OPSYS} == NetBSD", | | | |
1087 | "SUBST_FILTER_CMD.id=\tHAVE_OTHER", | | | |
1088 | ".endif") | | | |
1089 | | | | |
1090 | t.CheckOutputLines( | | | |
1091 | "WARN: filename.mk:7: Duplicate definition of \"SUBST_FILTER_CMD.id\".") | | | |
1092 | } | | | |
1093 | | | | |
1094 | // Hopefully nobody will ever trigger this case in real pkgsrc. | | | |
1095 | // It's plain confusing to a casual reader to nest a complete | | | |
1096 | // SUBST block into another SUBST block. | | | |
1097 | // That's why pkglint doesn't cover this case correctly. | | | |
1098 | func (s *Suite) Test_SubstContext_directive__conditionally_nested_block(c *check.C) { | | | |
1099 | t := s.Init(c) | | | |
1100 | | | | |
1101 | t.RunSubst( | | | |
1102 | "SUBST_CLASSES+= outer", | | | |
1103 | "SUBST_STAGE.outer= post-configure", | | | |
1104 | "SUBST_FILES.outer= outer.txt", | | | |
1105 | ".if ${OPSYS} == NetBSD", | | | |
1106 | "SUBST_CLASSES+= inner", | | | |
1107 | "SUBST_STAGE.inner= post-configure", | | | |
1108 | "SUBST_FILES.inner= inner.txt", | | | |
1109 | "SUBST_VARS.inner= INNER", | | | |
1110 | ".endif", | | | |
1111 | "SUBST_VARS.outer= OUTER") | | | |
1112 | | | | |
1113 | t.CheckOutputLines( | | | |
1114 | "WARN: filename.mk:5: Incomplete SUBST block: "+ | | | |
1115 | "SUBST_SED.outer, SUBST_VARS.outer or SUBST_FILTER_CMD.outer missing.", | | | |
1116 | "WARN: filename.mk:5: Subst block \"outer\" should be finished "+ | | | |
1117 | "before adding the next class to SUBST_CLASSES.", | | | |
1118 | "WARN: filename.mk:10: "+ | | | |
1119 | "Late additions to a SUBST variable should use the += operator.") | | | |
1120 | } | | | |
1121 | | | | |
1122 | // It's completely valid to have several SUBST blocks in a single paragraph. | | | |
1123 | // As soon as a SUBST_CLASSES line appears, pkglint assumes that all previous | | | |
1124 | // SUBST blocks are finished. That's exactly the case here. | | | |
1125 | func (s *Suite) Test_SubstContext_directive__conditionally_following_block(c *check.C) { | | | |
1126 | t := s.Init(c) | | | |
1127 | | | | |
1128 | t.RunSubst( | | | |
1129 | "SUBST_CLASSES+= outer", | | | |
1130 | "SUBST_STAGE.outer= post-configure", | | | |
1131 | "SUBST_FILES.outer= outer.txt", | | | |
1132 | "SUBST_VARS.outer= OUTER", | | | |
1133 | ".if ${OPSYS} == NetBSD", | | | |
1134 | "SUBST_CLASSES+= middle", | | | |
1135 | "SUBST_STAGE.middle= post-configure", | | | |
1136 | "SUBST_FILES.middle= inner.txt", | | | |
1137 | "SUBST_VARS.middle= INNER", | | | |
1138 | ". if ${MACHINE_ARCH} == amd64", | | | |
1139 | "SUBST_CLASSES+= inner", | | | |
1140 | "SUBST_STAGE.inner= post-configure", | | | |
1141 | "SUBST_FILES.inner= inner.txt", | | | |
1142 | "SUBST_VARS.inner= INNER", | | | |
1143 | ". endif", | | | |
1144 | ".endif") | | 1313 | ".endif") |
1145 | | | | |
1146 | t.CheckOutputEmpty() | | | |
1147 | } | | 1314 | } |
1148 | | | 1315 | |
1149 | func (s *Suite) Test_SubstContext_directive__two_blocks_in_condition(c *check.C) { | | 1316 | func (s *Suite) Test_substBlock_dupList__late_addition(c *check.C) { |
1150 | t := s.Init(c) | | 1317 | t := s.Init(c) |
1151 | | | 1318 | |
1152 | t.RunSubst( | | 1319 | t.RunSubst( |
| | | 1320 | "SUBST_CLASSES+=\tid", |
| | | 1321 | "SUBST_STAGE.id=\tpost-configure", |
| | | 1322 | "SUBST_FILES.id=\tfiles", |
| | | 1323 | "SUBST_VARS.id=\tPREFIX", |
| | | 1324 | "", |
1153 | ".if ${OPSYS} == NetBSD", | | 1325 | ".if ${OPSYS} == NetBSD", |
1154 | "SUBST_CLASSES+= a", | | 1326 | "SUBST_VARS.id=\tOPSYS", |
1155 | "SUBST_STAGE.a= post-configure", | | | |
1156 | "SUBST_FILES.a= outer.txt", | | | |
1157 | "SUBST_VARS.a= OUTER", | | | |
1158 | "SUBST_CLASSES+= b", | | | |
1159 | "SUBST_STAGE.b= post-configure", | | | |
1160 | "SUBST_FILES.b= inner.txt", | | | |
1161 | "SUBST_VARS.b= INNER", | | | |
1162 | ".endif") | | 1327 | ".endif") |
1163 | | | 1328 | |
1164 | // Up to 2019-12-12, pkglint wrongly warned in filename.mk:6: | | 1329 | t.CheckOutputLines( |
1165 | // Subst block "a" should be finished before adding | | 1330 | "WARN: filename.mk:7: All but the first assignment " + |
1166 | // the next class to SUBST_CLASSES. | | 1331 | "to \"SUBST_VARS.id\" should use the \"+=\" operator.") |
1167 | // The warning was wrong since block "a" has all required fields set. | | | |
1168 | // The warning was caused by an inconsistent check whether the current | | | |
1169 | // block had any conditional variables. | | | |
1170 | t.CheckOutputEmpty() | | | |
1171 | } | | 1332 | } |
1172 | | | 1333 | |
1173 | func (s *Suite) Test_SubstContext_directive__nested_conditional_incomplete_block(c *check.C) { | | 1334 | func (s *Suite) Test_substBlock_dupList__conditional_before_unconditional(c *check.C) { |
1174 | t := s.Init(c) | | 1335 | t := s.Init(c) |
1175 | | | 1336 | |
1176 | t.RunSubst( | | 1337 | t.RunSubst( |
1177 | "SUBST_CLASSES+= outer", | | 1338 | "SUBST_CLASSES+= os", |
1178 | "SUBST_STAGE.outer= post-configure", | | 1339 | "SUBST_STAGE.os= post-configure", |
1179 | "SUBST_FILES.outer= outer.txt", | | 1340 | ".if 1", |
1180 | "SUBST_VARS.outer= OUTER", | | 1341 | "SUBST_FILES.os= conditional", |
1181 | ".if ${OPSYS} == NetBSD", | | 1342 | ".endif", |
1182 | "SUBST_CLASSES+= inner1", | | 1343 | "SUBST_FILES.os= unconditional", |
1183 | "SUBST_STAGE.inner1= post-configure", | | 1344 | "SUBST_VARS.os= OPSYS") |
1184 | "SUBST_VARS.inner1= INNER", | | | |
1185 | "SUBST_CLASSES+= inner2", | | | |
1186 | "SUBST_STAGE.inner2= post-configure", | | | |
1187 | "SUBST_FILES.inner2= inner.txt", | | | |
1188 | "SUBST_VARS.inner2= INNER", | | | |
1189 | ".endif") | | | |
1190 | | | 1345 | |
1191 | t.CheckOutputLines( | | 1346 | t.CheckOutputLines( |
1192 | "WARN: filename.mk:9: Incomplete SUBST block: SUBST_FILES.inner1 missing.", | | 1347 | "WARN: filename.mk:6: All but the first assignment " + |
1193 | "WARN: filename.mk:9: Subst block \"inner1\" should be finished "+ | | 1348 | "to \"SUBST_FILES.os\" should use the \"+=\" operator.") |
1194 | "before adding the next class to SUBST_CLASSES.") | | | |
1195 | } | | 1349 | } |
1196 | | | 1350 | |
1197 | func (s *Suite) Test_SubstContext_finishClass__details_in_then_branch(c *check.C) { | | 1351 | func (s *Suite) Test_substBlock_extractVarname(c *check.C) { |
1198 | t := s.Init(c) | | 1352 | t := s.Init(c) |
1199 | | | 1353 | |
1200 | t.RunSubst( | | 1354 | test := func(input, expected string) { |
1201 | "SUBST_CLASSES+= os", | | 1355 | t.CheckEquals((*substBlock).extractVarname(nil, input), expected) |
1202 | ".if ${OPSYS} == NetBSD", | | 1356 | } |
1203 | "SUBST_VARS.os= OPSYS", | | | |
1204 | "SUBST_SED.os= -e s,@OPSYS@,NetBSD,", | | | |
1205 | "SUBST_STAGE.os= post-configure", | | | |
1206 | "SUBST_MESSAGE.os= Guessing operating system", | | | |
1207 | "SUBST_FILES.os= guess-os.h", | | | |
1208 | ".endif") | | | |
1209 | | | 1357 | |
1210 | t.CheckOutputLines( | | 1358 | // A simple variable name. |
1211 | "WARN: filename.mk:5: SUBST_STAGE.os should not be defined conditionally.", | | 1359 | test("s,@VAR@,${VAR},", "VAR") |
1212 | "WARN: filename.mk:6: SUBST_MESSAGE.os should not be defined conditionally.", | | | |
1213 | "WARN: filename.mk:EOF: Missing SUBST block for \"os\".") | | | |
1214 | } | | | |
1215 | | | 1360 | |
1216 | func (s *Suite) Test_SubstContext_finishClass__details_in_else_branch(c *check.C) { | | 1361 | // A parameterized variable name. |
1217 | t := s.Init(c) | | 1362 | test("s,@VAR.param@,${VAR.param},", "VAR.param") |
1218 | | | 1363 | |
1219 | t.RunSubst( | | 1364 | // Only substitution commands can be replaced with SUBST_VARS. |
1220 | "SUBST_CLASSES+= os", | | 1365 | test("/pattern/d", "") |
1221 | ".if ${OPSYS} == NetBSD", | | | |
1222 | ".else", | | | |
1223 | "SUBST_VARS.os= OPSYS", | | | |
1224 | "SUBST_SED.os= -e s,@OPSYS@,NetBSD,", | | | |
1225 | "SUBST_STAGE.os= post-configure", | | | |
1226 | "SUBST_MESSAGE.os= Guessing operating system", | | | |
1227 | "SUBST_FILES.os= guess-os.h", | | | |
1228 | ".endif") | | | |
1229 | | | 1366 | |
1230 | t.CheckOutputLines( | | 1367 | // An incomplete substitution command. |
1231 | "WARN: filename.mk:6: SUBST_STAGE.os should not be defined conditionally.", | | 1368 | test("s", "") |
1232 | "WARN: filename.mk:7: SUBST_MESSAGE.os should not be defined conditionally.", | | | |
1233 | "WARN: filename.mk:EOF: Missing SUBST block for \"os\".") | | | |
1234 | } | | | |
1235 | | | 1369 | |
1236 | func (s *Suite) Test_SubstContext_finishClass__empty_conditional_at_end(c *check.C) { | | 1370 | // Wrong placeholder character, only @ works. |
1237 | t := s.Init(c) | | 1371 | test("s,!VAR!,${VAR},", "") |
1238 | | | 1372 | |
1239 | t.RunSubst( | | 1373 | // The placeholder must have exactly 1 @ on each side. |
1240 | "SUBST_CLASSES+= os", | | 1374 | test("s,@@VAR@@,${VAR},", "") |
1241 | "SUBST_VARS.os= OPSYS", | | | |
1242 | "SUBST_SED.os= -e s,@OPSYS@,NetBSD,", | | | |
1243 | "SUBST_STAGE.os= post-configure", | | | |
1244 | "SUBST_MESSAGE.os= Guessing operating system", | | | |
1245 | "SUBST_FILES.os= guess-os.h", | | | |
1246 | ".if ${OPSYS} == NetBSD", | | | |
1247 | ".else", | | | |
1248 | ".endif") | | | |
1249 | | | 1375 | |
1250 | t.CheckOutputEmpty() | | 1376 | // Malformed because the comma is the separator. |
1251 | } | | 1377 | test("s,@VAR,VAR@,${VAR},", "") |
1252 | | | 1378 | |
1253 | func (s *Suite) Test_SubstContext_finishClass__missing_transformation_in_one_branch(c *check.C) { | | 1379 | // The replacement pattern is not a simple variable name enclosed in @. |
1254 | t := s.Init(c) | | 1380 | test("s,@VAR!VAR@,${VAR},", "") |
1255 | | | 1381 | |
1256 | t.RunSubst( | | 1382 | // The replacement may only contain the :Q modifier. |
1257 | "SUBST_CLASSES+= os", | | 1383 | test("s,@VAR@,${VAR:Mpattern},", "") |
1258 | "SUBST_STAGE.os= post-configure", | | | |
1259 | "SUBST_MESSAGE.os= Guessing operating system", | | | |
1260 | "SUBST_FILES.os= guess-os.h", | | | |
1261 | ".if ${OPSYS} == NetBSD", | | | |
1262 | "SUBST_FILES.os= -e s,@OpSYS@,NetBSD,", // A simple typo, this should be SUBST_SED. | | | |
1263 | ".elif ${OPSYS} == Darwin", | | | |
1264 | "SUBST_SED.os= -e s,@OPSYS@,Darwin1,", | | | |
1265 | "SUBST_SED.os= -e s,@OPSYS@,Darwin2,", | | | |
1266 | ".else", | | | |
1267 | "SUBST_VARS.os= OPSYS", | | | |
1268 | ".endif") | | | |
1269 | | | 1384 | |
1270 | t.CheckOutputLines( | | 1385 | // The :Q modifier is allowed in the replacement. |
1271 | "WARN: filename.mk:6: All but the first assignment "+ | | 1386 | test("s,@VAR@,${VAR:Q},", "VAR") |
1272 | "to \"SUBST_FILES.os\" should use the \"+=\" operator.", | | | |
1273 | "WARN: filename.mk:9: All but the first assignment "+ | | | |
1274 | "to \"SUBST_SED.os\" should use the \"+=\" operator.", | | | |
1275 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_SED.os, "+ | | | |
1276 | "SUBST_VARS.os or SUBST_FILTER_CMD.os missing.") | | | |
1277 | } | | | |
1278 | | | 1387 | |
1279 | func (s *Suite) Test_SubstContext_finishClass__nested_conditionals(c *check.C) { | | 1388 | // The replacement may contain the :Q modifier only once. |
1280 | t := s.Init(c) | | 1389 | test("s,@VAR@,${VAR:Q:Q},", "") |
1281 | | | 1390 | |
1282 | t.RunSubst( | | 1391 | // The replacement must be a plain variable expression, without prefix. |
1283 | "SUBST_CLASSES+= os", | | 1392 | test("s,@VAR@,prefix${VAR},", "") |
1284 | "SUBST_STAGE.os= post-configure", | | | |
1285 | "SUBST_MESSAGE.os= Guessing operating system", | | | |
1286 | ".if ${OPSYS} == NetBSD", | | | |
1287 | "SUBST_FILES.os= guess-netbsd.h", | | | |
1288 | ". if ${ARCH} == i386", | | | |
1289 | "SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS,NetBSD-i386,", | | | |
1290 | ". elif ${ARCH} == x86_64", | | | |
1291 | "SUBST_VARS.os= OPSYS", | | | |
1292 | ". else", | | | |
1293 | "SUBST_SED.os= -e s,@OPSYS,NetBSD-unknown", | | | |
1294 | ". endif", | | | |
1295 | ".else", | | | |
1296 | // This branch omits SUBST_FILES. | | | |
1297 | "SUBST_SED.os= -e s,@OPSYS@,unknown,", | | | |
1298 | ".endif") | | | |
1299 | | | 1393 | |
1300 | t.CheckOutputLines( | | 1394 | // The replacement must be a plain variable expression, without suffix. |
1301 | "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.os missing.") | | 1395 | test("s,@VAR@,${VAR}suffix,", "") |
1302 | } | | 1396 | } |
1303 | | | 1397 | |
1304 | func (s *Suite) Test_SubstContext_isComplete__incomplete(c *check.C) { | | 1398 | func (s *Suite) Test_substBlock_isComplete__incomplete(c *check.C) { |
1305 | t := s.Init(c) | | 1399 | t := s.Init(c) |
1306 | | | 1400 | |
1307 | ctx := NewSubstContext() | | 1401 | ctx := NewSubstContext() |
1308 | | | 1402 | |
1309 | ctx.varassign(t.NewMkLine("filename.mk", 10, "PKGNAME=pkgname-1.0")) | | 1403 | ctx.varassign(t.NewMkLine("filename.mk", 10, "PKGNAME=pkgname-1.0")) |
1310 | | | 1404 | |
1311 | t.CheckEquals(ctx.id, "") | | 1405 | t.CheckEquals(ctx.isActive(), false) |
1312 | | | 1406 | |
1313 | ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES+=interp")) | | 1407 | ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES+=interp")) |
1314 | | | 1408 | |
1315 | t.CheckEquals(ctx.id, "interp") | | 1409 | t.CheckEquals(ctx.isActive(), false) |
1316 | | | 1410 | |
1317 | ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_FILES.interp=Makefile")) | | 1411 | ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_FILES.interp=Makefile")) |
1318 | | | 1412 | |
1319 | t.CheckEquals(ctx.isComplete(), false) | | 1413 | t.CheckEquals(ctx.activeId(), "interp") |
| | | 1414 | t.CheckEquals(ctx.block().isComplete(), false) |
1320 | | | 1415 | |
1321 | ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_SED.interp=s,@PREFIX@,${PREFIX},g")) | | 1416 | ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_SED.interp=s,@PREFIX@,${PREFIX},g")) |
1322 | | | 1417 | |
1323 | t.CheckEquals(ctx.isComplete(), false) | | 1418 | t.CheckEquals(ctx.block().isComplete(), false) |
1324 | | | 1419 | |
1325 | ctx.Finish(t.NewMkLine("filename.mk", 14, "")) | | 1420 | ctx.Finish(t.NewMkLine("filename.mk", 14, "")) |
1326 | | | 1421 | |
1327 | t.CheckOutputLines( | | 1422 | t.CheckOutputLines( |
1328 | "NOTE: filename.mk:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" "+ | | 1423 | "NOTE: filename.mk:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" "+ |
1329 | "can be replaced with \"SUBST_VARS.interp= PREFIX\".", | | 1424 | "can be replaced with \"SUBST_VARS.interp= PREFIX\".", |
1330 | "WARN: filename.mk:14: Incomplete SUBST block: SUBST_STAGE.interp missing.") | | 1425 | "WARN: filename.mk:14: Incomplete SUBST block: SUBST_STAGE.interp missing.") |
1331 | } | | 1426 | } |
1332 | | | 1427 | |
1333 | func (s *Suite) Test_SubstContext_isComplete__complete(c *check.C) { | | 1428 | func (s *Suite) Test_substBlock_isComplete__complete(c *check.C) { |
1334 | t := s.Init(c) | | 1429 | t := s.Init(c) |
1335 | | | 1430 | |
1336 | ctx := NewSubstContext() | | 1431 | ctx := NewSubstContext() |
1337 | | | 1432 | |
1338 | ctx.varassign(t.NewMkLine("filename.mk", 10, "PKGNAME=pkgname-1.0")) | | 1433 | ctx.varassign(t.NewMkLine("filename.mk", 10, "PKGNAME=pkgname-1.0")) |
1339 | ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES+=p")) | | 1434 | ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES+=p")) |
1340 | ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_FILES.p=Makefile")) | | 1435 | ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_FILES.p=Makefile")) |
1341 | ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_SED.p=s,@PREFIX@,${PREFIX},g")) | | 1436 | ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_SED.p=s,@PREFIX@,${PREFIX},g")) |
1342 | | | 1437 | |
1343 | t.CheckEquals(ctx.isComplete(), false) | | 1438 | t.CheckEquals(ctx.block().isComplete(), false) |
1344 | | | 1439 | |
1345 | ctx.varassign(t.NewMkLine("filename.mk", 14, "SUBST_STAGE.p=post-configure")) | | 1440 | ctx.varassign(t.NewMkLine("filename.mk", 14, "SUBST_STAGE.p=post-configure")) |
1346 | | | 1441 | |
1347 | t.CheckEquals(ctx.isComplete(), true) | | 1442 | t.CheckEquals(ctx.block().isComplete(), true) |
1348 | | | 1443 | |
1349 | ctx.Finish(t.NewMkLine("filename.mk", 15, "")) | | 1444 | ctx.Finish(t.NewMkLine("filename.mk", 15, "")) |
1350 | | | 1445 | |
1351 | t.CheckOutputLines( | | 1446 | t.CheckOutputLines( |
1352 | "NOTE: filename.mk:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" " + | | 1447 | "NOTE: filename.mk:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" " + |
1353 | "can be replaced with \"SUBST_VARS.p= PREFIX\".") | | 1448 | "can be replaced with \"SUBST_VARS.p= PREFIX\".") |
1354 | } | | 1449 | } |
| | | 1450 | |
| | | 1451 | func (s *Suite) Test_substBlock_finish__conditional_inside_then(c *check.C) { |
| | | 1452 | t := s.Init(c) |
| | | 1453 | |
| | | 1454 | t.RunSubst( |
| | | 1455 | ".if ${OPSYS} == Linux", |
| | | 1456 | "SUBST_CLASSES+=\tid", |
| | | 1457 | "SUBST_STAGE.id=\tpre-configure", |
| | | 1458 | "SUBST_SED.id=\t-e sahara", |
| | | 1459 | ".else", |
| | | 1460 | ".endif") |
| | | 1461 | |
| | | 1462 | // The block already ends at the .else, not at the end of the file, |
| | | 1463 | // since that is the scope where the SUBST id is defined. |
| | | 1464 | t.CheckOutputLines( |
| | | 1465 | "WARN: filename.mk:5: Incomplete SUBST block: SUBST_FILES.id missing.") |
| | | 1466 | } |
| | | 1467 | |
| | | 1468 | func (s *Suite) Test_substBlock_finish__conditional_inside_else(c *check.C) { |
| | | 1469 | t := s.Init(c) |
| | | 1470 | |
| | | 1471 | t.RunSubst( |
| | | 1472 | ".if ${OPSYS} == Linux", |
| | | 1473 | ".else", |
| | | 1474 | "SUBST_CLASSES+=\tid", |
| | | 1475 | "SUBST_STAGE.id=\tpre-configure", |
| | | 1476 | "SUBST_SED.id=\t-e sahara", |
| | | 1477 | ".endif") |
| | | 1478 | |
| | | 1479 | // The block already ends at the .endif, not at the end of the file, |
| | | 1480 | // since that is the scope where the SUBST id is defined. |
| | | 1481 | t.CheckOutputLines( |
| | | 1482 | "WARN: filename.mk:6: Incomplete SUBST block: SUBST_FILES.id missing.") |
| | | 1483 | } |
| | | 1484 | |
| | | 1485 | func (s *Suite) Test_substBlock_finish__files_missing(c *check.C) { |
| | | 1486 | t := s.Init(c) |
| | | 1487 | |
| | | 1488 | t.RunSubst( |
| | | 1489 | "SUBST_CLASSES+= one", |
| | | 1490 | "SUBST_STAGE.one= pre-configure", |
| | | 1491 | "SUBST_CLASSES+= two", |
| | | 1492 | "SUBST_STAGE.two= pre-configure", |
| | | 1493 | "SUBST_FILES.two= two.txt", |
| | | 1494 | "SUBST_SED.two= s,two,2,g") |
| | | 1495 | |
| | | 1496 | t.CheckOutputLines( |
| | | 1497 | "WARN: filename.mk:3: Subst block \"one\" should be finished "+ |
| | | 1498 | "before adding the next class to SUBST_CLASSES.", |
| | | 1499 | "WARN: filename.mk:3: Incomplete SUBST block: SUBST_FILES.one missing.", |
| | | 1500 | "WARN: filename.mk:3: Incomplete SUBST block: "+ |
| | | 1501 | "SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.") |
| | | 1502 | } |
| | | 1503 | |
| | | 1504 | // Variables mentioned in SUBST_VARS may appear in the same paragraph, |
| | | 1505 | // or alternatively anywhere else in the file. |
| | | 1506 | func (s *Suite) Test_substBlock_checkForeignVariables__in_next_paragraph(c *check.C) { |
| | | 1507 | t := s.Init(c) |
| | | 1508 | |
| | | 1509 | t.RunSubst( |
| | | 1510 | "SUBST_CLASSES+=\tos", |
| | | 1511 | "SUBST_STAGE.os=\tpre-configure", |
| | | 1512 | "SUBST_FILES.os=\tguess-os.h", |
| | | 1513 | "SUBST_VARS.os=\tTODAY1", |
| | | 1514 | "", |
| | | 1515 | "TODAY1!=\tdate", |
| | | 1516 | "TODAY2!=\tdate") |
| | | 1517 | |
| | | 1518 | t.CheckOutputEmpty() |
| | | 1519 | } |
| | | 1520 | |
| | | 1521 | func (s *Suite) Test_substBlock_checkForeignVariables__mixed_separate(c *check.C) { |
| | | 1522 | t := s.Init(c) |
| | | 1523 | |
| | | 1524 | t.RunSubst( |
| | | 1525 | "SUBST_CLASSES+= 1", |
| | | 1526 | "SUBST_STAGE.1= post-configure", |
| | | 1527 | "SUBST_FILES.1= files", |
| | | 1528 | "", |
| | | 1529 | "SUBST_VARS.1= VAR", |
| | | 1530 | "USE_TOOLS+= gmake") |
| | | 1531 | |
| | | 1532 | // The USE_TOOLS is not in the SUBST block anymore since there is |
| | | 1533 | // an empty line between SUBST_CLASSES and SUBST_VARS. |
| | | 1534 | t.CheckOutputEmpty() |
| | | 1535 | } |
| | | 1536 | |
| | | 1537 | // Variables mentioned in SUBST_VARS are not considered "foreign" |
| | | 1538 | // in the block and may be mixed with the other SUBST variables. |
| | | 1539 | func (s *Suite) Test_substBlock_checkForeignVariables__in_block(c *check.C) { |
| | | 1540 | t := s.Init(c) |
| | | 1541 | |
| | | 1542 | t.RunSubst( |
| | | 1543 | "SUBST_CLASSES+=\tos", |
| | | 1544 | "SUBST_STAGE.os=\tpre-configure", |
| | | 1545 | "SUBST_FILES.os=\tguess-os.h", |
| | | 1546 | "SUBST_VARS.os=\tTODAY1", |
| | | 1547 | "TODAY1!=\tdate", |
| | | 1548 | "TODAY2!=\tdate") |
| | | 1549 | |
| | | 1550 | t.CheckOutputLines( |
| | | 1551 | "WARN: filename.mk:6: Foreign variable \"TODAY2\" in SUBST block.") |
| | | 1552 | } |