Annotation of pkgsrc/pkgtools/pkglint/files/shell_test.go, Revision 1.15
1.1 rillig 1: package main
2:
3: import (
4: check "gopkg.in/check.v1"
1.15 ! rillig 5: "netbsd.org/pkglint/textproc"
1.1 rillig 6: )
7:
1.11 rillig 8: func (s *Suite) Test_splitIntoShellTokens__line_continuation(c *check.C) {
1.9 rillig 9: words, rest := splitIntoShellTokens(dummyLine, "if true; then \\")
1.1 rillig 10:
11: c.Check(words, check.DeepEquals, []string{"if", "true", ";", "then"})
12: c.Check(rest, equals, "\\")
13:
1.9 rillig 14: c.Check(s.Output(), equals, "WARN: Pkglint parse error in ShTokenizer.ShAtom at \"\\\\\" (quoting=plain)\n")
15: }
16:
1.11 rillig 17: func (s *Suite) Test_splitIntoShellTokens__dollar_slash(c *check.C) {
1.9 rillig 18: words, rest := splitIntoShellTokens(dummyLine, "pax -s /.*~$$//g")
1.1 rillig 19:
20: c.Check(words, check.DeepEquals, []string{"pax", "-s", "/.*~$$//g"})
21: c.Check(rest, equals, "")
22: }
23:
1.11 rillig 24: func (s *Suite) Test_splitIntoShellTokens__dollar_subshell(c *check.C) {
1.9 rillig 25: words, rest := splitIntoShellTokens(dummyLine, "id=$$(${AWK} '{print}' < ${WRKSRC}/idfile) && echo \"$$id\"")
26:
27: c.Check(words, deepEquals, []string{"id=", "$$(", "${AWK}", "'{print}'", "<", "${WRKSRC}/idfile", ")", "&&", "echo", "\"$$id\""})
28: c.Check(rest, equals, "")
29: }
30:
1.11 rillig 31: func (s *Suite) Test_splitIntoShellTokens__semicolons(c *check.C) {
1.9 rillig 32: words, rest := splitIntoShellTokens(dummyLine, "word1 word2;;;")
33:
34: c.Check(words, deepEquals, []string{"word1", "word2", ";;", ";"})
35: c.Check(rest, equals, "")
36: }
37:
1.11 rillig 38: func (s *Suite) Test_splitIntoShellTokens__whitespace(c *check.C) {
1.9 rillig 39: text := "\t${RUN} cd ${WRKSRC}&&(${ECHO} ${PERL5:Q};${ECHO})|${BASH} ./install"
40: words, rest := splitIntoShellTokens(dummyLine, text)
41:
42: c.Check(words, deepEquals, []string{
43: "${RUN}",
44: "cd", "${WRKSRC}",
45: "&&", "(", "${ECHO}", "${PERL5:Q}", ";", "${ECHO}", ")",
46: "|", "${BASH}", "./install"})
47: c.Check(rest, equals, "")
48: }
49:
1.11 rillig 50: func (s *Suite) Test_splitIntoShellTokens__varuse_with_embedded_space_and_other_vars(c *check.C) {
1.9 rillig 51: varuseWord := "${GCONF_SCHEMAS:@.s.@${INSTALL_DATA} ${WRKSRC}/src/common/dbus/${.s.} ${DESTDIR}${GCONF_SCHEMAS_DIR}/@}"
52: words, rest := splitIntoShellTokens(dummyLine, varuseWord)
53:
54: c.Check(words, deepEquals, []string{varuseWord})
55: c.Check(rest, equals, "")
56: }
57:
1.11 rillig 58: func (s *Suite) Test_splitIntoMkWords__semicolons(c *check.C) {
1.9 rillig 59: words, rest := splitIntoMkWords(dummyLine, "word1 word2;;;")
60:
61: c.Check(words, deepEquals, []string{"word1", "word2;;;"})
62: c.Check(rest, equals, "")
63: }
64:
1.11 rillig 65: func (s *Suite) Test_splitIntoShellTokens__varuse_with_embedded_space(c *check.C) {
1.9 rillig 66: words, rest := splitIntoShellTokens(dummyLine, "${VAR:S/ /_/g}")
67:
68: c.Check(words, deepEquals, []string{"${VAR:S/ /_/g}"})
69: c.Check(rest, equals, "")
70: }
71:
1.11 rillig 72: func (s *Suite) Test_splitIntoMkWords__varuse_with_embedded_space(c *check.C) {
1.9 rillig 73: words, rest := splitIntoMkWords(dummyLine, "${VAR:S/ /_/g}")
74:
75: c.Check(words, deepEquals, []string{"${VAR:S/ /_/g}"})
76: c.Check(rest, equals, "")
77: }
78:
1.11 rillig 79: func (s *Suite) Test_splitIntoShellTokens__redirect(c *check.C) {
1.10 rillig 80: words, rest := splitIntoShellTokens(dummyLine, "echo 1>output 2>>append 3>|clobber 4>&5 6<input >>append")
81:
82: c.Check(words, deepEquals, []string{
83: "echo",
84: "1>", "output",
85: "2>>", "append",
86: "3>|", "clobber",
87: "4>&", "5",
88: "6<", "input",
89: ">>", "append"})
90: c.Check(rest, equals, "")
91:
92: words, rest = splitIntoShellTokens(dummyLine, "echo 1> output 2>> append 3>| clobber 4>& 5 6< input >> append")
93:
94: c.Check(words, deepEquals, []string{
95: "echo",
96: "1>", "output",
97: "2>>", "append",
98: "3>|", "clobber",
99: "4>&", "5",
100: "6<", "input",
101: ">>", "append"})
102: c.Check(rest, equals, "")
103: }
104:
1.11 rillig 105: func (s *Suite) Test_ShellLine_CheckShellCommandLine(c *check.C) {
1.13 rillig 106: s.Init(c)
107: s.UseCommandLine("-Wall")
1.7 rillig 108: G.Mk = s.NewMkLines("fname",
109: "# dummy")
110: shline := NewShellLine(G.Mk.mklines[0])
1.3 rillig 111:
1.7 rillig 112: shline.CheckShellCommandLine("@# Comment")
1.3 rillig 113:
114: c.Check(s.Output(), equals, "")
115:
1.7 rillig 116: shline.CheckShellCommandLine("uname=`uname`; echo $$uname; echo")
1.3 rillig 117:
118: c.Check(s.Output(), equals, ""+
119: "WARN: fname:1: Unknown shell command \"uname\".\n"+
120: "WARN: fname:1: Unknown shell command \"echo\".\n"+
1.7 rillig 121: "WARN: fname:1: Unknown shell command \"echo\".\n")
1.3 rillig 122:
1.9 rillig 123: s.RegisterTool(&Tool{Name: "echo", Predefined: true})
1.7 rillig 124: G.Mk = s.NewMkLines("fname",
125: "# dummy")
1.3 rillig 126: G.globalData.InitVartypes()
127:
1.7 rillig 128: shline.CheckShellCommandLine("echo ${PKGNAME:Q}") // vucQuotPlain
1.3 rillig 129:
130: c.Check(s.Output(), equals, ""+
1.9 rillig 131: "WARN: fname:1: PKGNAME may not be used in this file; it would be ok in Makefile, Makefile.*, *.mk.\n"+
1.3 rillig 132: "NOTE: fname:1: The :Q operator isn't necessary for ${PKGNAME} here.\n")
133:
1.7 rillig 134: shline.CheckShellCommandLine("echo \"${CFLAGS:Q}\"") // vucQuotDquot
1.1 rillig 135:
1.3 rillig 136: c.Check(s.Output(), equals, ""+
137: "WARN: fname:1: Please don't use the :Q operator in double quotes.\n"+
1.9 rillig 138: "WARN: fname:1: CFLAGS may not be used in this file; it would be ok in Makefile, Makefile.common, options.mk, *.mk.\n"+
1.3 rillig 139: "WARN: fname:1: Please use ${CFLAGS:M*:Q} instead of ${CFLAGS:Q} and make sure the variable appears outside of any quoting characters.\n")
140:
1.7 rillig 141: shline.CheckShellCommandLine("echo '${COMMENT:Q}'") // vucQuotSquot
1.3 rillig 142:
1.9 rillig 143: c.Check(s.Output(), equals, ""+
144: "WARN: fname:1: COMMENT may not be used in any file; it is a write-only variable.\n"+
145: "WARN: fname:1: Please move ${COMMENT:Q} outside of any quoting characters.\n")
1.4 rillig 146:
1.10 rillig 147: shline.CheckShellCommandLine("echo target=$@ exitcode=$$? '$$' \"\\$$\"")
148:
149: c.Check(s.Output(), equals, ""+
150: "WARN: fname:1: Please use \"${.TARGET}\" instead of \"$@\".\n"+
151: "WARN: fname:1: The $? shell variable is often not available in \"set -e\" mode.\n")
152:
1.7 rillig 153: shline.CheckShellCommandLine("echo $$@")
1.3 rillig 154:
155: c.Check(s.Output(), equals, "WARN: fname:1: The $@ shell variable should only be used in double quotes.\n")
1.4 rillig 156:
1.9 rillig 157: shline.CheckShellCommandLine("echo \"$$\"") // As seen by make(1); the shell sees: echo "$"
1.7 rillig 158:
1.9 rillig 159: c.Check(s.Output(), equals, ""+
160: "WARN: fname:1: Pkglint parse error in ShTokenizer.ShAtom at \"$$\\\"\" (quoting=d)\n"+
1.10 rillig 161: "WARN: fname:1: Pkglint ShellLine.CheckShellCommand: parse error at [\"]\n")
1.7 rillig 162:
1.10 rillig 163: shline.CheckShellCommandLine("echo \"\\n\"")
1.7 rillig 164:
165: c.Check(s.Output(), equals, "")
166:
167: shline.CheckShellCommandLine("${RUN} for f in *.c; do echo $${f%.c}; done")
168:
169: c.Check(s.Output(), equals, "")
170:
1.9 rillig 171: shline.CheckShellCommandLine("${RUN} echo $${variable+set}")
172:
173: c.Check(s.Output(), equals, "")
174:
1.7 rillig 175: // Based on mail/thunderbird/Makefile, rev. 1.159
176: shline.CheckShellCommandLine("${RUN} subdir=\"`unzip -c \"$$e\" install.rdf | awk '/re/ { print \"hello\" }'`\"")
1.4 rillig 177:
1.7 rillig 178: c.Check(s.Output(), equals, ""+
1.10 rillig 179: "WARN: fname:1: The exitcode of the left-hand-side command of the pipe operator is ignored.\n"+
1.7 rillig 180: "WARN: fname:1: Unknown shell command \"unzip\".\n"+
181: "WARN: fname:1: Unknown shell command \"awk\".\n")
182:
183: // From mail/thunderbird/Makefile, rev. 1.159
184: shline.CheckShellCommandLine("" +
185: "${RUN} for e in ${XPI_FILES}; do " +
186: " subdir=\"`${UNZIP_CMD} -c \"$$e\" install.rdf | awk '/^ <em:id>/ {sub(\".*<em:id>\",\"\");sub(\"</em:id>.*\",\"\");print;exit;}'`\" && " +
187: " ${MKDIR} \"${WRKDIR}/extensions/$$subdir\" && " +
188: " cd \"${WRKDIR}/extensions/$$subdir\" && " +
189: " ${UNZIP_CMD} -aqo $$e; " +
190: "done")
1.4 rillig 191:
1.7 rillig 192: c.Check(s.Output(), equals, ""+
193: "WARN: fname:1: XPI_FILES is used but not defined. Spelling mistake?\n"+
1.10 rillig 194: "WARN: fname:1: The exitcode of the left-hand-side command of the pipe operator is ignored.\n"+
1.7 rillig 195: "WARN: fname:1: UNZIP_CMD is used but not defined. Spelling mistake?\n"+
196: "WARN: fname:1: Unknown shell command \"awk\".\n"+
1.10 rillig 197: "WARN: fname:1: Unknown shell command \"${MKDIR}\".\n"+
1.7 rillig 198: "WARN: fname:1: MKDIR is used but not defined. Spelling mistake?\n"+
1.10 rillig 199: "WARN: fname:1: UNZIP_CMD is used but not defined. Spelling mistake?\n")
1.7 rillig 200:
201: // From x11/wxGTK28/Makefile
202: shline.CheckShellCommandLine("" +
203: "set -e; cd ${WRKSRC}/locale; " +
204: "for lang in *.po; do " +
205: " [ \"$${lang}\" = \"wxstd.po\" ] && continue; " +
206: " ${TOOLS_PATH.msgfmt} -c -o \"$${lang%.po}.mo\" \"$${lang}\"; " +
207: "done")
1.4 rillig 208:
1.7 rillig 209: c.Check(s.Output(), equals, ""+
1.9 rillig 210: "WARN: fname:1: WRKSRC may not be used in this file; it would be ok in Makefile, Makefile.*, *.mk.\n"+
1.7 rillig 211: "WARN: fname:1: Unknown shell command \"[\".\n"+
212: "WARN: fname:1: Unknown shell command \"${TOOLS_PATH.msgfmt}\".\n")
213:
214: shline.CheckShellCommandLine("@cp from to")
215:
216: c.Check(s.Output(), equals, ""+
217: "WARN: fname:1: The shell command \"cp\" should not be hidden.\n"+
218: "WARN: fname:1: Unknown shell command \"cp\".\n")
219:
220: shline.CheckShellCommandLine("${RUN} ${INSTALL_DATA_DIR} share/pkgbase ${PREFIX}/share/pkgbase")
221:
1.10 rillig 222: c.Check(s.Output(), equals, ""+
223: "NOTE: fname:1: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= share/pkgbase\" instead of \"${INSTALL_DATA_DIR}\".\n"+
224: "WARN: fname:1: The INSTALL_*_DIR commands can only handle one directory at a time.\n")
225:
226: // See PR 46570, item "1. It does not"
227: shline.CheckShellCommandLine("for x in 1 2 3; do echo \"$$x\" || exit 1; done")
228:
229: c.Check(s.Output(), equals, "") // No warning about missing error checking.
1.1 rillig 230: }
231:
1.11 rillig 232: func (s *Suite) Test_ShellLine_CheckShellCommandLine__nofix(c *check.C) {
1.13 rillig 233: s.Init(c)
234: s.UseCommandLine("-Wall")
1.6 rillig 235: G.globalData.InitVartypes()
1.9 rillig 236: s.RegisterTool(&Tool{Name: "echo", Predefined: true})
1.7 rillig 237: G.Mk = s.NewMkLines("Makefile",
238: "\techo ${PKGNAME:Q}")
239: shline := NewShellLine(G.Mk.mklines[0])
240:
241: shline.CheckShellCommandLine("echo ${PKGNAME:Q}")
242:
243: c.Check(s.Output(), equals, ""+
244: "NOTE: Makefile:1: The :Q operator isn't necessary for ${PKGNAME} here.\n")
245: }
246:
1.11 rillig 247: func (s *Suite) Test_ShellLine_CheckShellCommandLine__show_autofix(c *check.C) {
1.13 rillig 248: s.Init(c)
249: s.UseCommandLine("-Wall", "--show-autofix")
1.7 rillig 250: G.globalData.InitVartypes()
1.9 rillig 251: s.RegisterTool(&Tool{Name: "echo", Predefined: true})
1.7 rillig 252: G.Mk = s.NewMkLines("Makefile",
253: "\techo ${PKGNAME:Q}")
254: shline := NewShellLine(G.Mk.mklines[0])
255:
256: shline.CheckShellCommandLine("echo ${PKGNAME:Q}")
257:
258: c.Check(s.Output(), equals, ""+
259: "NOTE: Makefile:1: The :Q operator isn't necessary for ${PKGNAME} here.\n"+
260: "AUTOFIX: Makefile:1: Replacing \"${PKGNAME:Q}\" with \"${PKGNAME}\".\n")
261: }
262:
1.11 rillig 263: func (s *Suite) Test_ShellLine_CheckShellCommandLine__autofix(c *check.C) {
1.13 rillig 264: s.Init(c)
265: s.UseCommandLine("-Wall", "--autofix")
1.7 rillig 266: G.globalData.InitVartypes()
1.9 rillig 267: s.RegisterTool(&Tool{Name: "echo", Predefined: true})
1.7 rillig 268: G.Mk = s.NewMkLines("Makefile",
269: "\techo ${PKGNAME:Q}")
270: shline := NewShellLine(G.Mk.mklines[0])
271:
272: shline.CheckShellCommandLine("echo ${PKGNAME:Q}")
273:
274: c.Check(s.Output(), equals, ""+
275: "AUTOFIX: Makefile:1: Replacing \"${PKGNAME:Q}\" with \"${PKGNAME}\".\n")
276: }
277:
1.11 rillig 278: func (s *Suite) Test_ShellLine_CheckShellCommandLine__implementation(c *check.C) {
1.13 rillig 279: s.Init(c)
280: s.UseCommandLine("-Wall")
1.7 rillig 281: G.globalData.InitVartypes()
282: G.Mk = s.NewMkLines("fname",
283: "# dummy")
284: shline := NewShellLine(G.Mk.mklines[0])
1.6 rillig 285:
286: // foobar="`echo \"foo bar\"`"
1.9 rillig 287: text := "foobar=\"`echo \\\"foo bar\\\"`\""
288:
289: tokens, rest := splitIntoShellTokens(dummyLine, text)
290:
291: c.Check(tokens, deepEquals, []string{text})
292: c.Check(rest, equals, "")
1.6 rillig 293:
1.10 rillig 294: shline.CheckWord(text, false)
295:
296: c.Check(s.Output(), equals, "WARN: fname:1: Unknown shell command \"echo\".\n")
297:
1.9 rillig 298: shline.CheckShellCommandLine(text)
299:
300: c.Check(s.Output(), equals, ""+ // No parse errors
301: "WARN: fname:1: Unknown shell command \"echo\".\n")
1.6 rillig 302: }
303:
1.11 rillig 304: func (s *Suite) Test_ShellLine_CheckShelltext__dollar_without_variable(c *check.C) {
1.6 rillig 305: G.globalData.InitVartypes()
1.7 rillig 306: G.Mk = s.NewMkLines("fname",
307: "# dummy")
308: shline := NewShellLine(G.Mk.mklines[0])
1.9 rillig 309: s.RegisterTool(&Tool{Name: "pax", Varname: "PAX"})
1.7 rillig 310: G.Mk.tools["pax"] = true
1.6 rillig 311:
1.7 rillig 312: shline.CheckShellCommandLine("pax -rwpp -s /.*~$$//g . ${DESTDIR}${PREFIX}")
1.6 rillig 313:
1.7 rillig 314: c.Check(s.Output(), equals, "")
1.6 rillig 315: }
316:
1.9 rillig 317: func (s *Suite) Test_ShellLine_CheckWord(c *check.C) {
1.13 rillig 318: s.Init(c)
319: s.UseCommandLine("-Wall")
1.1 rillig 320: G.globalData.InitVartypes()
1.7 rillig 321: shline := NewShellLine(NewMkLine(NewLine("fname", 1, "# dummy", nil)))
1.1 rillig 322:
1.9 rillig 323: shline.CheckWord("${${list}}", false)
1.1 rillig 324:
1.9 rillig 325: c.Check(s.Output(), equals, "") // No warning for variables that are completely indirect.
1.1 rillig 326:
1.9 rillig 327: shline.CheckWord("${SED_FILE.${id}}", false)
328:
1.10 rillig 329: c.Check(s.Output(), equals, "") // No warning for variables that are partly indirect.
1.1 rillig 330:
1.9 rillig 331: shline.CheckWord("\"$@\"", false)
1.1 rillig 332:
333: c.Check(s.Output(), equals, "WARN: fname:1: Please use \"${.TARGET}\" instead of \"$@\".\n")
1.7 rillig 334:
1.9 rillig 335: shline.CheckWord("${COMMENT:Q}", true)
1.7 rillig 336:
1.9 rillig 337: c.Check(s.Output(), equals, "WARN: fname:1: COMMENT may not be used in any file; it is a write-only variable.\n")
1.7 rillig 338:
1.9 rillig 339: shline.CheckWord("\"${DISTINFO_FILE:Q}\"", true)
1.7 rillig 340:
341: c.Check(s.Output(), equals, ""+
1.9 rillig 342: "WARN: fname:1: DISTINFO_FILE may not be used in this file; it would be ok in Makefile, Makefile.*, *.mk.\n"+
1.7 rillig 343: "NOTE: fname:1: The :Q operator isn't necessary for ${DISTINFO_FILE} here.\n")
344:
1.9 rillig 345: shline.CheckWord("embed${DISTINFO_FILE:Q}ded", true)
1.7 rillig 346:
347: c.Check(s.Output(), equals, ""+
1.9 rillig 348: "WARN: fname:1: DISTINFO_FILE may not be used in this file; it would be ok in Makefile, Makefile.*, *.mk.\n"+
1.7 rillig 349: "NOTE: fname:1: The :Q operator isn't necessary for ${DISTINFO_FILE} here.\n")
350:
1.9 rillig 351: shline.CheckWord("s,\\.,,", true)
1.7 rillig 352:
353: c.Check(s.Output(), equals, "")
354:
1.9 rillig 355: shline.CheckWord("\"s,\\.,,\"", true)
1.7 rillig 356:
357: c.Check(s.Output(), equals, "")
1.1 rillig 358: }
359:
1.11 rillig 360: func (s *Suite) Test_ShellLine_CheckWord__dollar_without_variable(c *check.C) {
1.7 rillig 361: shline := NewShellLine(NewMkLine(NewLine("fname", 1, "# dummy", nil)))
1.6 rillig 362:
1.9 rillig 363: shline.CheckWord("/.*~$$//g", false) // Typical argument to pax(1).
1.6 rillig 364:
1.7 rillig 365: c.Check(s.Output(), equals, "")
1.6 rillig 366: }
367:
1.11 rillig 368: func (s *Suite) Test_ShellLine_CheckShellCommandLine__echo(c *check.C) {
1.13 rillig 369: s.Init(c)
370: s.UseCommandLine("-Wall")
1.9 rillig 371: s.RegisterTool(&Tool{Name: "echo", Varname: "ECHO", MustUseVarForm: true, Predefined: true})
1.7 rillig 372: G.Mk = s.NewMkLines("fname",
373: "# dummy")
374: mkline := NewMkLine(NewLine("fname", 3, "# dummy", nil))
375:
1.15 ! rillig 376: MkLineChecker{mkline}.checkText("echo \"hello, world\"")
1.1 rillig 377:
1.7 rillig 378: c.Check(s.Output(), equals, "")
379:
380: NewShellLine(mkline).CheckShellCommandLine("echo \"hello, world\"")
1.1 rillig 381:
382: c.Check(s.Output(), equals, ""+
383: "WARN: fname:3: Please use \"${ECHO}\" instead of \"echo\".\n")
384: }
1.3 rillig 385:
1.11 rillig 386: func (s *Suite) Test_ShellLine_CheckShellCommandLine__shell_variables(c *check.C) {
1.9 rillig 387: text := "\tfor f in *.pl; do ${SED} s,@PREFIX@,${PREFIX}, < $f > $f.tmp && ${MV} $f.tmp $f; done"
1.3 rillig 388:
1.9 rillig 389: shline := NewShellLine(NewMkLine(NewLine("Makefile", 3, text, nil)))
390: shline.CheckShellCommandLine(text)
1.3 rillig 391:
1.9 rillig 392: c.Check(s.Output(), equals, ""+
393: "WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Makefile variable or $$f if you mean a shell variable.\n"+
394: "WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Makefile variable or $$f if you mean a shell variable.\n"+
395: "WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Makefile variable or $$f if you mean a shell variable.\n"+
396: "WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Makefile variable or $$f if you mean a shell variable.\n"+
397: "NOTE: Makefile:3: Please use the SUBST framework instead of ${SED} and ${MV}.\n")
1.3 rillig 398:
1.7 rillig 399: shline.CheckShellCommandLine("install -c manpage.1 ${PREFIX}/man/man1/manpage.1")
1.3 rillig 400:
1.5 rillig 401: c.Check(s.Output(), equals, "WARN: Makefile:3: Please use ${PKGMANDIR} instead of \"man\".\n")
1.3 rillig 402:
1.7 rillig 403: shline.CheckShellCommandLine("cp init-script ${PREFIX}/etc/rc.d/service")
1.3 rillig 404:
1.5 rillig 405: c.Check(s.Output(), equals, "WARN: Makefile:3: Please use the RCD_SCRIPTS mechanism to install rc.d scripts automatically to ${RCD_SCRIPTS_EXAMPLEDIR}.\n")
1.3 rillig 406: }
407:
1.11 rillig 408: func (s *Suite) Test_ShellLine_checkCommandUse(c *check.C) {
1.7 rillig 409: G.Mk = s.NewMkLines("fname",
410: "# dummy")
411: G.Mk.target = "do-install"
1.3 rillig 412:
1.7 rillig 413: shline := NewShellLine(NewMkLine(NewLine("fname", 1, "\tdummy", nil)))
1.3 rillig 414:
415: shline.checkCommandUse("sed")
416:
417: c.Check(s.Output(), equals, "WARN: fname:1: The shell command \"sed\" should not be used in the install phase.\n")
418:
419: shline.checkCommandUse("cp")
420:
421: c.Check(s.Output(), equals, "WARN: fname:1: ${CP} should not be used to install files.\n")
422: }
1.7 rillig 423:
1.11 rillig 424: func (s *Suite) Test_splitIntoMkWords(c *check.C) {
1.7 rillig 425: url := "http://registry.gimp.org/file/fix-ca.c?action=download&id=9884&file="
426:
1.14 rillig 427: words, rest := splitIntoShellTokens(dummyLine, url) // Doesn't really make sense
1.7 rillig 428:
429: c.Check(words, check.DeepEquals, []string{"http://registry.gimp.org/file/fix-ca.c?action=download", "&", "id=9884", "&", "file="})
430: c.Check(rest, equals, "")
431:
1.9 rillig 432: words, rest = splitIntoMkWords(dummyLine, url)
1.7 rillig 433:
434: c.Check(words, check.DeepEquals, []string{"http://registry.gimp.org/file/fix-ca.c?action=download&id=9884&file="})
435: c.Check(rest, equals, "")
436:
1.9 rillig 437: words, rest = splitIntoMkWords(dummyLine, "a b \"c c c\" d;;d;; \"e\"''`` 'rest")
1.7 rillig 438:
439: c.Check(words, check.DeepEquals, []string{"a", "b", "\"c c c\"", "d;;d;;", "\"e\"''``"})
440: c.Check(rest, equals, "'rest")
441: }
442:
1.11 rillig 443: func (s *Suite) Test_ShellLine_CheckShellCommandLine__sed_and_mv(c *check.C) {
1.7 rillig 444: shline := NewShellLine(NewMkLine(NewLine("Makefile", 85, "\t${RUN} ${SED} 's,#,// comment:,g' fname > fname.tmp; ${MV} fname.tmp fname", nil)))
445:
446: shline.CheckShellCommandLine(shline.mkline.Shellcmd())
447:
448: c.Check(s.Output(), equals, "NOTE: Makefile:85: Please use the SUBST framework instead of ${SED} and ${MV}.\n")
449: }
450:
1.11 rillig 451: func (s *Suite) Test_ShellLine_CheckShellCommandLine__subshell(c *check.C) {
1.7 rillig 452: shline := NewShellLine(NewMkLine(NewLine("Makefile", 85, "\t${RUN} uname=$$(uname)", nil)))
453:
454: shline.CheckShellCommandLine(shline.mkline.Shellcmd())
455:
456: c.Check(s.Output(), equals, "WARN: Makefile:85: Invoking subshells via $(...) is not portable enough.\n")
457: }
458:
1.11 rillig 459: func (s *Suite) Test_ShellLine_CheckShellCommandLine__install_dir(c *check.C) {
1.7 rillig 460: shline := NewShellLine(NewMkLine(NewLine("Makefile", 85, "\t${RUN} ${INSTALL_DATA_DIR} ${DESTDIR}${PREFIX}/dir1 ${DESTDIR}${PREFIX}/dir2", nil)))
461:
462: shline.CheckShellCommandLine(shline.mkline.Shellcmd())
463:
464: c.Check(s.Output(), equals, ""+
1.10 rillig 465: "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL_DATA_DIR}\".\n"+
466: "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL_DATA_DIR}\".\n"+
467: "WARN: Makefile:85: The INSTALL_*_DIR commands can only handle one directory at a time.\n")
468:
469: shline.CheckShellCommandLine("${INSTALL_DATA_DIR} -d -m 0755 ${DESTDIR}${PREFIX}/share/examples/gdchart")
470:
471: // No warning about multiple directories, since 0755 is an option, not an argument.
472: c.Check(s.Output(), equals, ""+
473: "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= share/examples/gdchart\" instead of \"${INSTALL_DATA_DIR}\".\n")
474:
475: shline.CheckShellCommandLine("${INSTALL_DATA_DIR} -d -m 0755 ${DESTDIR}${PREFIX}/dir1 ${PREFIX}/dir2")
476:
477: c.Check(s.Output(), equals, ""+
478: "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL_DATA_DIR}\".\n"+
479: "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL_DATA_DIR}\".\n"+
1.7 rillig 480: "WARN: Makefile:85: The INSTALL_*_DIR commands can only handle one directory at a time.\n")
481: }
482:
1.11 rillig 483: func (s *Suite) Test_ShellLine_CheckShellCommandLine__install_option_d(c *check.C) {
1.7 rillig 484: shline := NewShellLine(NewMkLine(NewLine("Makefile", 85, "\t${RUN} ${INSTALL} -d ${DESTDIR}${PREFIX}/dir1 ${DESTDIR}${PREFIX}/dir2", nil)))
485:
486: shline.CheckShellCommandLine(shline.mkline.Shellcmd())
487:
488: c.Check(s.Output(), equals, ""+
1.10 rillig 489: "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL} -d\".\n"+
490: "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL} -d\".\n")
1.7 rillig 491: }
492:
1.11 rillig 493: func (s *Suite) Test_ShellLine__shell_comment_with_line_continuation(c *check.C) {
1.13 rillig 494: s.Init(c)
495: tmpfile := s.CreateTmpFile("Makefile", ""+
1.7 rillig 496: "# $"+"NetBSD$\n"+
497: "pre-install:\n"+
498: "\t"+"# comment\\\n"+
499: "\t"+"echo \"hello\"\n")
500: lines := LoadNonemptyLines(tmpfile, true)
501:
502: NewMkLines(lines).Check()
503:
1.8 rillig 504: c.Check(s.Output(), equals, "WARN: ~/Makefile:3--4: A shell comment does not stop at the end of line.\n")
1.7 rillig 505: }
1.9 rillig 506:
1.11 rillig 507: func (s *Suite) Test_ShellLine_unescapeBackticks(c *check.C) {
1.9 rillig 508: shline := NewShellLine(NewMkLine(dummyLine))
509: // foobar="`echo \"foo bar\"`"
510: text := "foobar=\"`echo \\\"foo bar\\\"`\""
1.15 ! rillig 511: repl := textproc.NewPrefixReplacer(text)
1.9 rillig 512: repl.AdvanceStr("foobar=\"`")
513:
514: backtCommand, newQuoting := shline.unescapeBackticks(text, repl, shqDquotBackt)
515: c.Check(backtCommand, equals, "echo \"foo bar\"")
516: c.Check(newQuoting, equals, shqDquot)
1.15 ! rillig 517: c.Check(repl.Rest(), equals, "\"")
1.9 rillig 518: }
CVSweb <webmaster@jp.NetBSD.org>