summaryrefslogtreecommitdiff
path: root/pkgtools
diff options
context:
space:
mode:
authorrillig <rillig@pkgsrc.org>2016-01-31 17:18:54 +0000
committerrillig <rillig@pkgsrc.org>2016-01-31 17:18:54 +0000
commit1a4a9a096ed8227b9019fd8484d76a90e297bc36 (patch)
tree2cea5cda3a43d1e9538d94a1ad05b89d113cb290 /pkgtools
parent72f7889e530a4709b7bac8c88dcf635ff9f781a0 (diff)
downloadpkgsrc-1a4a9a096ed8227b9019fd8484d76a90e297bc36.tar.gz
Updated pkglint to 5.3.5
Changes since 5.3.4: * Added parser for Makefile conditionals * Variables that are matched using the :M modifier are checked whether the matched value is sensible * Reworded and explained warning for variable ordering in packages * Fixed bug in Tree.String * Fixed a few variable types
Diffstat (limited to 'pkgtools')
-rw-r--r--pkgtools/pkglint/Makefile4
-rw-r--r--pkgtools/pkglint/TODO5
-rw-r--r--pkgtools/pkglint/files/globaldata.go3
-rw-r--r--pkgtools/pkglint/files/mkline.go72
-rw-r--r--pkgtools/pkglint/files/mkline_test.go40
-rw-r--r--pkgtools/pkglint/files/mklines.go30
-rw-r--r--pkgtools/pkglint/files/package.go5
-rw-r--r--pkgtools/pkglint/files/package_test.go4
-rw-r--r--pkgtools/pkglint/files/parser.go133
-rw-r--r--pkgtools/pkglint/files/parser_test.go77
-rw-r--r--pkgtools/pkglint/files/tree.go54
-rw-r--r--pkgtools/pkglint/files/tree_test.go19
-rw-r--r--pkgtools/pkglint/files/util.go3
-rw-r--r--pkgtools/pkglint/files/vardefs.go34
-rw-r--r--pkgtools/pkglint/files/vartypecheck.go91
-rw-r--r--pkgtools/pkglint/files/vartypecheck_test.go35
16 files changed, 421 insertions, 188 deletions
diff --git a/pkgtools/pkglint/Makefile b/pkgtools/pkglint/Makefile
index 69b87d96636..af4a7fb08b0 100644
--- a/pkgtools/pkglint/Makefile
+++ b/pkgtools/pkglint/Makefile
@@ -1,6 +1,6 @@
-# $NetBSD: Makefile,v 1.480 2016/01/27 21:55:50 rillig Exp $
+# $NetBSD: Makefile,v 1.481 2016/01/31 17:18:54 rillig Exp $
-PKGNAME= pkglint-5.3.4
+PKGNAME= pkglint-5.3.5
DISTFILES= # none
CATEGORIES= pkgtools
diff --git a/pkgtools/pkglint/TODO b/pkgtools/pkglint/TODO
index 8291cf30fe2..af556720868 100644
--- a/pkgtools/pkglint/TODO
+++ b/pkgtools/pkglint/TODO
@@ -1,11 +1,10 @@
-$NetBSD: TODO,v 1.83 2015/11/25 13:29:07 rillig Exp $
+$NetBSD: TODO,v 1.84 2016/01/31 17:18:54 rillig Exp $
Please add your own entries at the bottom of this file. If possible,
include the name of an example package where a warning should occur.
* warn about the use of ${WRKDIR:=...}, as this construct should only
be used with lists.
-* Add checks for binary packages. See Debian/lintian for ideas.
* Of the user-defined variables, some may be used at load-time and some
don't. Find out how pkglint can distinguish them.
* Make sure that no variable is modified at load-time after it has been
@@ -19,8 +18,6 @@ include the name of an example package where a warning should occur.
depend on the same option in the buildlink3.mk file.
* Complain about ${PKGSRC_COMPILER} == "sunpro", which should be
!empty(PKGSRC_COMPILER:Msunpro).
-* If USE_TOOLS has autoconf213, and the package does stuff like
- cd ${WRKSRC} && autoconf, then an incorrect warning is issued.
* don't complain about "procedure calls", like for pkg-build-options in
the various buildlink3.mk files.
* if package A conflicts with B, then B should also conflict with A.
diff --git a/pkgtools/pkglint/files/globaldata.go b/pkgtools/pkglint/files/globaldata.go
index d86f67e8952..5d2d33a4433 100644
--- a/pkgtools/pkglint/files/globaldata.go
+++ b/pkgtools/pkglint/files/globaldata.go
@@ -532,5 +532,8 @@ func (gd *GlobalData) loadDeprecatedVars() {
// October 2014
"SVR4_PKGNAME": "Just remove it.",
"PKG_INSTALLATION_TYPES": "Just remove it.",
+
+ // January 2016
+ "SUBST_POSTCMD.*": "Has been removed, as it seemed unused.",
}
}
diff --git a/pkgtools/pkglint/files/mkline.go b/pkgtools/pkglint/files/mkline.go
index 599fbb0d539..aa51d9d4fe2 100644
--- a/pkgtools/pkglint/files/mkline.go
+++ b/pkgtools/pkglint/files/mkline.go
@@ -811,32 +811,37 @@ func (mkline *MkLine) CheckText(text string) {
}
}
-func (mkline *MkLine) CheckIf() {
+func (mkline *MkLine) CheckCond() {
if G.opts.DebugTrace {
defer tracecall0()()
}
- tree := mkline.parseMkCond(mkline.Args())
+ p := NewParser(mkline.Args())
+ cond := p.MkCond()
+ if !p.EOF() {
+ mkline.Warn1("Invalid conditional %q.", mkline.Args())
+ return
+ }
- {
- var pvarname, ppattern *string
- if tree.Match(NewTree("not", NewTree("empty", NewTree("match", &pvarname, &ppattern)))) {
- vartype := mkline.getVariableType(*pvarname)
- if vartype != nil && vartype.checker.IsEnum() {
- if !matches(*ppattern, `[\$\[*]`) && !vartype.checker.HasEnum(*ppattern) {
- mkline.Warn2("Invalid :M value %q. Only { %s } are allowed.", *ppattern, vartype.checker.AllowedEnums())
- }
+ cond.Visit("empty", func(node *Tree) {
+ varuse := node.args[0].(MkVarUse)
+ varname := varuse.varname
+ for _, modifier := range varuse.modifiers {
+ if modifier[0] == 'M' || modifier[0] == 'N' {
+ mkline.CheckVartype(varname, opUseMatch, modifier[1:], "")
}
- return
}
- }
-
- {
- var pop, pvarname, pvalue *string
- if tree.Match(NewTree("compareVarStr", &pvarname, &pop, &pvalue)) {
- mkline.CheckVartype(*pvarname, opUse, *pvalue, "")
+ })
+
+ cond.Visit("compareVarStr", func(node *Tree) {
+ varuse := node.args[0].(MkVarUse)
+ varname := varuse.varname
+ varmods := varuse.modifiers
+ value := node.args[2].(string)
+ if len(varmods) == 0 || len(varmods) == 1 && matches(varmods[0], `^[MN]`) && value != "" {
+ mkline.CheckVartype(varname, opUseMatch, value, "")
}
- }
+ })
}
func (mkline *MkLine) CheckValidCharactersInValue(reValid string) {
@@ -950,37 +955,6 @@ func matchMkCond(text string) (m bool, indent, directive, args string) {
return
}
-func (mkline *MkLine) parseMkCond(cond string) *Tree {
- if G.opts.DebugTrace {
- defer tracecall1(cond)()
- }
-
- const (
- repartVarname = `[A-Z_][A-Z0-9_]*(?:\.[\w_+\-]+)?`
- reDefined = `^defined\((` + repartVarname + `)\)`
- reEmpty = `^empty\((` + repartVarname + `)\)`
- reEmptyMatch = `^empty\((` + repartVarname + `):M([^\$:{})]+)\)`
- reCompare = `^\$\{(` + repartVarname + `)\}\s+(==|!=)\s+"([^"\$\\]*)"`
- )
-
- if m, rest := replaceFirst(cond, `^!`, ""); m != nil {
- return NewTree("not", mkline.parseMkCond(rest))
- }
- if m, rest := replaceFirst(cond, reDefined, ""); m != nil {
- return NewTree("defined", mkline.parseMkCond(rest))
- }
- if m, _ := replaceFirst(cond, reEmpty, ""); m != nil {
- return NewTree("empty", m[1])
- }
- if m, _ := replaceFirst(cond, reEmptyMatch, ""); m != nil {
- return NewTree("empty", NewTree("match", m[1], m[2]))
- }
- if m, _ := replaceFirst(cond, reCompare, ""); m != nil {
- return NewTree("compareVarStr", m[1], m[2], m[3])
- }
- return NewTree("unknown", cond)
-}
-
type NeedsQuoting uint8
const (
diff --git a/pkgtools/pkglint/files/mkline_test.go b/pkgtools/pkglint/files/mkline_test.go
index 74b01ddbd44..2f71f5d066e 100644
--- a/pkgtools/pkglint/files/mkline_test.go
+++ b/pkgtools/pkglint/files/mkline_test.go
@@ -305,39 +305,31 @@ func (s *Suite) TestMkLine_checkVarassign(c *check.C) {
c.Check(s.Output(), equals, "")
}
-func (s *Suite) TestParseMkCond_NotEmptyMatch(c *check.C) {
- mkline := NewMkLine(NewLine("fname", 1, ".if !empty(USE_LIBTOOL:M[Yy][Ee][Ss])", nil))
-
- cond := mkline.parseMkCond(mkline.Args())
-
- c.Check(cond, check.DeepEquals, NewTree("not", NewTree("empty", NewTree("match", "USE_LIBTOOL", "[Yy][Ee][Ss]"))))
-}
-
-func (s *Suite) TestParseMkCond_Compare(c *check.C) {
- mkline := NewMkLine(NewLine("fname", 1, ".if ${VARNAME} != \"Value\"", nil))
-
- cond := mkline.parseMkCond(mkline.Args())
-
- c.Check(cond, check.DeepEquals, NewTree("compareVarStr", "VARNAME", "!=", "Value"))
-}
-
func (s *Suite) TestChecklineMkCondition(c *check.C) {
s.UseCommandLine(c, "-Wtypes")
G.globalData.InitVartypes()
- NewMkLine(NewLine("fname", 1, ".if !empty(PKGSRC_COMPILER:Mmycc)", nil)).CheckIf()
+ NewMkLine(NewLine("fname", 1, ".if !empty(PKGSRC_COMPILER:Mmycc)", nil)).CheckCond()
- c.Check(s.Stdout(), equals, "WARN: fname:1: Invalid :M value \"mycc\". "+
- "Only { ccache ccc clang distcc f2c gcc hp icc ido gcc mipspro "+
- "mipspro-ucode pcc sunpro xlc } are allowed.\n")
+ c.Check(s.Stdout(), equals, "WARN: fname:1: The pattern \"mycc\" cannot match any of "+
+ "{ ccache ccc clang distcc f2c gcc hp icc ido "+
+ "gcc mipspro mipspro-ucode pcc sunpro xlc } for PKGSRC_COMPILER.\n")
- NewMkLine(NewLine("fname", 1, ".elif ${A} != ${B}", nil)).CheckIf()
+ NewMkLine(NewLine("fname", 1, ".elif ${A} != ${B}", nil)).CheckCond()
- c.Check(s.Stdout(), equals, "") // Unknown condition types are silently ignored
+ c.Check(s.Stdout(), equals, "")
- NewMkLine(NewLine("fname", 1, ".if ${HOMEPAGE} == \"mailto:someone@example.org\"", nil)).CheckIf()
+ NewMkLine(NewLine("fname", 1, ".if ${HOMEPAGE} == \"mailto:someone@example.org\"", nil)).CheckCond()
c.Check(s.Output(), equals, "WARN: fname:1: \"mailto:someone@example.org\" is not a valid URL.\n")
+
+ NewMkLine(NewLine("fname", 1, ".if !empty(PKGSRC_RUN_TEST:M[Y][eE][sS])", nil)).CheckCond()
+
+ c.Check(s.Output(), equals, "WARN: fname:1: PKGSRC_RUN_TEST should be matched against \"[yY][eE][sS]\" or \"[nN][oO]\", not \"[Y][eE][sS]\".\n")
+
+ NewMkLine(NewLine("fname", 1, ".if !empty(IS_BUILTIN.Xfixes:M[yY][eE][sS])", nil)).CheckCond()
+
+ c.Check(s.Output(), equals, "")
}
func (s *Suite) TestMkLine_variableNeedsQuoting(c *check.C) {
@@ -443,7 +435,7 @@ func (s *Suite) TestMkLine_CheckVarusePermissions(c *check.C) {
"WARN: options.mk:2: The user-defined variable GAMES_USER is used but not added to BUILD_DEFS.\n"+
"WARN: options.mk:3: PKGBASE should not be evaluated at load time.\n"+
"WARN: options.mk:4: The variable PYPKGPREFIX may not be set in this file; it would be ok in pyversion.mk.\n"+
- "WARN: options.mk:4: \"${PKGBASE}\" is not valid for PYPKGPREFIX. Use one of { py27 py33 py34 } instead.\n"+
+ "WARN: options.mk:4: \"${PKGBASE}\" is not valid for PYPKGPREFIX. Use one of { py27 py33 py34 py35 } instead.\n"+
"WARN: options.mk:4: PKGBASE should not be evaluated indirectly at load time.\n"+
"NOTE: options.mk:4: This variable value should be aligned to column 17.\n")
}
diff --git a/pkgtools/pkglint/files/mklines.go b/pkgtools/pkglint/files/mklines.go
index be9ac0ff354..eda58e239a1 100644
--- a/pkgtools/pkglint/files/mklines.go
+++ b/pkgtools/pkglint/files/mklines.go
@@ -42,13 +42,13 @@ func NewMkLines(lines []*Line) *MkLines {
tools}
}
-func (mklines *MkLines) IndentDepth() int {
+func (mklines *MkLines) indentDepth() int {
return mklines.indentation[len(mklines.indentation)-1]
}
-func (mklines *MkLines) PopIndent() {
+func (mklines *MkLines) popIndent() {
mklines.indentation = mklines.indentation[:len(mklines.indentation)-1]
}
-func (mklines *MkLines) PushIndent(indent int) {
+func (mklines *MkLines) pushIndent(indent int) {
mklines.indentation = append(mklines.indentation, indent)
}
@@ -88,8 +88,8 @@ func (mklines *MkLines) Check() {
defer func() { G.Mk = nil }()
allowedTargets := make(map[string]bool)
- prefixes := splitOnSpace("pre do post")
- actions := splitOnSpace("fetch extract patch tools wrapper configure build test install package clean")
+ prefixes := [...]string{"pre", "do", "post"}
+ actions := [...]string{"fetch", "extract", "patch", "tools", "wrapper", "configure", "build", "test", "install", "package", "clean"}
for _, prefix := range prefixes {
for _, action := range actions {
allowedTargets[prefix+"-"+action] = true
@@ -105,8 +105,8 @@ func (mklines *MkLines) Check() {
mklines.lines[0].CheckRcsid(`#\s+`, "# ")
- substcontext := new(SubstContext)
- varalign := new(VaralignBlock)
+ var substcontext SubstContext
+ var varalign VaralignBlock
for _, mkline := range mklines.mklines {
mkline.Line.CheckTrailingWhitespace()
mkline.Line.CheckValidCharacters(`[\t -~]`)
@@ -143,8 +143,8 @@ func (mklines *MkLines) Check() {
ChecklinesTrailingEmptyLines(mklines.lines)
- if len(mklines.indentation) != 1 && mklines.IndentDepth() != 0 {
- lastMkline.Line.Errorf("Directive indentation is not 0, but %d.", mklines.IndentDepth())
+ if len(mklines.indentation) != 1 && mklines.indentDepth() != 0 {
+ lastMkline.Line.Errorf("Directive indentation is not 0, but %d.", mklines.indentDepth())
}
SaveAutofixChanges(mklines.lines)
@@ -223,24 +223,24 @@ func (mklines *MkLines) checklineCond(mkline *MkLine) {
switch directive {
case "endif", "endfor", "elif", "else":
if len(mklines.indentation) > 1 {
- mklines.PopIndent()
+ mklines.popIndent()
} else {
mkline.Error1("Unmatched .%s.", directive)
}
}
// Check the indentation
- if expected := strings.Repeat(" ", mklines.IndentDepth()); indent != expected {
+ if expected := strings.Repeat(" ", mklines.indentDepth()); indent != expected {
if G.opts.WarnSpace && !mkline.Line.AutofixReplace("."+indent, "."+expected) {
- mkline.Line.Notef("This directive should be indented by %d spaces.", mklines.IndentDepth())
+ mkline.Line.Notef("This directive should be indented by %d spaces.", mklines.indentDepth())
}
}
if directive == "if" && matches(args, `^!defined\([\w]+_MK\)$`) {
- mklines.PushIndent(mklines.IndentDepth())
+ mklines.pushIndent(mklines.indentDepth())
} else if matches(directive, `^(?:if|ifdef|ifndef|for|elif|else)$`) {
- mklines.PushIndent(mklines.IndentDepth() + 2)
+ mklines.pushIndent(mklines.indentDepth() + 2)
}
reDirectivesWithArgs := `^(?:if|ifdef|ifndef|elif|for|undef)$`
@@ -255,7 +255,7 @@ func (mklines *MkLines) checklineCond(mkline *MkLine) {
}
} else if directive == "if" || directive == "elif" {
- mkline.CheckIf()
+ mkline.CheckCond()
} else if directive == "ifdef" || directive == "ifndef" {
if matches(args, `\s`) {
diff --git a/pkgtools/pkglint/files/package.go b/pkgtools/pkglint/files/package.go
index a2205397407..4419db20092 100644
--- a/pkgtools/pkglint/files/package.go
+++ b/pkgtools/pkglint/files/package.go
@@ -683,7 +683,10 @@ func (pkg *Package) ChecklinesPackageMakefileVarorder(mklines *MkLines) {
default:
for varindex < len(vars) {
if vars[varindex].count == once && !maySkipSection {
- line.Warn1("%s should be set here.", vars[varindex].varname)
+ line.Warn1("The canonical position for the required variable %s is here.", vars[varindex].varname)
+ Explain(
+ "See doc/Makefile-example or the pkgsrc guide, section",
+ "\"Package components\", subsection \"Makefile\" for more information.")
}
below[vars[varindex].varname] = belowWhat
varindex++
diff --git a/pkgtools/pkglint/files/package_test.go b/pkgtools/pkglint/files/package_test.go
index ebebfa9bb67..cf753fe17e9 100644
--- a/pkgtools/pkglint/files/package_test.go
+++ b/pkgtools/pkglint/files/package_test.go
@@ -39,8 +39,8 @@ func (s *Suite) TestChecklinesPackageMakefileVarorder(c *check.C) {
".include \"../../mk/bsd.pkg.mk\""))
c.Check(s.Output(), equals, ""+
- "WARN: Makefile:6: COMMENT should be set here.\n"+
- "WARN: Makefile:6: LICENSE should be set here.\n")
+ "WARN: Makefile:6: The canonical position for the required variable COMMENT is here.\n"+
+ "WARN: Makefile:6: The canonical position for the required variable LICENSE is here.\n")
}
func (s *Suite) TestGetNbpart(c *check.C) {
diff --git a/pkgtools/pkglint/files/parser.go b/pkgtools/pkglint/files/parser.go
index 49fadbcc402..0b8b3b98206 100644
--- a/pkgtools/pkglint/files/parser.go
+++ b/pkgtools/pkglint/files/parser.go
@@ -280,3 +280,136 @@ func (p *Parser) VarUseModifiers(closing string) []string {
}
return modifiers
}
+
+func (p *Parser) MkCond() *Tree {
+ return p.mkCondOr()
+}
+
+func (p *Parser) mkCondOr() *Tree {
+ and := p.mkCondAnd()
+ if and == nil {
+ return nil
+ }
+
+ ands := append([]interface{}(nil), and)
+ for {
+ mark := p.repl.Mark()
+ if !p.repl.AdvanceRegexp(`^\s*\|\|\s*`) {
+ break
+ }
+ next := p.mkCondAnd()
+ if next == nil {
+ p.repl.Reset(mark)
+ break
+ }
+ ands = append(ands, next)
+ }
+ if len(ands) == 1 {
+ return and
+ }
+ return NewTree("or", ands...)
+}
+
+func (p *Parser) mkCondAnd() *Tree {
+ atom := p.mkCondAtom()
+ if atom == nil {
+ return nil
+ }
+
+ atoms := append([]interface{}(nil), atom)
+ for {
+ mark := p.repl.Mark()
+ if !p.repl.AdvanceRegexp(`^\s*&&\s*`) {
+ break
+ }
+ next := p.mkCondAtom()
+ if next == nil {
+ p.repl.Reset(mark)
+ break
+ }
+ atoms = append(atoms, next)
+ }
+ if len(atoms) == 1 {
+ return atom
+ }
+ return NewTree("and", atoms...)
+}
+
+func (p *Parser) mkCondAtom() *Tree {
+ if G.opts.DebugTrace {
+ defer tracecall1(p.Rest())()
+ }
+
+ repl := p.repl
+ mark := repl.Mark()
+ repl.SkipSpace()
+ switch {
+ case repl.AdvanceStr("!"):
+ cond := p.mkCondAtom()
+ if cond != nil {
+ return NewTree("not", cond)
+ }
+ case repl.AdvanceStr("("):
+ cond := p.MkCond()
+ if cond != nil {
+ repl.SkipSpace()
+ if repl.AdvanceStr(")") {
+ return cond
+ }
+ }
+ case repl.AdvanceRegexp(`^defined\s*\(`):
+ if varname := p.Varname(); varname != "" {
+ if repl.AdvanceStr(")") {
+ return NewTree("defined", varname)
+ }
+ }
+ case repl.AdvanceRegexp(`^empty\s*\(`):
+ if varname := p.Varname(); varname != "" {
+ modifiers := p.VarUseModifiers(")")
+ if repl.AdvanceStr(")") {
+ return NewTree("empty", MkVarUse{varname, modifiers})
+ }
+ }
+ case repl.AdvanceRegexp(`^(commands|exists|make|target)\s*\(`):
+ funcname := repl.m[1]
+ argMark := repl.Mark()
+ for p.VarUse() != nil || repl.AdvanceRegexp(`^[^$)]+`) {
+ }
+ arg := repl.Since(argMark)
+ if repl.AdvanceStr(")") {
+ return NewTree(funcname, arg)
+ }
+ default:
+ lhs := p.VarUse()
+ mark := repl.Mark()
+ if lhs == nil && repl.AdvanceStr("\"") {
+ if quotedLhs := p.VarUse(); quotedLhs != nil && repl.AdvanceStr("\"") {
+ lhs = quotedLhs
+ } else {
+ repl.Reset(mark)
+ }
+ }
+ if lhs != nil {
+ if repl.AdvanceRegexp(`^\s*(<|<=|==|!=|>=|>)\s*(\d+(?:\.\d+)?)`) {
+ return NewTree("compareVarNum", *lhs, repl.m[1], repl.m[2])
+ }
+ if repl.AdvanceRegexp(`^\s*(<|<=|==|!=|>=|>)\s*`) {
+ op := repl.m[1]
+ if (op == "!=" || op == "==") && repl.AdvanceRegexp(`^"([^"\$\\]*)"`) {
+ return NewTree("compareVarStr", *lhs, op, repl.m[1])
+ } else if repl.AdvanceRegexp(`^\w+`) {
+ return NewTree("compareVarStr", *lhs, op, repl.m[0])
+ } else if rhs := p.VarUse(); rhs != nil {
+ return NewTree("compareVarVar", *lhs, op, *rhs)
+ }
+ } else {
+ return NewTree("not", NewTree("empty", *lhs)) // See devel/bmake/files/cond.c:/\* For \.if \$/
+ }
+ }
+ if repl.AdvanceRegexp(`^\d+(?:\.\d+)?`) {
+ return NewTree("literalNum", repl.m[0])
+ }
+ }
+ repl.Reset(mark)
+ return nil
+}
diff --git a/pkgtools/pkglint/files/parser_test.go b/pkgtools/pkglint/files/parser_test.go
index 6c7dad7d263..72dd8b8d12f 100644
--- a/pkgtools/pkglint/files/parser_test.go
+++ b/pkgtools/pkglint/files/parser_test.go
@@ -138,3 +138,80 @@ func (s *Suite) TestParser_MkTokens(c *check.C) {
parse("${VAR)", nil, "${VAR)") // Opening brace, closing parenthesis
parse("$(VAR}", nil, "$(VAR}") // Opening parenthesis, closing brace
}
+
+func (s *Suite) TestParser_MkCond_Basics(c *check.C) {
+ condrest := func(input string, expectedTree *Tree, expectedRest string) {
+ p := NewParser(input)
+ actualTree := p.MkCond()
+ c.Check(actualTree, deepEquals, expectedTree)
+ c.Check(p.Rest(), equals, expectedRest)
+ }
+ cond := func(input string, expectedTree *Tree) {
+ condrest(input, expectedTree, "")
+ }
+ literal := func(literal string) MkToken {
+ return MkToken{literal: literal}
+ }
+ varuse := func(varname string, modifiers ...string) MkVarUse {
+ return MkVarUse{varname: varname, modifiers: modifiers}
+ }
+ _, _ = literal, varuse
+
+ cond("${OPSYS:MNetBSD}",
+ NewTree("not", NewTree("empty", varuse("OPSYS", "MNetBSD"))))
+ cond("defined(VARNAME)",
+ NewTree("defined", "VARNAME"))
+ cond("empty(VARNAME)",
+ NewTree("empty", varuse("VARNAME")))
+ cond("!empty(VARNAME)",
+ NewTree("not", NewTree("empty", varuse("VARNAME"))))
+ cond("!empty(VARNAME:M[yY][eE][sS])",
+ NewTree("not", NewTree("empty", varuse("VARNAME", "M[yY][eE][sS]"))))
+ cond("${VARNAME} != \"Value\"",
+ NewTree("compareVarStr", varuse("VARNAME"), "!=", "Value"))
+ cond("${VARNAME:Mi386} != \"Value\"",
+ NewTree("compareVarStr", varuse("VARNAME", "Mi386"), "!=", "Value"))
+ cond("${VARNAME} != Value",
+ NewTree("compareVarStr", varuse("VARNAME"), "!=", "Value"))
+ cond("\"${VARNAME}\" != Value",
+ NewTree("compareVarStr", varuse("VARNAME"), "!=", "Value"))
+ cond("(defined(VARNAME))",
+ NewTree("defined", "VARNAME"))
+ cond("exists(/etc/hosts)",
+ NewTree("exists", "/etc/hosts"))
+ cond("exists(${PREFIX}/var)",
+ NewTree("exists", "${PREFIX}/var"))
+ cond("${OPSYS} == \"NetBSD\" || ${OPSYS} == \"OpenBSD\"",
+ NewTree("or",
+ NewTree("compareVarStr", varuse("OPSYS"), "==", "NetBSD"),
+ NewTree("compareVarStr", varuse("OPSYS"), "==", "OpenBSD")))
+ cond("${OPSYS} == \"NetBSD\" && ${MACHINE_ARCH} == \"i386\"",
+ NewTree("and",
+ NewTree("compareVarStr", varuse("OPSYS"), "==", "NetBSD"),
+ NewTree("compareVarStr", varuse("MACHINE_ARCH"), "==", "i386")))
+ cond("defined(A) && defined(B) || defined(C) && defined(D)",
+ NewTree("or",
+ NewTree("and",
+ NewTree("defined", "A"),
+ NewTree("defined", "B")),
+ NewTree("and",
+ NewTree("defined", "C"),
+ NewTree("defined", "D"))))
+
+ // Exotic cases
+ cond("0",
+ NewTree("literalNum", "0"))
+ cond("! ( defined(A) && empty(VARNAME) )",
+ NewTree("not", NewTree("and", NewTree("defined", "A"), NewTree("empty", varuse("VARNAME")))))
+ cond("${REQD_MAJOR} > ${MAJOR}",
+ NewTree("compareVarVar", varuse("REQD_MAJOR"), ">", varuse("MAJOR")))
+ cond("${OS_VERSION} >= 6.5",
+ NewTree("compareVarNum", varuse("OS_VERSION"), ">=", "6.5"))
+ cond("${OS_VERSION} == 5.3",
+ NewTree("compareVarNum", varuse("OS_VERSION"), "==", "5.3"))
+
+ // Errors
+ condrest("!empty(PKG_OPTIONS:Msndfile) || defined(PKG_OPTIONS:Msamplerate)",
+ NewTree("not", NewTree("empty", varuse("PKG_OPTIONS", "Msndfile"))),
+ " || defined(PKG_OPTIONS:Msamplerate)")
+}
diff --git a/pkgtools/pkglint/files/tree.go b/pkgtools/pkglint/files/tree.go
index b1377f88523..4ceeb525e07 100644
--- a/pkgtools/pkglint/files/tree.go
+++ b/pkgtools/pkglint/files/tree.go
@@ -13,46 +13,6 @@ func NewTree(name string, args ...interface{}) *Tree {
return &Tree{name, args}
}
-// Checks whether this tree matches the given pattern, and if so,
-// copies the corresponding nodes from the tree to the pattern.
-// If the match is partially successful, some or all of the variables
-// may have been copied or not.
-func (t *Tree) Match(pattern *Tree) bool {
- if G.opts.DebugTrace {
- defer tracecall(t, pattern)()
- }
- if t.name != pattern.name || len(t.args) != len(pattern.args) {
- return false
- }
-
- for i, targ := range t.args {
- parg := pattern.args[i]
- switch parg := parg.(type) {
- case *Tree:
- if targ, ok := targ.(*Tree); ok {
- if !targ.Match(parg) {
- return false
- }
- } else {
- return false
- }
- case **string:
- if targ, ok := targ.(string); ok {
- if *parg == nil {
- *parg = &targ
- } else if **parg != targ {
- return false
- }
- } else {
- return false
- }
- default:
- return false
- }
- }
- return true
-}
-
func (t *Tree) String() string {
s := "(" + t.name
for _, arg := range t.args {
@@ -63,9 +23,19 @@ func (t *Tree) String() string {
if arg, ok := arg.(string); ok {
s += fmt.Sprintf(" %q", arg)
continue
- } else {
- s += fmt.Sprintf(" %v", arg)
}
+ s += fmt.Sprintf(" %v", arg)
}
return s + ")"
}
+
+func (t *Tree) Visit(nodename string, action func(t *Tree)) {
+ if t.name == nodename {
+ action(t)
+ }
+ for _, arg := range t.args {
+ if child, ok := arg.(*Tree); ok {
+ child.Visit(nodename, action)
+ }
+ }
+}
diff --git a/pkgtools/pkglint/files/tree_test.go b/pkgtools/pkglint/files/tree_test.go
index 4bf0ccf8f3c..d7d8716287a 100644
--- a/pkgtools/pkglint/files/tree_test.go
+++ b/pkgtools/pkglint/files/tree_test.go
@@ -4,25 +4,6 @@ import (
check "gopkg.in/check.v1"
)
-func (s *Suite) TestTreeMatch_successful(c *check.C) {
- var varname *string
- pattern := NewTree("not", NewTree("empty", &varname))
- tree := NewTree("not", NewTree("empty", "VARNAME"))
-
- c.Check(tree.Match(pattern), equals, true)
- c.Assert(varname, check.Not(equals), nil)
- c.Check(*varname, equals, "VARNAME")
-}
-
-func (s *Suite) TestTreeMatch_fails(c *check.C) {
- var varname *string
- pattern := NewTree("not", NewTree("empty", &varname))
- tree := NewTree("not", NewTree("full", "VARNAME"))
-
- c.Check(tree.Match(pattern), equals, false)
- c.Check(varname, equals, (*string)(nil))
-}
-
func (s *Suite) TestTreeString(c *check.C) {
c.Check(NewTree("not", NewTree("empty", "varname")).String(), equals, "(not (empty \"varname\"))")
}
diff --git a/pkgtools/pkglint/files/util.go b/pkgtools/pkglint/files/util.go
index e323091c426..55d4dc8f62e 100644
--- a/pkgtools/pkglint/files/util.go
+++ b/pkgtools/pkglint/files/util.go
@@ -357,6 +357,9 @@ func (pr *PrefixReplacer) Reset(mark string) {
func (pr *PrefixReplacer) Skip(n int) {
pr.rest = pr.rest[n:]
}
+func (pr *PrefixReplacer) SkipSpace() {
+ pr.rest = strings.TrimLeft(pr.rest, " \t")
+}
func (pr *PrefixReplacer) Since(mark string) string {
return mark[:len(mark)-len(pr.rest)]
}
diff --git a/pkgtools/pkglint/files/vardefs.go b/pkgtools/pkglint/files/vardefs.go
index b73307a7be6..3352c07ee91 100644
--- a/pkgtools/pkglint/files/vardefs.go
+++ b/pkgtools/pkglint/files/vardefs.go
@@ -29,7 +29,7 @@ func (gd *GlobalData) InitVartypes() {
usr("PKGSRC_SLEEPSECS", lkNone, CheckvarInteger)
usr("USETBL", lkNone, CheckvarYes)
usr("ABI", lkNone, enum("32 64"))
- usr("PKG_DEVELOPER", lkNone, CheckvarYes)
+ usr("PKG_DEVELOPER", lkNone, CheckvarYesNo)
usr("USE_ABI_DEPENDS", lkNone, CheckvarYesNo)
usr("PKG_REGISTER_SHELLS", lkNone, enum("YES NO"))
usr("PKGSRC_COMPILER", lkShell, enum("ccache ccc clang distcc f2c gcc hp icc ido gcc mipspro mipspro-ucode pcc sunpro xlc"))
@@ -270,11 +270,11 @@ func (gd *GlobalData) InitVartypes() {
sys("EMULSUBDIR", lkNone, CheckvarPathname)
sys("OPSYS_EMULDIR", lkNone, CheckvarPathname)
sys("EMULSUBDIRSLASH", lkNone, CheckvarPathname)
- sys("EMUL_ARCH", lkNone, enum("i386 none x86_64"))
+ sys("EMUL_ARCH", lkNone, enum("arm i386 m68k none ns32k sparc vax x86_64"))
sys("EMUL_DISTRO", lkNone, CheckvarIdentifier)
sys("EMUL_IS_NATIVE", lkNone, CheckvarYes)
pkg("EMUL_MODULES.*", lkShell, CheckvarIdentifier)
- sys("EMUL_OPSYS", lkNone, enum("freebsd hpux irix linux osf1 solaris sunos none"))
+ sys("EMUL_OPSYS", lkNone, enum("darwin freebsd hpux irix linux osf1 solaris sunos none"))
pkg("EMUL_PKG_FMT", lkNone, enum("plain rpm"))
usr("EMUL_PLATFORM", lkNone, CheckvarEmulPlatform)
pkg("EMUL_PLATFORMS", lkShell, CheckvarEmulPlatform)
@@ -537,7 +537,7 @@ func (gd *GlobalData) InitVartypes() {
acl("PKG_HACKS", lkShell, CheckvarIdentifier, "hacks.mk: append")
sys("PKG_INFO", lkNone, CheckvarShellCommand)
sys("PKG_JAVA_HOME", lkNone, CheckvarPathname)
- jvms := enum("blackdown-jdk13 jdk jdk14 kaffe run-jdk13 sun-jdk14 sun-jdk15 sun-jdk6 openjdk7 openjdk7-bin sun-jdk7")
+ jvms := enum("blackdown-jdk13 jdk jdk14 kaffe run-jdk13 sun-jdk14 sun-jdk15 sun-jdk6 openjdk7 openjdk7-bin sun-jdk7 openjdk8 oracle-jdk8")
sys("PKG_JVM", lkNone, jvms)
acl("PKG_JVMS_ACCEPTED", lkShell, jvms, "Makefile: set; Makefile.common: default, set")
usr("PKG_JVM_DEFAULT", lkNone, jvms)
@@ -587,7 +587,7 @@ func (gd *GlobalData) InitVartypes() {
acl("PTHREAD_OPTS", lkShell, enum("native optional require"), "Makefile: set, append; Makefile.common: append; buildlink3.mk: append")
sys("PTHREAD_TYPE", lkNone, CheckvarIdentifier) // Or "native" or "none".
pkg("PY_PATCHPLIST", lkNone, CheckvarYes)
- acl("PYPKGPREFIX", lkNone, enum("py27 py33 py34"), "pyversion.mk: set; *: use-loadtime, use")
+ acl("PYPKGPREFIX", lkNone, enum("py27 py33 py34 py35"), "pyversion.mk: set; *: use-loadtime, use")
pkg("PYTHON_FOR_BUILD_ONLY", lkNone, CheckvarYes)
pkglist("REPLACE_PYTHON", lkShell, CheckvarPathmask)
pkg("PYTHON_VERSIONS_ACCEPTED", lkShell, CheckvarVersion)
@@ -670,6 +670,7 @@ func (gd *GlobalData) InitVartypes() {
acl("USE_BUILTIN.*", lkNone, CheckvarYesNoIndirectly, "builtin.mk: set")
pkg("USE_CMAKE", lkNone, CheckvarYes)
acl("USE_CROSSBASE", lkNone, CheckvarYes, "Makefile: set")
+ usr("USE_DESTDIR", lkNone, CheckvarYes)
pkg("USE_FEATURES", lkShell, CheckvarIdentifier)
pkg("USE_GCC_RUNTIME", lkNone, CheckvarYesNo)
pkg("USE_GNU_CONFIGURE_HOST", lkNone, CheckvarYesNo)
@@ -705,9 +706,26 @@ func enum(values string) *VarChecker {
vmap[value] = true
}
name := "enum: " + values + " " // See IsEnum
- return &VarChecker{name, func(ctx *VartypeCheck) {
- if !vmap[ctx.value] {
- ctx.line.Warnf("%q is not valid for %s. Use one of { %s } instead.", ctx.value, ctx.varname, values)
+ return &VarChecker{name, func(cv *VartypeCheck) {
+ if cv.op == opUseMatch {
+ if !vmap[cv.value] && cv.value == cv.valueNovar {
+ canMatch := false
+ for value := range vmap {
+ if ok, err := path.Match(cv.value, value); err != nil {
+ cv.line.Warnf("Invalid match pattern %q.", cv.value)
+ } else if ok {
+ canMatch = true
+ }
+ }
+ if !canMatch {
+ cv.line.Warnf("The pattern %q cannot match any of { %s } for %s.", cv.value, values, cv.varname)
+ }
+ }
+ return
+ }
+
+ if !vmap[cv.value] {
+ cv.line.Warnf("%q is not valid for %s. Use one of { %s } instead.", cv.value, cv.varname, values)
}
}}
}
diff --git a/pkgtools/pkglint/files/vartypecheck.go b/pkgtools/pkglint/files/vartypecheck.go
index defc1ca3b55..a91a1ce372d 100644
--- a/pkgtools/pkglint/files/vartypecheck.go
+++ b/pkgtools/pkglint/files/vartypecheck.go
@@ -25,8 +25,9 @@ const (
opAssignEval // :=
opAssignAppend // +=
opAssignDefault // ?=
- opUseLoadtime
- opUse
+ opUse //
+ opUseLoadtime //
+ opUseMatch // Used in the :M operator
)
func NewMkOperator(op string) MkOperator {
@@ -46,7 +47,7 @@ func NewMkOperator(op string) MkOperator {
}
func (op MkOperator) String() string {
- return [...]string{"=", "!=", ":=", "+=", "?=", "use", "use-loadtime"}[op]
+ return [...]string{"=", "!=", ":=", "+=", "?=", "use", "use-loadtime", "use-match"}[op]
}
func (cv *VartypeCheck) AwkCommand() {
@@ -91,6 +92,9 @@ func (cv *VartypeCheck) Category() {
// A single option to the C/C++ compiler.
func (cv *VartypeCheck) CFlag() {
+ if cv.op == opUseMatch {
+ return
+ }
cflag := cv.value
switch {
case matches(cflag, `^-[DILOUWfgm]`),
@@ -291,6 +295,8 @@ func (cv *VartypeCheck) FetchURL() {
// See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_169
func (cv *VartypeCheck) Filename() {
switch {
+ case cv.op == opUseMatch:
+ break
case contains(cv.valueNovar, "/"):
cv.line.Warn0("A filename should not contain a slash.")
case !matches(cv.valueNovar, `^[-0-9@A-Za-z.,_~+%]*$`):
@@ -299,6 +305,9 @@ func (cv *VartypeCheck) Filename() {
}
func (cv *VartypeCheck) Filemask() {
+ if cv.op == opUseMatch {
+ return
+ }
if !matches(cv.valueNovar, `^[-0-9A-Za-z._~+%*?]*$`) {
cv.line.Warn1("%q is not a valid filename mask.", cv.value)
}
@@ -316,6 +325,12 @@ func (cv *VartypeCheck) FileMode() {
}
func (cv *VartypeCheck) Identifier() {
+ if cv.op == opUseMatch {
+ if cv.value == cv.valueNovar && !matches(cv.value, `^[\w*?]`) {
+ cv.line.Warn2("Invalid identifier pattern %q for %s.", cv.value, cv.varname)
+ }
+ return
+ }
if cv.value != cv.valueNovar {
//line.logWarning("Identifiers should be given directly.")
}
@@ -336,6 +351,9 @@ func (cv *VartypeCheck) Integer() {
}
func (cv *VartypeCheck) LdFlag() {
+ if cv.op == opUseMatch {
+ return
+ }
ldflag := cv.value
if m, rpathFlag := match1(ldflag, `^(-Wl,(?:-R|-rpath|--rpath))`); m {
cv.line.Warn1("Please use \"${COMPILER_RPATH_FLAG}\" instead of %q.", rpathFlag)
@@ -452,6 +470,9 @@ func (cv *VartypeCheck) Pathlist() {
// Shell globbing including slashes.
// See Filemask
func (cv *VartypeCheck) Pathmask() {
+ if cv.op == opUseMatch {
+ return
+ }
if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%*?/\[\]]*`) {
cv.line.Warn1("%q is not a valid pathname mask.", cv.value)
}
@@ -461,6 +482,9 @@ func (cv *VartypeCheck) Pathmask() {
// Like Filename, but including slashes
// See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266
func (cv *VartypeCheck) Pathname() {
+ if cv.op == opUseMatch {
+ return
+ }
if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%/]*$`) {
cv.line.Warn1("%q is not a valid pathname.", cv.value)
}
@@ -474,7 +498,7 @@ func (cv *VartypeCheck) Perl5Packlist() {
}
func (cv *VartypeCheck) PkgName() {
- if cv.value == cv.valueNovar && !matches(cv.value, rePkgname) {
+ if cv.op != opUseMatch && cv.value == cv.valueNovar && !matches(cv.value, rePkgname) {
cv.line.Warn1("%q is not a valid package name. A valid package name has the form packagename-version, where version consists only of digits, letters and dots.", cv.value)
}
}
@@ -642,6 +666,9 @@ func (cv *VartypeCheck) SedCommands() {
}
func (cv *VartypeCheck) ShellCommand() {
+ if cv.op == opUseMatch {
+ return
+ }
setE := true
NewShellLine(cv.mkline).CheckShellCommand(cv.value, &setE)
}
@@ -680,7 +707,7 @@ func (cv *VartypeCheck) Tool() {
default:
cv.line.Error1("Unknown tool dependency %q. Use one of \"build\", \"pkgsrc\" or \"run\".", tooldep)
}
- } else {
+ } else if cv.op != opUseMatch {
cv.line.Error1("Invalid tool syntax: %q.", cv.value)
}
}
@@ -741,7 +768,11 @@ func (cv *VartypeCheck) Varname() {
}
func (cv *VartypeCheck) Version() {
- if !matches(cv.value, `^([\d.])+$`) {
+ if cv.op == opUseMatch {
+ if !matches(cv.value, `^[\d?\[][\w\-.*?\[\]]+$`) {
+ cv.line.Warn1("Invalid version number pattern %q.", cv.value)
+ }
+ } else if cv.value == cv.valueNovar && !matches(cv.value, `^\d[\w.]+$`) {
cv.line.Warn1("Invalid version number %q.", cv.value)
}
}
@@ -786,31 +817,47 @@ func (cv *VartypeCheck) WrksrcSubdirectory() {
}
}
-// Used for variables that are checked using `.if defined(VAR)`.
func (cv *VartypeCheck) Yes() {
- if !matches(cv.value, `^(?:YES|yes)(?:\s+#.*)?$`) {
- cv.line.Warn1("%s should be set to YES or yes.", cv.varname)
- Explain4(
- "This variable means \"yes\" if it is defined, and \"no\" if it is",
- "undefined. Even when it has the value \"no\", this means \"yes\".",
- "Therefore when it is defined, its value should correspond to its",
- "meaning.")
+ switch cv.op {
+ case opUseMatch:
+ cv.line.Warn1("%s should only be used in a \".if defined(...)\" conditional.", cv.varname)
+ Explain(
+ "This variable can have only two values: defined or undefined.",
+ "When it is defined, it means \"yes\", even when its value is",
+ "\"no\" or the empty string.",
+ "",
+ "Therefore, it should not be checked by comparing its value",
+ "but using \".if defined(VARNAME)\" alone.")
+
+ default:
+ if !matches(cv.value, `^(?:YES|yes)(?:\s+#.*)?$`) {
+ cv.line.Warn1("%s should be set to YES or yes.", cv.varname)
+ Explain4(
+ "This variable means \"yes\" if it is defined, and \"no\" if it is",
+ "undefined. Even when it has the value \"no\", this means \"yes\".",
+ "Therefore when it is defined, its value should correspond to its",
+ "meaning.")
+ }
}
}
-// The type YesNo is used for variables that are checked using
-// .if defined(VAR) && !empty(VAR:M[Yy][Ee][Ss])
-//
func (cv *VartypeCheck) YesNo() {
- if !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) {
+ if cv.op == opUseMatch {
+ switch cv.value {
+ case "[yY][eE][sS]":
+ case "[Yy][Ee][Ss]":
+ case "[nN][oO]":
+ case "[Nn][Oo]":
+ default:
+ cv.line.Warnf("%s should be matched against %q or %q, not %q.", cv.varname, "[yY][eE][sS]", "[nN][oO]", cv.value)
+ }
+ } else if !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) {
cv.line.Warn1("%s should be set to YES, yes, NO, or no.", cv.varname)
}
}
-// Like YesNo, but the value may be produced by a shell command using the
-// != operator.
func (cv *VartypeCheck) YesNoIndirectly() {
- if cv.valueNovar != "" && !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) {
- cv.line.Warn1("%s should be set to YES, yes, NO, or no.", cv.varname)
+ if cv.valueNovar != "" {
+ cv.YesNo()
}
}
diff --git a/pkgtools/pkglint/files/vartypecheck_test.go b/pkgtools/pkglint/files/vartypecheck_test.go
index cbcb313c3b8..4cf8a54c4f7 100644
--- a/pkgtools/pkglint/files/vartypecheck_test.go
+++ b/pkgtools/pkglint/files/vartypecheck_test.go
@@ -1,6 +1,8 @@
package main
import (
+ "fmt"
+
check "gopkg.in/check.v1"
)
@@ -169,6 +171,16 @@ func (s *Suite) TestVartypeCheck_EmulPlatform(c *check.C) {
"WARN: fname:3: \"${LINUX}\" is not a valid emulation platform.\n")
}
+func (s *Suite) TestVartypeCheck_Enum(c *check.C) {
+ runVartypeMatchChecks("JDK", enum("jdk1 jdk2 jdk4").checker,
+ "*",
+ "jdk*",
+ "sun-jdk*",
+ "${JDKNAME}")
+
+ c.Check(s.Output(), equals, "WARN: fname:3: The pattern \"sun-jdk*\" cannot match any of { jdk1 jdk2 jdk4 } for JDK.\n")
+}
+
func (s *Suite) TestVartypeCheck_FetchURL(c *check.C) {
G.globalData.MasterSiteUrls = map[string]string{
"https://github.com/": "MASTER_SITE_GITHUB",
@@ -363,6 +375,16 @@ func (s *Suite) TestVartypeCheck_Yes(c *check.C) {
c.Check(s.Output(), equals, ""+
"WARN: fname:2: APACHE_MODULE should be set to YES or yes.\n"+
"WARN: fname:3: APACHE_MODULE should be set to YES or yes.\n")
+
+ runVartypeMatchChecks("PKG_DEVELOPER", (*VartypeCheck).Yes,
+ "yes",
+ "no",
+ "${YESVAR}")
+
+ c.Check(s.Output(), equals, ""+
+ "WARN: fname:1: PKG_DEVELOPER should only be used in a \".if defined(...)\" conditional.\n"+
+ "WARN: fname:2: PKG_DEVELOPER should only be used in a \".if defined(...)\" conditional.\n"+
+ "WARN: fname:3: PKG_DEVELOPER should only be used in a \".if defined(...)\" conditional.\n")
}
func (s *Suite) TestVartypeCheck_YesNo(c *check.C) {
@@ -389,6 +411,9 @@ func (s *Suite) TestVartypeCheck_YesNoIndirectly(c *check.C) {
}
func runVartypeChecks(varname string, op MkOperator, checker func(*VartypeCheck), values ...string) {
+ if !contains(op.String(), "=") {
+ panic("runVartypeChecks needs an assignment operator")
+ }
for i, value := range values {
mkline := NewMkLine(NewLine("fname", i+1, varname+op.String()+value, nil))
valueNovar := mkline.withoutMakeVariables(mkline.Value(), true)
@@ -397,6 +422,16 @@ func runVartypeChecks(varname string, op MkOperator, checker func(*VartypeCheck)
}
}
+func runVartypeMatchChecks(varname string, checker func(*VartypeCheck), values ...string) {
+ for i, value := range values {
+ text := fmt.Sprintf(".if ${%s:M%s} == \"\"", varname, value)
+ mkline := NewMkLine(NewLine("fname", i+1, text, nil))
+ valueNovar := mkline.withoutMakeVariables(value, true)
+ vc := &VartypeCheck{mkline, mkline.Line, varname, opUseMatch, value, valueNovar, "", true, false}
+ checker(vc)
+ }
+}
+
func runVartypeChecksFname(fname, varname string, op MkOperator, checker func(*VartypeCheck), values ...string) {
for i, value := range values {
mkline := NewMkLine(NewLine(fname, i+1, varname+op.String()+value, nil))