package main
import (
check "gopkg.in/check.v1"
)
func (s *Suite) Test_splitIntoShellTokens__line_continuation(c *check.C) {
words, rest := splitIntoShellTokens(dummyLine, "if true; then \\")
c.Check(words, check.DeepEquals, []string{"if", "true", ";", "then"})
c.Check(rest, equals, "\\")
c.Check(s.Output(), equals, "WARN: Pkglint parse error in ShTokenizer.ShAtom at \"\\\\\" (quoting=plain)\n")
}
func (s *Suite) Test_splitIntoShellTokens__dollar_slash(c *check.C) {
words, rest := splitIntoShellTokens(dummyLine, "pax -s /.*~$$//g")
c.Check(words, check.DeepEquals, []string{"pax", "-s", "/.*~$$//g"})
c.Check(rest, equals, "")
}
func (s *Suite) Test_splitIntoShellTokens__dollar_subshell(c *check.C) {
words, rest := splitIntoShellTokens(dummyLine, "id=$$(${AWK} '{print}' < ${WRKSRC}/idfile) && echo \"$$id\"")
c.Check(words, deepEquals, []string{"id=", "$$(", "${AWK}", "'{print}'", "<", "${WRKSRC}/idfile", ")", "&&", "echo", "\"$$id\""})
c.Check(rest, equals, "")
}
func (s *Suite) Test_splitIntoShellTokens__semicolons(c *check.C) {
words, rest := splitIntoShellTokens(dummyLine, "word1 word2;;;")
c.Check(words, deepEquals, []string{"word1", "word2", ";;", ";"})
c.Check(rest, equals, "")
}
func (s *Suite) Test_splitIntoShellTokens__whitespace(c *check.C) {
text := "\t${RUN} cd ${WRKSRC}&&(${ECHO} ${PERL5:Q};${ECHO})|${BASH} ./install"
words, rest := splitIntoShellTokens(dummyLine, text)
c.Check(words, deepEquals, []string{
"${RUN}",
"cd", "${WRKSRC}",
"&&", "(", "${ECHO}", "${PERL5:Q}", ";", "${ECHO}", ")",
"|", "${BASH}", "./install"})
c.Check(rest, equals, "")
}
func (s *Suite) Test_splitIntoShellTokens__varuse_with_embedded_space_and_other_vars(c *check.C) {
varuseWord := "${GCONF_SCHEMAS:@.s.@${INSTALL_DATA} ${WRKSRC}/src/common/dbus/${.s.} ${DESTDIR}${GCONF_SCHEMAS_DIR}/@}"
words, rest := splitIntoShellTokens(dummyLine, varuseWord)
c.Check(words, deepEquals, []string{varuseWord})
c.Check(rest, equals, "")
}
func (s *Suite) Test_splitIntoMkWords__semicolons(c *check.C) {
words, rest := splitIntoMkWords(dummyLine, "word1 word2;;;")
c.Check(words, deepEquals, []string{"word1", "word2;;;"})
c.Check(rest, equals, "")
}
func (s *Suite) Test_splitIntoShellTokens__varuse_with_embedded_space(c *check.C) {
words, rest := splitIntoShellTokens(dummyLine, "${VAR:S/ /_/g}")
c.Check(words, deepEquals, []string{"${VAR:S/ /_/g}"})
c.Check(rest, equals, "")
}
func (s *Suite) Test_splitIntoMkWords__varuse_with_embedded_space(c *check.C) {
words, rest := splitIntoMkWords(dummyLine, "${VAR:S/ /_/g}")
c.Check(words, deepEquals, []string{"${VAR:S/ /_/g}"})
c.Check(rest, equals, "")
}
func (s *Suite) Test_splitIntoShellTokens__redirect(c *check.C) {
words, rest := splitIntoShellTokens(dummyLine, "echo 1>output 2>>append 3>|clobber 4>&5 6>append")
c.Check(words, deepEquals, []string{
"echo",
"1>", "output",
"2>>", "append",
"3>|", "clobber",
"4>&", "5",
"6<", "input",
">>", "append"})
c.Check(rest, equals, "")
words, rest = splitIntoShellTokens(dummyLine, "echo 1> output 2>> append 3>| clobber 4>& 5 6< input >> append")
c.Check(words, deepEquals, []string{
"echo",
"1>", "output",
"2>>", "append",
"3>|", "clobber",
"4>&", "5",
"6<", "input",
">>", "append"})
c.Check(rest, equals, "")
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine(c *check.C) {
s.UseCommandLine(c, "-Wall")
G.Mk = s.NewMkLines("fname",
"# dummy")
shline := NewShellLine(G.Mk.mklines[0])
shline.CheckShellCommandLine("@# Comment")
c.Check(s.Output(), equals, "")
shline.CheckShellCommandLine("uname=`uname`; echo $$uname; echo")
c.Check(s.Output(), equals, ""+
"WARN: fname:1: Unknown shell command \"uname\".\n"+
"WARN: fname:1: Unknown shell command \"echo\".\n"+
"WARN: fname:1: Unknown shell command \"echo\".\n")
s.RegisterTool(&Tool{Name: "echo", Predefined: true})
G.Mk = s.NewMkLines("fname",
"# dummy")
G.globalData.InitVartypes()
shline.CheckShellCommandLine("echo ${PKGNAME:Q}") // vucQuotPlain
c.Check(s.Output(), equals, ""+
"WARN: fname:1: PKGNAME may not be used in this file; it would be ok in Makefile, Makefile.*, *.mk.\n"+
"NOTE: fname:1: The :Q operator isn't necessary for ${PKGNAME} here.\n")
shline.CheckShellCommandLine("echo \"${CFLAGS:Q}\"") // vucQuotDquot
c.Check(s.Output(), equals, ""+
"WARN: fname:1: Please don't use the :Q operator in double quotes.\n"+
"WARN: fname:1: CFLAGS may not be used in this file; it would be ok in Makefile, Makefile.common, options.mk, *.mk.\n"+
"WARN: fname:1: Please use ${CFLAGS:M*:Q} instead of ${CFLAGS:Q} and make sure the variable appears outside of any quoting characters.\n")
shline.CheckShellCommandLine("echo '${COMMENT:Q}'") // vucQuotSquot
c.Check(s.Output(), equals, ""+
"WARN: fname:1: COMMENT may not be used in any file; it is a write-only variable.\n"+
"WARN: fname:1: Please move ${COMMENT:Q} outside of any quoting characters.\n")
shline.CheckShellCommandLine("echo target=$@ exitcode=$$? '$$' \"\\$$\"")
c.Check(s.Output(), equals, ""+
"WARN: fname:1: Please use \"${.TARGET}\" instead of \"$@\".\n"+
"WARN: fname:1: The $? shell variable is often not available in \"set -e\" mode.\n")
shline.CheckShellCommandLine("echo $$@")
c.Check(s.Output(), equals, "WARN: fname:1: The $@ shell variable should only be used in double quotes.\n")
shline.CheckShellCommandLine("echo \"$$\"") // As seen by make(1); the shell sees: echo "$"
c.Check(s.Output(), equals, ""+
"WARN: fname:1: Pkglint parse error in ShTokenizer.ShAtom at \"$$\\\"\" (quoting=d)\n"+
"WARN: fname:1: Pkglint ShellLine.CheckShellCommand: parse error at [\"]\n")
shline.CheckShellCommandLine("echo \"\\n\"")
c.Check(s.Output(), equals, "")
shline.CheckShellCommandLine("${RUN} for f in *.c; do echo $${f%.c}; done")
c.Check(s.Output(), equals, "")
shline.CheckShellCommandLine("${RUN} echo $${variable+set}")
c.Check(s.Output(), equals, "")
// Based on mail/thunderbird/Makefile, rev. 1.159
shline.CheckShellCommandLine("${RUN} subdir=\"`unzip -c \"$$e\" install.rdf | awk '/re/ { print \"hello\" }'`\"")
c.Check(s.Output(), equals, ""+
"WARN: fname:1: The exitcode of the left-hand-side command of the pipe operator is ignored.\n"+
"WARN: fname:1: Unknown shell command \"unzip\".\n"+
"WARN: fname:1: Unknown shell command \"awk\".\n")
// From mail/thunderbird/Makefile, rev. 1.159
shline.CheckShellCommandLine("" +
"${RUN} for e in ${XPI_FILES}; do " +
" subdir=\"`${UNZIP_CMD} -c \"$$e\" install.rdf | awk '/^ / {sub(\".*\",\"\");sub(\".*\",\"\");print;exit;}'`\" && " +
" ${MKDIR} \"${WRKDIR}/extensions/$$subdir\" && " +
" cd \"${WRKDIR}/extensions/$$subdir\" && " +
" ${UNZIP_CMD} -aqo $$e; " +
"done")
c.Check(s.Output(), equals, ""+
"WARN: fname:1: XPI_FILES is used but not defined. Spelling mistake?\n"+
"WARN: fname:1: The exitcode of the left-hand-side command of the pipe operator is ignored.\n"+
"WARN: fname:1: UNZIP_CMD is used but not defined. Spelling mistake?\n"+
"WARN: fname:1: Unknown shell command \"awk\".\n"+
"WARN: fname:1: Unknown shell command \"${MKDIR}\".\n"+
"WARN: fname:1: MKDIR is used but not defined. Spelling mistake?\n"+
"WARN: fname:1: UNZIP_CMD is used but not defined. Spelling mistake?\n")
// From x11/wxGTK28/Makefile
shline.CheckShellCommandLine("" +
"set -e; cd ${WRKSRC}/locale; " +
"for lang in *.po; do " +
" [ \"$${lang}\" = \"wxstd.po\" ] && continue; " +
" ${TOOLS_PATH.msgfmt} -c -o \"$${lang%.po}.mo\" \"$${lang}\"; " +
"done")
c.Check(s.Output(), equals, ""+
"WARN: fname:1: WRKSRC may not be used in this file; it would be ok in Makefile, Makefile.*, *.mk.\n"+
"WARN: fname:1: Unknown shell command \"[\".\n"+
"WARN: fname:1: Unknown shell command \"${TOOLS_PATH.msgfmt}\".\n")
shline.CheckShellCommandLine("@cp from to")
c.Check(s.Output(), equals, ""+
"WARN: fname:1: The shell command \"cp\" should not be hidden.\n"+
"WARN: fname:1: Unknown shell command \"cp\".\n")
shline.CheckShellCommandLine("${RUN} ${INSTALL_DATA_DIR} share/pkgbase ${PREFIX}/share/pkgbase")
c.Check(s.Output(), equals, ""+
"NOTE: fname:1: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= share/pkgbase\" instead of \"${INSTALL_DATA_DIR}\".\n"+
"WARN: fname:1: The INSTALL_*_DIR commands can only handle one directory at a time.\n")
// See PR 46570, item "1. It does not"
shline.CheckShellCommandLine("for x in 1 2 3; do echo \"$$x\" || exit 1; done")
c.Check(s.Output(), equals, "") // No warning about missing error checking.
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine__nofix(c *check.C) {
s.UseCommandLine(c, "-Wall")
G.globalData.InitVartypes()
s.RegisterTool(&Tool{Name: "echo", Predefined: true})
G.Mk = s.NewMkLines("Makefile",
"\techo ${PKGNAME:Q}")
shline := NewShellLine(G.Mk.mklines[0])
c.Check(shline.line.raw[0].textnl, equals, "\techo ${PKGNAME:Q}\n")
c.Check(shline.line.raw[0].Lineno, equals, 1)
shline.CheckShellCommandLine("echo ${PKGNAME:Q}")
c.Check(s.Output(), equals, ""+
"NOTE: Makefile:1: The :Q operator isn't necessary for ${PKGNAME} here.\n")
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine__show_autofix(c *check.C) {
s.UseCommandLine(c, "-Wall", "--show-autofix")
G.globalData.InitVartypes()
s.RegisterTool(&Tool{Name: "echo", Predefined: true})
G.Mk = s.NewMkLines("Makefile",
"\techo ${PKGNAME:Q}")
shline := NewShellLine(G.Mk.mklines[0])
shline.CheckShellCommandLine("echo ${PKGNAME:Q}")
c.Check(s.Output(), equals, ""+
"NOTE: Makefile:1: The :Q operator isn't necessary for ${PKGNAME} here.\n"+
"AUTOFIX: Makefile:1: Replacing \"${PKGNAME:Q}\" with \"${PKGNAME}\".\n")
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine__autofix(c *check.C) {
s.UseCommandLine(c, "-Wall", "--autofix")
G.globalData.InitVartypes()
s.RegisterTool(&Tool{Name: "echo", Predefined: true})
G.Mk = s.NewMkLines("Makefile",
"\techo ${PKGNAME:Q}")
shline := NewShellLine(G.Mk.mklines[0])
shline.CheckShellCommandLine("echo ${PKGNAME:Q}")
c.Check(s.Output(), equals, ""+
"AUTOFIX: Makefile:1: Replacing \"${PKGNAME:Q}\" with \"${PKGNAME}\".\n")
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine__implementation(c *check.C) {
s.UseCommandLine(c, "-Wall")
G.globalData.InitVartypes()
G.Mk = s.NewMkLines("fname",
"# dummy")
shline := NewShellLine(G.Mk.mklines[0])
// foobar="`echo \"foo bar\"`"
text := "foobar=\"`echo \\\"foo bar\\\"`\""
tokens, rest := splitIntoShellTokens(dummyLine, text)
c.Check(tokens, deepEquals, []string{text})
c.Check(rest, equals, "")
shline.CheckWord(text, false)
c.Check(s.Output(), equals, "WARN: fname:1: Unknown shell command \"echo\".\n")
shline.CheckShellCommandLine(text)
c.Check(s.Output(), equals, ""+ // No parse errors
"WARN: fname:1: Unknown shell command \"echo\".\n")
}
func (s *Suite) Test_ShellLine_CheckShelltext__dollar_without_variable(c *check.C) {
G.globalData.InitVartypes()
G.Mk = s.NewMkLines("fname",
"# dummy")
shline := NewShellLine(G.Mk.mklines[0])
s.RegisterTool(&Tool{Name: "pax", Varname: "PAX"})
G.Mk.tools["pax"] = true
shline.CheckShellCommandLine("pax -rwpp -s /.*~$$//g . ${DESTDIR}${PREFIX}")
c.Check(s.Output(), equals, "")
}
func (s *Suite) Test_ShellLine_CheckWord(c *check.C) {
s.UseCommandLine(c, "-Wall")
G.globalData.InitVartypes()
shline := NewShellLine(NewMkLine(NewLine("fname", 1, "# dummy", nil)))
shline.CheckWord("${${list}}", false)
c.Check(s.Output(), equals, "") // No warning for variables that are completely indirect.
shline.CheckWord("${SED_FILE.${id}}", false)
c.Check(s.Output(), equals, "") // No warning for variables that are partly indirect.
shline.CheckWord("\"$@\"", false)
c.Check(s.Output(), equals, "WARN: fname:1: Please use \"${.TARGET}\" instead of \"$@\".\n")
shline.CheckWord("${COMMENT:Q}", true)
c.Check(s.Output(), equals, "WARN: fname:1: COMMENT may not be used in any file; it is a write-only variable.\n")
shline.CheckWord("\"${DISTINFO_FILE:Q}\"", true)
c.Check(s.Output(), equals, ""+
"WARN: fname:1: DISTINFO_FILE may not be used in this file; it would be ok in Makefile, Makefile.*, *.mk.\n"+
"NOTE: fname:1: The :Q operator isn't necessary for ${DISTINFO_FILE} here.\n")
shline.CheckWord("embed${DISTINFO_FILE:Q}ded", true)
c.Check(s.Output(), equals, ""+
"WARN: fname:1: DISTINFO_FILE may not be used in this file; it would be ok in Makefile, Makefile.*, *.mk.\n"+
"NOTE: fname:1: The :Q operator isn't necessary for ${DISTINFO_FILE} here.\n")
shline.CheckWord("s,\\.,,", true)
c.Check(s.Output(), equals, "")
shline.CheckWord("\"s,\\.,,\"", true)
c.Check(s.Output(), equals, "")
}
func (s *Suite) Test_ShellLine_CheckWord__dollar_without_variable(c *check.C) {
shline := NewShellLine(NewMkLine(NewLine("fname", 1, "# dummy", nil)))
shline.CheckWord("/.*~$$//g", false) // Typical argument to pax(1).
c.Check(s.Output(), equals, "")
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine__echo(c *check.C) {
s.UseCommandLine(c, "-Wall")
s.RegisterTool(&Tool{Name: "echo", Varname: "ECHO", MustUseVarForm: true, Predefined: true})
G.Mk = s.NewMkLines("fname",
"# dummy")
mkline := NewMkLine(NewLine("fname", 3, "# dummy", nil))
mkline.checkText("echo \"hello, world\"")
c.Check(s.Output(), equals, "")
NewShellLine(mkline).CheckShellCommandLine("echo \"hello, world\"")
c.Check(s.Output(), equals, ""+
"WARN: fname:3: Please use \"${ECHO}\" instead of \"echo\".\n")
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine__shell_variables(c *check.C) {
text := "\tfor f in *.pl; do ${SED} s,@PREFIX@,${PREFIX}, < $f > $f.tmp && ${MV} $f.tmp $f; done"
shline := NewShellLine(NewMkLine(NewLine("Makefile", 3, text, nil)))
shline.CheckShellCommandLine(text)
c.Check(s.Output(), equals, ""+
"WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Makefile variable or $$f if you mean a shell variable.\n"+
"WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Makefile variable or $$f if you mean a shell variable.\n"+
"WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Makefile variable or $$f if you mean a shell variable.\n"+
"WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Makefile variable or $$f if you mean a shell variable.\n"+
"NOTE: Makefile:3: Please use the SUBST framework instead of ${SED} and ${MV}.\n")
shline.CheckShellCommandLine("install -c manpage.1 ${PREFIX}/man/man1/manpage.1")
c.Check(s.Output(), equals, "WARN: Makefile:3: Please use ${PKGMANDIR} instead of \"man\".\n")
shline.CheckShellCommandLine("cp init-script ${PREFIX}/etc/rc.d/service")
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")
}
func (s *Suite) Test_ShellLine_checkCommandUse(c *check.C) {
G.Mk = s.NewMkLines("fname",
"# dummy")
G.Mk.target = "do-install"
shline := NewShellLine(NewMkLine(NewLine("fname", 1, "\tdummy", nil)))
shline.checkCommandUse("sed")
c.Check(s.Output(), equals, "WARN: fname:1: The shell command \"sed\" should not be used in the install phase.\n")
shline.checkCommandUse("cp")
c.Check(s.Output(), equals, "WARN: fname:1: ${CP} should not be used to install files.\n")
}
func (s *Suite) Test_splitIntoMkWords(c *check.C) {
url := "http://registry.gimp.org/file/fix-ca.c?action=download&id=9884&file="
words, rest := splitIntoShellTokens(dummyLine, url) // Doesn’t really make sense
c.Check(words, check.DeepEquals, []string{"http://registry.gimp.org/file/fix-ca.c?action=download", "&", "id=9884", "&", "file="})
c.Check(rest, equals, "")
words, rest = splitIntoMkWords(dummyLine, url)
c.Check(words, check.DeepEquals, []string{"http://registry.gimp.org/file/fix-ca.c?action=download&id=9884&file="})
c.Check(rest, equals, "")
words, rest = splitIntoMkWords(dummyLine, "a b \"c c c\" d;;d;; \"e\"''`` 'rest")
c.Check(words, check.DeepEquals, []string{"a", "b", "\"c c c\"", "d;;d;;", "\"e\"''``"})
c.Check(rest, equals, "'rest")
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine__sed_and_mv(c *check.C) {
shline := NewShellLine(NewMkLine(NewLine("Makefile", 85, "\t${RUN} ${SED} 's,#,// comment:,g' fname > fname.tmp; ${MV} fname.tmp fname", nil)))
shline.CheckShellCommandLine(shline.mkline.Shellcmd())
c.Check(s.Output(), equals, "NOTE: Makefile:85: Please use the SUBST framework instead of ${SED} and ${MV}.\n")
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine__subshell(c *check.C) {
shline := NewShellLine(NewMkLine(NewLine("Makefile", 85, "\t${RUN} uname=$$(uname)", nil)))
shline.CheckShellCommandLine(shline.mkline.Shellcmd())
c.Check(s.Output(), equals, "WARN: Makefile:85: Invoking subshells via $(...) is not portable enough.\n")
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine__install_dir(c *check.C) {
shline := NewShellLine(NewMkLine(NewLine("Makefile", 85, "\t${RUN} ${INSTALL_DATA_DIR} ${DESTDIR}${PREFIX}/dir1 ${DESTDIR}${PREFIX}/dir2", nil)))
shline.CheckShellCommandLine(shline.mkline.Shellcmd())
c.Check(s.Output(), equals, ""+
"NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL_DATA_DIR}\".\n"+
"NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL_DATA_DIR}\".\n"+
"WARN: Makefile:85: The INSTALL_*_DIR commands can only handle one directory at a time.\n")
shline.CheckShellCommandLine("${INSTALL_DATA_DIR} -d -m 0755 ${DESTDIR}${PREFIX}/share/examples/gdchart")
// No warning about multiple directories, since 0755 is an option, not an argument.
c.Check(s.Output(), equals, ""+
"NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= share/examples/gdchart\" instead of \"${INSTALL_DATA_DIR}\".\n")
shline.CheckShellCommandLine("${INSTALL_DATA_DIR} -d -m 0755 ${DESTDIR}${PREFIX}/dir1 ${PREFIX}/dir2")
c.Check(s.Output(), equals, ""+
"NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL_DATA_DIR}\".\n"+
"NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL_DATA_DIR}\".\n"+
"WARN: Makefile:85: The INSTALL_*_DIR commands can only handle one directory at a time.\n")
}
func (s *Suite) Test_ShellLine_CheckShellCommandLine__install_option_d(c *check.C) {
shline := NewShellLine(NewMkLine(NewLine("Makefile", 85, "\t${RUN} ${INSTALL} -d ${DESTDIR}${PREFIX}/dir1 ${DESTDIR}${PREFIX}/dir2", nil)))
shline.CheckShellCommandLine(shline.mkline.Shellcmd())
c.Check(s.Output(), equals, ""+
"NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL} -d\".\n"+
"NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL} -d\".\n")
}
func (s *Suite) Test_ShellLine__shell_comment_with_line_continuation(c *check.C) {
tmpfile := s.CreateTmpFile(c, "Makefile", ""+
"# $"+"NetBSD$\n"+
"pre-install:\n"+
"\t"+"# comment\\\n"+
"\t"+"echo \"hello\"\n")
lines := LoadNonemptyLines(tmpfile, true)
NewMkLines(lines).Check()
c.Check(s.Output(), equals, "WARN: ~/Makefile:3--4: A shell comment does not stop at the end of line.\n")
}
func (s *Suite) Test_ShellLine_unescapeBackticks(c *check.C) {
shline := NewShellLine(NewMkLine(dummyLine))
// foobar="`echo \"foo bar\"`"
text := "foobar=\"`echo \\\"foo bar\\\"`\""
repl := NewPrefixReplacer(text)
repl.AdvanceStr("foobar=\"`")
backtCommand, newQuoting := shline.unescapeBackticks(text, repl, shqDquotBackt)
c.Check(backtCommand, equals, "echo \"foo bar\"")
c.Check(newQuoting, equals, shqDquot)
c.Check(repl.rest, equals, "\"")
}