pkgsrc-Changes archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

CVS commit: pkgsrc/pkgtools/pkglint



Module Name:    pkgsrc
Committed By:   rillig
Date:           Wed Aug 21 16:45:17 UTC 2019

Modified Files:
        pkgsrc/pkgtools/pkglint: Makefile
        pkgsrc/pkgtools/pkglint/files: check_test.go mkline_test.go
            mklinechecker.go mklinechecker_test.go mkshparser.go
            mkshparser_test.go mktypes.go options.go options_test.go package.go
            package_test.go varalignblock.go varalignblock_test.go vardefs.go
            vardefs_test.go vartype.go vartypecheck.go vartypecheck_test.go

Log Message:
pkgtools/pkglint: update to 5.7.21

Changes since 5.7.20:

* PKG_OPTIONS that are handled using patterns are correctly identified.

* Simple R packages should follow the canonical variable order.

* Fixed some edge cases for aligning variable assignments.

* Improved detection of allowed values for USE_LANGUAGES.


To generate a diff of this commit:
cvs rdiff -u -r1.593 -r1.594 pkgsrc/pkgtools/pkglint/Makefile
cvs rdiff -u -r1.46 -r1.47 pkgsrc/pkgtools/pkglint/files/check_test.go
cvs rdiff -u -r1.63 -r1.64 pkgsrc/pkgtools/pkglint/files/mkline_test.go
cvs rdiff -u -r1.43 -r1.44 pkgsrc/pkgtools/pkglint/files/mklinechecker.go
cvs rdiff -u -r1.39 -r1.40 \
    pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go
cvs rdiff -u -r1.14 -r1.15 pkgsrc/pkgtools/pkglint/files/mkshparser.go \
    pkgsrc/pkgtools/pkglint/files/options_test.go
cvs rdiff -u -r1.17 -r1.18 pkgsrc/pkgtools/pkglint/files/mkshparser_test.go
cvs rdiff -u -r1.16 -r1.17 pkgsrc/pkgtools/pkglint/files/mktypes.go \
    pkgsrc/pkgtools/pkglint/files/options.go
cvs rdiff -u -r1.59 -r1.60 pkgsrc/pkgtools/pkglint/files/package.go
cvs rdiff -u -r1.50 -r1.51 pkgsrc/pkgtools/pkglint/files/package_test.go
cvs rdiff -u -r1.2 -r1.3 pkgsrc/pkgtools/pkglint/files/varalignblock.go \
    pkgsrc/pkgtools/pkglint/files/varalignblock_test.go
cvs rdiff -u -r1.69 -r1.70 pkgsrc/pkgtools/pkglint/files/vardefs.go
cvs rdiff -u -r1.19 -r1.20 pkgsrc/pkgtools/pkglint/files/vardefs_test.go
cvs rdiff -u -r1.35 -r1.36 pkgsrc/pkgtools/pkglint/files/vartype.go
cvs rdiff -u -r1.61 -r1.62 pkgsrc/pkgtools/pkglint/files/vartypecheck.go
cvs rdiff -u -r1.53 -r1.54 pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: pkgsrc/pkgtools/pkglint/Makefile
diff -u pkgsrc/pkgtools/pkglint/Makefile:1.593 pkgsrc/pkgtools/pkglint/Makefile:1.594
--- pkgsrc/pkgtools/pkglint/Makefile:1.593      Fri Aug 16 21:00:17 2019
+++ pkgsrc/pkgtools/pkglint/Makefile    Wed Aug 21 16:45:16 2019
@@ -1,6 +1,6 @@
-# $NetBSD: Makefile,v 1.593 2019/08/16 21:00:17 rillig Exp $
+# $NetBSD: Makefile,v 1.594 2019/08/21 16:45:16 rillig Exp $
 
-PKGNAME=       pkglint-5.7.20
+PKGNAME=       pkglint-5.7.21
 CATEGORIES=    pkgtools
 DISTNAME=      tools
 MASTER_SITES=  ${MASTER_SITE_GITHUB:=golang/}

Index: pkgsrc/pkgtools/pkglint/files/check_test.go
diff -u pkgsrc/pkgtools/pkglint/files/check_test.go:1.46 pkgsrc/pkgtools/pkglint/files/check_test.go:1.47
--- pkgsrc/pkgtools/pkglint/files/check_test.go:1.46    Tue Jul 30 18:16:13 2019
+++ pkgsrc/pkgtools/pkglint/files/check_test.go Wed Aug 21 16:45:17 2019
@@ -309,6 +309,18 @@ func (t *Tester) SetUpPkgsrc() {
        t.CreateFileLines("mk/bsd.fast.prefs.mk",
                MkCvsID)
 
+       // This file is used for initializing the allowed values for
+       // USE_LANGUAGES; see VarTypeRegistry.compilerLanguages.
+       t.CreateFileLines("mk/compiler.mk",
+               "_CXX_STD_VERSIONS=\tc++ c++14",
+               ".if ${USE_LANGUAGES:Mada} || \\",
+               "    ${USE_LANGUAGES:Mc} || \\",
+               "    ${USE_LANGUAGES:Mc99} || \\",
+               "    ${USE_LANGUAGES:Mobjc} || \\",
+               "    ${USE_LANGUAGES:Mfortran} || \\",
+               "    ${USE_LANGUAGES:Mfortran77}",
+               ".endif")
+
        // Category Makefiles require this file for the common definitions.
        t.CreateFileLines("mk/misc/category.mk")
 

Index: pkgsrc/pkgtools/pkglint/files/mkline_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mkline_test.go:1.63 pkgsrc/pkgtools/pkglint/files/mkline_test.go:1.64
--- pkgsrc/pkgtools/pkglint/files/mkline_test.go:1.63   Thu Aug  1 22:38:49 2019
+++ pkgsrc/pkgtools/pkglint/files/mkline_test.go        Wed Aug 21 16:45:17 2019
@@ -457,9 +457,10 @@ func (s *Suite) Test_MkLine__aligned(c *
                "\tvalue",
                true)
 
-       // In commented multilines, the continuation lines may or may not start
-       // with a comment character. Bmake doesn't care, but for human readers
-       // it is confusing to omit the leading comment character.
+       // In commented multilines, bmake doesn't care whether the
+       // continuation lines does or doesn't start with a comment character.
+       // For human readers though, it is confusing to omit the leading
+       // comment character.
        //
        // For determining whether a multiline is aligned, the initial comment
        // character is ignored.

Index: pkgsrc/pkgtools/pkglint/files/mklinechecker.go
diff -u pkgsrc/pkgtools/pkglint/files/mklinechecker.go:1.43 pkgsrc/pkgtools/pkglint/files/mklinechecker.go:1.44
--- pkgsrc/pkgtools/pkglint/files/mklinechecker.go:1.43 Sun Jul 14 21:25:47 2019
+++ pkgsrc/pkgtools/pkglint/files/mklinechecker.go      Wed Aug 21 16:45:17 2019
@@ -599,7 +599,7 @@ func (ck MkLineChecker) checkVaruseModif
        if len(mods) == 3 {
                if m, _, from, to, options := mods[0].MatchSubst(); m && from == "^" && matches(to, `^\w+$`) && options == "1" {
                        magic := to
-                       if m, positive, pattern := mods[1].MatchMatch(); m && positive && pattern == magic+"*" {
+                       if m, positive, pattern, _ := mods[1].MatchMatch(); m && positive && pattern == magic+"*" {
                                if m, _, from, to, options = mods[2].MatchSubst(); m && from == "^"+magic && to == "" && options == "" {
                                        fix := ck.MkLine.Autofix()
                                        fix.Notef("The modifier %q can be written as %q.", varuse.Mod(), ":[1]")
@@ -1583,7 +1583,8 @@ func (ck MkLineChecker) simplifyConditio
                        pattern +
                        condStr(fromEmpty, ")", "}")
 
-               to := "${" + varname + "} " + op + " " + pattern
+               quote := condStr(matches(pattern, `[^\-/0-9@A-Za-z]`), "\"", "")
+               to := "${" + varname + "} " + op + " " + quote + pattern + quote
 
                // TODO: Check in more cases whether the parentheses are really necessary.
                //  In a !!${VAR} expression, parentheses are necessary.
@@ -1599,11 +1600,11 @@ func (ck MkLineChecker) simplifyConditio
        modifiers := varuse.modifiers
 
        for _, modifier := range modifiers {
-               if m, positive, pattern := modifier.MatchMatch(); m && (positive || len(modifiers) == 1) {
+               if m, positive, pattern, exact := modifier.MatchMatch(); m && (positive || len(modifiers) == 1) {
                        ck.checkVartype(varname, opUseMatch, pattern, "")
 
                        vartype := G.Pkgsrc.VariableType(ck.MkLines, varname)
-                       if matches(pattern, `^[\w-/]+$`) && vartype != nil && !vartype.List() {
+                       if exact && matches(pattern, `^[\w-/]+$`) && vartype != nil && !vartype.List() {
 
                                fix := ck.MkLine.Autofix()
                                fix.Notef("%s should be compared using %s instead of matching against %q.",
@@ -1649,7 +1650,7 @@ func (ck MkLineChecker) checkDirectiveCo
                ck.checkCompareVarStr(varname, op, str)
 
        case 1:
-               if m, _, pattern := varmods[0].MatchMatch(); m {
+               if m, _, pattern, _ := varmods[0].MatchMatch(); m {
                        ck.checkVartype(varname, opUseMatch, pattern, "")
 
                        // After applying the :M or :N modifier, every expression may end up empty,

Index: pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go:1.39 pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go:1.40
--- pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go:1.39    Sun Jul 14 21:25:47 2019
+++ pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go Wed Aug 21 16:45:17 2019
@@ -2184,6 +2184,9 @@ func (s *Suite) Test_MkLineChecker_check
        mklines := t.SetUpFileMkLines("options.mk",
                MkCvsID,
                "GOPATH=\t${WRKDIR}",
+               "",
+               "CONFIGURE_ENV+=\tNAME=${R_PKGNAME} VER=${R_PKGVER}",
+               "",
                "do-build:",
                "\tcd ${WRKSRC} && GOPATH=${GOPATH} PATH=${PATH} :")
 
@@ -2197,7 +2200,7 @@ func (s *Suite) Test_MkLineChecker_check
        // of pkgsrc, and these may contain special characters.
 
        t.CheckOutputLines(
-               "WARN: ~/options.mk:4: The variable PATH should be quoted as part of a shell word.")
+               "WARN: ~/options.mk:7: The variable PATH should be quoted as part of a shell word.")
 }
 
 func (s *Suite) Test_MkLineChecker_checkVarUseQuoting__mstar(c *check.C) {

Index: pkgsrc/pkgtools/pkglint/files/mkshparser.go
diff -u pkgsrc/pkgtools/pkglint/files/mkshparser.go:1.14 pkgsrc/pkgtools/pkglint/files/mkshparser.go:1.15
--- pkgsrc/pkgtools/pkglint/files/mkshparser.go:1.14    Fri Aug  2 18:55:07 2019
+++ pkgsrc/pkgtools/pkglint/files/mkshparser.go Wed Aug 21 16:45:17 2019
@@ -11,12 +11,12 @@ func parseShellProgram(line *Line, progr
        lexer := NewShellLexer(tokens, rest)
        parser := shyyParserImpl{}
 
-       succeeded := parser.Parse(lexer)
+       zeroMeansSuccess := parser.Parse(lexer)
 
        switch {
-       case succeeded == 0 && lexer.error == "":
+       case zeroMeansSuccess == 0 && lexer.error == "":
                return lexer.result, nil
-       case succeeded == 0:
+       case zeroMeansSuccess == 0:
                return nil, fmt.Errorf("splitIntoShellTokens couldn't parse %q", rest)
        default:
                return nil, &ParseError{append([]string{lexer.current}, lexer.remaining...)}
Index: pkgsrc/pkgtools/pkglint/files/options_test.go
diff -u pkgsrc/pkgtools/pkglint/files/options_test.go:1.14 pkgsrc/pkgtools/pkglint/files/options_test.go:1.15
--- pkgsrc/pkgtools/pkglint/files/options_test.go:1.14  Sun Jun 30 20:56:19 2019
+++ pkgsrc/pkgtools/pkglint/files/options_test.go       Wed Aug 21 16:45:17 2019
@@ -324,3 +324,66 @@ func (s *Suite) Test_CheckLinesOptionsMk
                ".  endif",
                ".endif")
 }
+
+// A few packages (such as www/w3m) define several options that are
+// handled by a single .if block in the lower part.
+func (s *Suite) Test_CheckLinesOptionsMk__combined_option_handling(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpOption("opt-variant1", "")
+       t.SetUpOption("opt-variant2", "")
+       t.SetUpOption("other", "")
+       t.CreateFileLines("mk/bsd.options.mk")
+       t.SetUpPackage("category/package",
+               ".include \"options.mk\"")
+       t.CreateFileLines("category/package/options.mk",
+               MkCvsID,
+               "",
+               "PKG_OPTIONS_VAR=\tPKG_OPTIONS.package",
+               "PKG_SUPPORTED_OPTIONS=\topt-variant1 opt-variant2",
+               "",
+               ".include \"../../mk/bsd.options.mk\"",
+               "",
+               ".if ${PKG_OPTIONS:Mopt-variant*}",
+               ".endif")
+       t.FinishSetUp()
+       t.Chdir("category/package")
+
+       G.Check(".")
+
+       // Before 5.7.21 on 2019-08-17, pkglint issued an error about the
+       // "invalid option name opt-variant*" and warnings about the
+       // unhandled options "opt-variant1" and "opt-variant2".
+       t.CheckOutputEmpty()
+}
+
+func (s *Suite) Test_CheckLinesOptionsMk__combined_option_handling_coverage(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpOption("opt-variant", "")
+       t.CreateFileLines("mk/bsd.options.mk")
+       t.SetUpPackage("category/package",
+               ".include \"options.mk\"")
+       t.CreateFileLines("category/package/options.mk",
+               MkCvsID,
+               "",
+               "PKG_OPTIONS_VAR=\tPKG_OPTIONS.package",
+               "PKG_SUPPORTED_OPTIONS=\topt-variant",
+               "",
+               ".include \"../../mk/bsd.options.mk\"",
+               "",
+               ".if ${PKG_OPTIONS:Mopt-[}", // intentional syntax error
+               ".endif",
+               "",
+               ".if ${PKG_OPTIONS:Mother-*}",
+               ".endif")
+       t.FinishSetUp()
+       t.Chdir("category/package")
+
+       G.Check(".")
+
+       // The warning appears because the pattern "opt-[" is malformed
+       // and therefore doesn't match the option.
+       t.CheckOutputLines(
+               "WARN: options.mk:4: Option \"opt-variant\" should be handled below in an .if block.")
+}

Index: pkgsrc/pkgtools/pkglint/files/mkshparser_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mkshparser_test.go:1.17 pkgsrc/pkgtools/pkglint/files/mkshparser_test.go:1.18
--- pkgsrc/pkgtools/pkglint/files/mkshparser_test.go:1.17       Fri Aug  2 18:55:07 2019
+++ pkgsrc/pkgtools/pkglint/files/mkshparser_test.go    Wed Aug 21 16:45:17 2019
@@ -19,6 +19,7 @@ func (s *Suite) Test_parseShellProgram__
                                t.CheckEquals(err, expError)
                        } else {
                                t.CheckDeepEquals(err, expError)
+                               t.CheckDeepEquals(err.Error(), expError.Error()) // Just for code coverage
                                t.CheckDeepEquals(program, expProgram)
                        }
 
@@ -48,6 +49,12 @@ func (s *Suite) Test_parseShellProgram__
                nil,
                nil,
                nil...)
+
+       test(
+               "case ;;",
+               nil,
+               &ParseError{[]string{";;"}},
+               nil...)
 }
 
 type ShSuite struct {
@@ -396,11 +403,17 @@ func (s *ShSuite) Test_ShellParser__case
                                b.Words("*"),
                                b.List(), sepNone))))
 
-       // The default case may be omitted if PATTERNS can never be empty.
+       // The default case may even be omitted.
        s.test("case $$expr in ${PATTERNS:@p@ (${p}) action ;; @} esac",
                b.List().AddCommand(b.Case(
                        b.Token("$$expr"),
                        b.CaseItemVar("${PATTERNS:@p@ (${p}) action ;; @}"))))
+
+       // Only variables that end with a :@ modifier may be used in this
+       // construct. All others are tokenized as normal words and lead
+       // to a syntax error in the shell parser.
+       s.testFail("case $$expr in ${PATTERNS} esac",
+               []string{}...)
 }
 
 func (s *ShSuite) Test_ShellParser__if_clause(c *check.C) {
@@ -589,6 +602,21 @@ func (s *ShSuite) test(program string, e
        }
 }
 
+func (s *ShSuite) testFail(program string, expectedRemaining ...string) {
+       t := s.t
+
+       tokens, rest := splitIntoShellTokens(dummyLine, program)
+       t.CheckEquals(rest, "")
+       lexer := ShellLexer{remaining: tokens, atCommandStart: true}
+       parser := shyyParserImpl{}
+
+       zeroMeansSuccess := parser.Parse(&lexer)
+
+       if t.CheckEquals(zeroMeansSuccess, 1) && t.Check(lexer.error, check.Not(check.Equals), "") {
+               t.CheckDeepEquals(lexer.remaining, expectedRemaining)
+       }
+}
+
 func (s *ShSuite) Test_ShellLexer_Lex__redirects(c *check.C) {
        t := s.t
 
@@ -708,6 +736,15 @@ func (s *Suite) Test_ShellLexer_Lex__cas
                tkIN,
                tkWORD,
                tkESAC)
+
+       test(
+               "case $$expr in ${PATTERNS:Mpattern} esac",
+
+               tkCASE,
+               tkWORD,
+               tkIN,
+               tkWORD,
+               tkWORD) // No tkESAC since there is no :@ modifier.
 }
 
 type MkShBuilder struct {

Index: pkgsrc/pkgtools/pkglint/files/mktypes.go
diff -u pkgsrc/pkgtools/pkglint/files/mktypes.go:1.16 pkgsrc/pkgtools/pkglint/files/mktypes.go:1.17
--- pkgsrc/pkgtools/pkglint/files/mktypes.go:1.16       Sun Jul 14 21:25:47 2019
+++ pkgsrc/pkgtools/pkglint/files/mktypes.go    Wed Aug 21 16:45:17 2019
@@ -92,11 +92,13 @@ func (m MkVarUseModifier) Subst(str stri
 //  :Mpattern   => true, true, "pattern"
 //  :Npattern   => true, false, "pattern"
 //  :X          => false
-func (m MkVarUseModifier) MatchMatch() (ok bool, positive bool, pattern string) {
+func (m MkVarUseModifier) MatchMatch() (ok bool, positive bool, pattern string, exact bool) {
        if hasPrefix(m.Text, "M") || hasPrefix(m.Text, "N") {
-               return true, m.Text[0] == 'M', m.Text[1:]
+               // See devel/bmake/files/str.c:^Str_Match
+               exact := !strings.ContainsAny(m.Text[1:], "*?[\\")
+               return true, m.Text[0] == 'M', m.Text[1:], exact
        }
-       return false, false, ""
+       return false, false, "", false
 }
 
 func (m MkVarUseModifier) IsToLower() bool { return m.Text == "tl" }
Index: pkgsrc/pkgtools/pkglint/files/options.go
diff -u pkgsrc/pkgtools/pkglint/files/options.go:1.16 pkgsrc/pkgtools/pkglint/files/options.go:1.17
--- pkgsrc/pkgtools/pkglint/files/options.go:1.16       Fri Aug 16 21:00:17 2019
+++ pkgsrc/pkgtools/pkglint/files/options.go    Wed Aug 21 16:45:17 2019
@@ -1,5 +1,7 @@
 package pkglint
 
+import "path"
+
 func CheckLinesOptionsMk(mklines *MkLines) {
        ck := OptionsLinesChecker{
                mklines,
@@ -125,13 +127,27 @@ func (ck *OptionsLinesChecker) handleLow
 func (ck *OptionsLinesChecker) handleLowerCondition(mkline *MkLine, cond *MkCond) {
 
        recordUsedOption := func(varuse *MkVarUse) {
-               if varuse.varname == "PKG_OPTIONS" && len(varuse.modifiers) == 1 {
-                       if m, positive, pattern := varuse.modifiers[0].MatchMatch(); m && positive {
-                               option := pattern
-                               if !containsVarRef(option) {
-                                       ck.handledOptions[option] = mkline
-                                       ck.optionsInDeclarationOrder = append(ck.optionsInDeclarationOrder, option)
-                               }
+               if varuse.varname != "PKG_OPTIONS" || len(varuse.modifiers) != 1 {
+                       return
+               }
+
+               m, positive, pattern, exact := varuse.modifiers[0].MatchMatch()
+               if !m || !positive || containsVarRef(pattern) {
+                       return
+               }
+
+               if exact {
+                       option := pattern
+                       ck.handledOptions[option] = mkline
+                       ck.optionsInDeclarationOrder = append(ck.optionsInDeclarationOrder, option)
+                       return
+               }
+
+               for declaredOption := range ck.declaredOptions {
+                       matched, err := path.Match(pattern, declaredOption)
+                       if err == nil && matched {
+                               ck.handledOptions[declaredOption] = mkline
+                               ck.optionsInDeclarationOrder = append(ck.optionsInDeclarationOrder, declaredOption)
                        }
                }
        }

Index: pkgsrc/pkgtools/pkglint/files/package.go
diff -u pkgsrc/pkgtools/pkglint/files/package.go:1.59 pkgsrc/pkgtools/pkglint/files/package.go:1.60
--- pkgsrc/pkgtools/pkglint/files/package.go:1.59       Sun Jul 14 21:25:47 2019
+++ pkgsrc/pkgtools/pkglint/files/package.go    Wed Aug 21 16:45:17 2019
@@ -950,6 +950,8 @@ func (pkg *Package) CheckVarorder(mkline
                {"GITHUB_TAG", optional},
                {"DISTNAME", optional},
                {"PKGNAME", optional},
+               {"R_PKGNAME", optional},
+               {"R_PKGVER", optional},
                {"PKGREVISION", optional},
                {"CATEGORIES", once},
                {"MASTER_SITES", many},

Index: pkgsrc/pkgtools/pkglint/files/package_test.go
diff -u pkgsrc/pkgtools/pkglint/files/package_test.go:1.50 pkgsrc/pkgtools/pkglint/files/package_test.go:1.51
--- pkgsrc/pkgtools/pkglint/files/package_test.go:1.50  Sun Jul 14 21:25:47 2019
+++ pkgsrc/pkgtools/pkglint/files/package_test.go       Wed Aug 21 16:45:17 2019
@@ -1639,12 +1639,15 @@ func (s *Suite) Test_Package_checkGnuCon
                "",
                ".include \"../../mk/compiler.mk\"")
        t.CreateFileLines("mk/compiler.mk",
-               MkCvsID,
-               ".include \"bsd.prefs.mk\"",
+               "_CXX_STD_VERSIONS=\tc++ c++14",
+               ".if ${USE_LANGUAGES:Mada} \\",
+               " || ${USE_LANGUAGES:Mc} \\",
+               " || ${USE_LANGUAGES:Mfortran77}",
+               ".endif",
                "",
-               "USE_LANGUAGES?=\tc",
-               "USE_LANGUAGES+=\tc",
-               "USE_LANGUAGES+=\tc++")
+               // This line is ignored since it comes from the pkgsrc infrastructure.
+               "USE_LANGUAGES?=\t\tc")
+
        t.FinishSetUp()
 
        G.Check(t.File("category/package"))
@@ -1776,8 +1779,6 @@ func (s *Suite) Test_Package_checkUseLan
        t.SetUpPackage("category/package",
                ".include \"../../mk/compiler.mk\"",
                "USE_LANGUAGES=\tc c99 fortran ada c++14")
-       t.CreateFileLines("mk/compiler.mk",
-               MkCvsID)
        t.FinishSetUp()
 
        G.Check(t.File("category/package"))
@@ -1798,8 +1799,6 @@ func (s *Suite) Test_Package_checkUseLan
        t.CreateFileLines("category/package/compiler.mk",
                MkCvsID,
                "USE_LANGUAGES=\tc++")
-       t.CreateFileLines("mk/compiler.mk",
-               MkCvsID)
        t.FinishSetUp()
 
        G.Check(t.File("category/package"))

Index: pkgsrc/pkgtools/pkglint/files/varalignblock.go
diff -u pkgsrc/pkgtools/pkglint/files/varalignblock.go:1.2 pkgsrc/pkgtools/pkglint/files/varalignblock.go:1.3
--- pkgsrc/pkgtools/pkglint/files/varalignblock.go:1.2  Thu Aug  1 22:38:49 2019
+++ pkgsrc/pkgtools/pkglint/files/varalignblock.go      Wed Aug 21 16:45:17 2019
@@ -173,6 +173,10 @@ func (*VaralignBlock) split(textnl strin
        lexer := p.lexer
 
        parseLeadingComment := func() string {
+               if hasPrefix(lexer.Rest(), "# ") {
+                       return ""
+               }
+
                mark := lexer.Mark()
 
                if !lexer.SkipByte('#') && initial && lexer.SkipByte(' ') {
@@ -379,7 +383,7 @@ func (va *VaralignBlock) realign(info *v
        }
 }
 
-func (va *VaralignBlock) realignMultiEmptyInitial(info *varalignLine, newWidth int) {
+func (*VaralignBlock) realignMultiEmptyInitial(info *varalignLine, newWidth int) {
        leadingComment := info.parts.leadingComment
        varnameOp := info.parts.varnameOp
        oldSpace := info.parts.spaceBeforeValue
@@ -400,15 +404,15 @@ func (va *VaralignBlock) realignMultiEmp
        }
 
        hasSpace := strings.IndexByte(oldSpace, ' ') != -1
-       oldColumn := tabWidth(leadingComment + varnameOp + oldSpace)
+       oldColumn := info.varnameOpSpaceWidth()
        column := tabWidth(leadingComment + varnameOp + newSpace)
 
-       assert(column >= oldColumn || column > info.varnameOpWidth())
-
        // TODO: explicitly mention "single space", "tabs to the newWidth", "tabs to column 72"
 
        fix := info.mkline.Autofix()
-       if hasSpace && column != oldColumn {
+       if newSpace == " " {
+               fix.Notef("This outlier variable should be aligned with a single space.")
+       } else if hasSpace && column != oldColumn {
                fix.Notef("This variable value should be aligned with tabs, not spaces, to column %d.", column+1)
        } else if column != oldColumn {
                fix.Notef("This variable value should be aligned to column %d.", column+1)
@@ -431,15 +435,7 @@ func (va *VaralignBlock) realignMultiEmp
                }
        }
 
-       newWidth = oldWidth + va.indentDiff
-       if newWidth < 8 {
-               newWidth = oldWidth & -8
-               if newWidth < 8 {
-                       newWidth = 8
-               }
-       }
-
-       newSpace := indent(newWidth)
+       newSpace := indent(imax(oldWidth+va.indentDiff, 8))
        if newSpace == oldSpace {
                return
        }
@@ -531,12 +527,11 @@ func (va *VaralignBlock) realignSingle(i
        oldColumn := tabWidth(leadingComment + varnameOp + oldSpace)
        column := tabWidth(leadingComment + varnameOp + newSpace)
 
-       if info.parts.value == "" && info.parts.trailingComment == "" && !info.continuation() {
-               return
-       }
-
        fix := info.mkline.Autofix()
-       if hasSpace && column != oldColumn {
+       if newSpace == " " {
+               fix.Notef("This outlier variable value should be aligned with a single space.")
+               va.explainWrongColumn(fix)
+       } else if hasSpace && column != oldColumn {
                fix.Notef("This variable value should be aligned with tabs, not spaces, to column %d.", column+1)
                va.explainWrongColumn(fix)
        } else if column != oldColumn {
Index: pkgsrc/pkgtools/pkglint/files/varalignblock_test.go
diff -u pkgsrc/pkgtools/pkglint/files/varalignblock_test.go:1.2 pkgsrc/pkgtools/pkglint/files/varalignblock_test.go:1.3
--- pkgsrc/pkgtools/pkglint/files/varalignblock_test.go:1.2     Thu Aug  1 22:38:49 2019
+++ pkgsrc/pkgtools/pkglint/files/varalignblock_test.go Wed Aug 21 16:45:17 2019
@@ -29,6 +29,8 @@ func (vt *VaralignTester) Input(lines ..
 
 // Internals remembers the expected internal state of the varalignBlockInfos,
 // to better trace down at which points the decisions are made.
+//
+// Each line has the format "<min-width> <actual-width>".
 func (vt *VaralignTester) Internals(lines ...string) { vt.internals = lines }
 
 // Diagnostics remembers the expected diagnostics.
@@ -1053,7 +1055,7 @@ func (s *Suite) Test_VaralignBlock__outl
                "38 38",
                "   24")
        vt.Diagnostics(
-               "NOTE: ~/Makefile:2: This variable value should be aligned to column 40.")
+               "NOTE: ~/Makefile:2: This outlier variable should be aligned with a single space.")
        vt.Autofixes(
                "AUTOFIX: ~/Makefile:2: Replacing \"\" with \" \".")
        vt.Fixed(
@@ -1800,6 +1802,60 @@ func (s *Suite) Test_VaralignBlock__outl
        vt.Run()
 }
 
+func (s *Suite) Test_VaralignBlock__outlier_with_several_spaces(c *check.C) {
+       vt := NewVaralignTester(s, c)
+       vt.Input(
+               "SHORT=\tvalue",
+               "VERY_VERY_LONG_VARIABLE_NAME=   value")
+       vt.Internals(
+               "06 08",
+               "29 32")
+       vt.Diagnostics(
+               "NOTE: ~/Makefile:2: This outlier variable value should be aligned with a single space.")
+       vt.Autofixes(
+               "AUTOFIX: ~/Makefile:2: Replacing \"   \" with \" \".")
+       vt.Fixed(
+               "SHORT=  value",
+               "VERY_VERY_LONG_VARIABLE_NAME= value")
+       vt.Run()
+}
+
+func (s *Suite) Test_VaralignBlock__single_space_in_short_line(c *check.C) {
+       vt := NewVaralignTester(s, c)
+       vt.Input(
+               "SHORT= value",
+               "LONG_NAME=\tvalue")
+       vt.Internals(
+               "06 07",
+               "10 16")
+       vt.Diagnostics(
+               "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.")
+       vt.Autofixes(
+               "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\\t\".")
+       vt.Fixed(
+               "SHORT=          value",
+               "LONG_NAME=      value")
+       vt.Run()
+}
+
+func (s *Suite) Test_VaralignBlock__outlier_without_space(c *check.C) {
+       vt := NewVaralignTester(s, c)
+       vt.Input(
+               "SHORT=\tvalue",
+               "LONG.678901234567890=value")
+       vt.Internals(
+               "06 08",
+               "21 21")
+       vt.Diagnostics(
+               "NOTE: ~/Makefile:2: This outlier variable value should be aligned with a single space.")
+       vt.Autofixes(
+               "AUTOFIX: ~/Makefile:2: Replacing \"\" with \" \".")
+       vt.Fixed(
+               "SHORT=  value",
+               "LONG.678901234567890= value")
+       vt.Run()
+}
+
 // The INSTALLATION_DIRS line is so long that it is considered an outlier,
 // since compared to the DIST line, it is at least two tabs away.
 // Pkglint before 2018-01-26 suggested that it "should be aligned to column 9",
@@ -2224,6 +2280,25 @@ func (s *Suite) Test_VaralignBlock__comm
        vt.Run()
 }
 
+// Variables with empty values and no comments are completely ignored,
+// since they have nothing to be aligned with the other lines.
+func (s *Suite) Test_VaralignBlock__empty_value(c *check.C) {
+       vt := NewVaralignTester(s, c)
+       vt.Input(
+               "EMPTY_VALUE=",
+               "VAR=\t\tvalue")
+       vt.Internals(
+               "04 16")
+       vt.Diagnostics(
+               nil...)
+       vt.Autofixes(
+               nil...)
+       vt.Fixed(
+               "EMPTY_VALUE=",
+               "VAR=            value")
+       vt.Run()
+}
+
 func (s *Suite) Test_VaralignBlock_Process__autofix(c *check.C) {
        t := s.Init(c)
 
@@ -2345,14 +2420,105 @@ func (s *Suite) Test_VaralignBlock_reali
        mklines := t.NewMkLines("filename.mk",
                MkCvsID,
                "VAR=\t${VAR}",
-               // FIXME: It's not possible to align with tabs to column 21.
                "LONG_VARIABLE_NAME=    \t        \\",
                "\t${LONG_VARIABLE_NAME}")
 
        mklines.Check()
 
        t.CheckOutputLines(
-               "NOTE: filename.mk:3: This variable value should be aligned with tabs, not spaces, to column 21.")
+               "NOTE: filename.mk:3: This outlier variable should be aligned with a single space.")
+}
+
+func (s *Suite) Test_VaralignBlock_realignMultiEmptyInitial__spaces(c *check.C) {
+       vt := NewVaralignTester(s, c)
+       vt.Input(
+               "VAR=    \\",
+               "\tvalue",
+               // This line is necessary to trigger the realignment; see VaralignBlock.Finish.
+               "VAR= value")
+       vt.Internals(
+               "04 08",
+               "   08",
+               "04 05")
+       vt.Diagnostics(
+               "NOTE: ~/Makefile:1: Variable values should be aligned with tabs, not spaces.",
+               "NOTE: ~/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 9.")
+       vt.Autofixes(
+               "AUTOFIX: ~/Makefile:1: Replacing \"    \" with \"\\t\".",
+               "AUTOFIX: ~/Makefile:3: Replacing \" \" with \"\\t\".")
+       vt.Fixed(
+               "VAR=    \\",
+               "        value",
+               "VAR=    value")
+       vt.Run()
+}
+
+func (s *Suite) Test_VaralignBlock_realignMultiInitial__spaces(c *check.C) {
+       vt := NewVaralignTester(s, c)
+       vt.Input(
+               "VAR=    value1 \\",
+               "        value2")
+       vt.Internals(
+               "04 08",
+               "   08")
+       vt.Diagnostics(
+               "NOTE: ~/Makefile:1: Variable values should be aligned with tabs, not spaces.",
+               "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\".")
+       vt.Autofixes(
+               "AUTOFIX: ~/Makefile:1: Replacing \"    \" with \"\\t\".",
+               "AUTOFIX: ~/Makefile:2: Replacing \"        \" with \"\\t\".")
+       vt.Fixed(
+               "VAR=    value1 \\",
+               "        value2")
+       vt.Run()
+}
+
+// This example is quite unrealistic since typically the first line is
+// the least indented.
+//
+// All follow-up lines are indented with at least one tab, to make clear
+// they are continuation lines.
+func (s *Suite) Test_VaralignBlock_realignMultiEmptyFollow(c *check.C) {
+       vt := NewVaralignTester(s, c)
+       vt.Input(
+               "VAR= \\",
+               "        value1 \\",
+               "          value2 \\",
+               "      value3 \\",
+               "value4 \\",
+               "\\",
+               "# comment")
+       vt.Internals(
+               "04 05",
+               "   08",
+               "   10",
+               "   06",
+               "   00",
+               "   00",
+               "   00")
+       vt.Diagnostics(
+               "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\".",
+               "NOTE: ~/Makefile:3: This continuation line should be indented with \"\\t  \".",
+               "NOTE: ~/Makefile:4: This continuation line should be indented with \"\\t\".",
+               "NOTE: ~/Makefile:5: This continuation line should be indented with \"\\t\".",
+               "NOTE: ~/Makefile:6: This continuation line should be indented with \"\\t\".",
+               "NOTE: ~/Makefile:7: This continuation line should be indented with \"\\t\".")
+       vt.Autofixes(
+               "AUTOFIX: ~/Makefile:2: Replacing \"        \" with \"\\t\".",
+               "AUTOFIX: ~/Makefile:3: Replacing \"          \" with \"\\t  \".",
+               "AUTOFIX: ~/Makefile:4: Replacing \"      \" with \"\\t\".",
+               "AUTOFIX: ~/Makefile:5: Replacing \"\" with \"\\t\".",
+               "AUTOFIX: ~/Makefile:6: Replacing \"\" with \"\\t\".",
+               "AUTOFIX: ~/Makefile:7: Replacing \"\" with \"\\t\".")
+       vt.Fixed(
+               "VAR= \\",
+               "        value1 \\",
+               "          value2 \\",
+               "        value3 \\",
+               "        value4 \\",
+               "        \\",
+               "        # comment")
+       vt.Run()
 }
 
 func (s *Suite) Test_VaralignBlock_split(c *check.C) {
@@ -2546,6 +2712,68 @@ func (s *Suite) Test_VaralignBlock_split
                        continuation:      "\\",
                })
 
+       // A follow-up line may start with a comment character. There are
+       // two possible interpretations:
+       //
+       // 1. It is a leading comment, and the rest of the line is parsed
+       // as usual.
+       //
+       // 2. It is a continuation of the value, and therefore the value ends
+       // here; everything after this line is part of the trailing comment.
+       //
+       // The character that follows the comment character decides which
+       // interpretation is used. A space makes the comment a trailing
+       // comment since that's the way these trailing comments typically look.
+       // Any other character makes it a leading comment.
+
+       test("#\tcomment", false,
+               varalignSplitResult{
+                       leadingComment:    "#",
+                       varnameOp:         "",
+                       spaceBeforeValue:  "\t",
+                       value:             "comment",
+                       spaceAfterValue:   "",
+                       trailingComment:   "",
+                       spaceAfterComment: "",
+                       continuation:      "",
+               })
+
+       test("#\tcomment \\", false,
+               varalignSplitResult{
+                       leadingComment:    "#",
+                       varnameOp:         "",
+                       spaceBeforeValue:  "\t",
+                       value:             "comment",
+                       spaceAfterValue:   " ",
+                       trailingComment:   "",
+                       spaceAfterComment: "",
+                       continuation:      "\\",
+               })
+
+       test("# comment", false,
+               varalignSplitResult{
+                       leadingComment:    "",
+                       varnameOp:         "",
+                       spaceBeforeValue:  "",
+                       value:             "",
+                       spaceAfterValue:   "",
+                       trailingComment:   "# comment",
+                       spaceAfterComment: "",
+                       continuation:      "",
+               })
+
+       test("# comment \\", false,
+               varalignSplitResult{
+                       leadingComment:    "",
+                       varnameOp:         "",
+                       spaceBeforeValue:  "",
+                       value:             "",
+                       spaceAfterValue:   "",
+                       trailingComment:   "# comment",
+                       spaceAfterComment: " ",
+                       continuation:      "\\",
+               })
+
        // Commented variable assignments are only valid if they
        // directly follow the comment sign.
        //
@@ -2555,6 +2783,26 @@ func (s *Suite) Test_VaralignBlock_split
                func() { test("#  VAR=    value", true, varalignSplitResult{}) })
 }
 
+// This test runs canonicalInitial directly since as of August 2019
+// that function is only used in a single place, and from this place
+// varnameOpSpaceWidth is always bigger than width.
+func (s *Suite) Test_varalignLine_canonicalInitial(c *check.C) {
+       t := s.Init(c)
+
+       var v varalignLine
+       v.parts.varnameOp = "LONG.123456789="
+       v.parts.spaceBeforeValue = " "
+       t.CheckEquals(v.canonicalInitial(16), false)
+
+       v.parts.varnameOp = "LONG.1234567890="
+
+       t.CheckEquals(v.canonicalInitial(16), true)
+
+       v.parts.spaceBeforeValue = ""
+
+       t.CheckEquals(v.canonicalInitial(16), false)
+}
+
 func (s *Suite) Test_varalignLine_canonicalFollow(c *check.C) {
        t := s.Init(c)
 

Index: pkgsrc/pkgtools/pkglint/files/vardefs.go
diff -u pkgsrc/pkgtools/pkglint/files/vardefs.go:1.69 pkgsrc/pkgtools/pkglint/files/vardefs.go:1.70
--- pkgsrc/pkgtools/pkglint/files/vardefs.go:1.69       Fri Aug 16 21:00:17 2019
+++ pkgsrc/pkgtools/pkglint/files/vardefs.go    Wed Aug 21 16:45:17 2019
@@ -311,7 +311,12 @@ func (reg *VarTypeRegistry) infralist(va
 // compilerLanguages reads the available languages that are typically
 // bundled in a single compiler framework, such as GCC or Clang.
 func (reg *VarTypeRegistry) compilerLanguages(src *Pkgsrc) *BasicType {
-       mklines := LoadMk(src.File("mk/compiler.mk"), NotEmpty)
+       options := NotEmpty
+       if !G.Testing {
+               options = NotEmpty | MustSucceed
+       }
+       mklines := LoadMk(src.File("mk/compiler.mk"), options)
+
        languages := make(map[string]bool)
        if mklines != nil {
                for _, mkline := range mklines.mklines {
@@ -321,15 +326,19 @@ func (reg *VarTypeRegistry) compilerLang
                                        languages[intern(word)] = true
                                }
                        }
-               }
-       }
 
-       alwaysAvailable := [...]string{
-               "ada", "c", "c99", "c++", "c++11", "c++14",
-               "fortran", "fortran77", "java", "objc", "obj-c++"}
-
-       for _, language := range alwaysAvailable {
-               languages[language] = true
+                       if mkline.IsDirective() && mkline.Cond() != nil {
+                               mkline.Cond().Walk(&MkCondCallback{
+                                       VarUse: func(varuse *MkVarUse) {
+                                               if varuse.varname == "USE_LANGUAGES" && len(varuse.modifiers) == 1 {
+                                                       ok, _, pattern, exact := varuse.modifiers[0].MatchMatch()
+                                                       if ok && exact && !containsVarRef(pattern) {
+                                                               languages[intern(pattern)] = true
+                                                       }
+                                               }
+                                       }})
+                       }
+               }
        }
 
        joined := keysJoined(languages)
@@ -1529,6 +1538,8 @@ func (reg *VarTypeRegistry) Init(src *Pk
                "*: use, use-loadtime")
        reg.sys("RUN", BtShellCommand)
        reg.sys("RUN_LDCONFIG", BtYesNo)
+       reg.pkg("R_PKGNAME", BtRPkgName)
+       reg.pkg("R_PKGVER", BtRPkgVer)
        reg.pkglist("SCRIPTS_ENV", BtShellWord)
        reg.usrlist("SETGID_GAMES_PERMS", BtPerms)
        reg.usrlist("SETUID_ROOT_PERMS", BtPerms)

Index: pkgsrc/pkgtools/pkglint/files/vardefs_test.go
diff -u pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.19 pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.20
--- pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.19  Fri Aug 16 21:00:17 2019
+++ pkgsrc/pkgtools/pkglint/files/vardefs_test.go       Wed Aug 21 16:45:17 2019
@@ -12,6 +12,35 @@ func (s *Suite) Test_VarTypeRegistry_Ini
        t.CheckEquals(src.vartypes.Canon("USE_BUILTIN.*").basicType.name, "YesNoIndirectly")
 }
 
+func (s *Suite) Test_VarTypeRegistry_compilerLanguages(c *check.C) {
+       t := s.Init(c)
+
+       G.Testing = false // Just for code coverage
+       t.CreateFileLines("mk/compiler.mk",
+               MkCvsID,
+               "",
+               "_CXX_STD_VERSIONS=      gnu++14",
+               ".for _version_ in ${_CXX_STD_VERSIONS}",
+               ".  if !empty(USE_LANGUAGES:M${_version_})",
+               "USE_LANGUAGES+=         c++",
+               ".  endif",
+               ".endfor",
+               "",
+               ".if ${USE_LANGUAGES:Mexpr-lang} || !empty(USE_LANGUAGES:Mempty-lang)",
+               ".endif",
+               "",
+               // Just for code coverage
+               ".if ${OTHER} || ${USE_LANGUAGES} \\",
+               " || ${USE_LANGUAGES:O} || ${USE_LANGUAGES:Mc++-*}",
+               ".endif")
+       reg := NewVarTypeRegistry()
+
+       compilerLanguages := reg.compilerLanguages(&G.Pkgsrc)
+
+       enumValues := compilerLanguages.AllowedEnums()
+       t.CheckEquals(enumValues, "empty-lang expr-lang gnu++14")
+}
+
 func (s *Suite) Test_VarTypeRegistry_enumFrom(c *check.C) {
        t := s.Init(c)
 
@@ -38,7 +67,10 @@ func (s *Suite) Test_VarTypeRegistry_enu
                ".  if !empty(USE_LANGUAGES:M${_version_})",
                "USE_LANGUAGES+=         c++",
                ".  endif",
-               ".endfor")
+               ".endfor",
+               "",
+               ".if ${USE_LANGUAGES:Mexpr-lang} || !empty(USE_LANGUAGES:Mempty-lang)",
+               ".endif")
 
        t.SetUpVartypes()
 
@@ -49,8 +81,8 @@ func (s *Suite) Test_VarTypeRegistry_enu
 
        test("EMACS_VERSIONS_ACCEPTED", "enum: emacs29 emacs31  (list, package-settable)")
        test("PKG_JVM", "enum: jdk16 openjdk7 openjdk8 oracle-jdk8 sun-jdk7  (system-provided)")
-       test("USE_LANGUAGES", "enum: ada c c++ c++03 c++0x c++11 c++14 c99 "+
-               "fortran fortran77 gnu++03 gnu++0x gnu++11 gnu++14 java obj-c++ objc  (list, package-settable)")
+       test("USE_LANGUAGES", "enum: c++03 c++0x c++11 c++14 empty-lang expr-lang "+
+               "gnu++03 gnu++0x gnu++11 gnu++14  (list, package-settable)")
        test("PKGSRC_COMPILER", "enum: ccache distcc f2c g95 gcc ido mipspro-ucode sunpro  (list, user-settable)")
 }
 

Index: pkgsrc/pkgtools/pkglint/files/vartype.go
diff -u pkgsrc/pkgtools/pkglint/files/vartype.go:1.35 pkgsrc/pkgtools/pkglint/files/vartype.go:1.36
--- pkgsrc/pkgtools/pkglint/files/vartype.go:1.35       Sun Jul 14 21:25:47 2019
+++ pkgsrc/pkgtools/pkglint/files/vartype.go    Wed Aug 21 16:45:17 2019
@@ -264,6 +264,8 @@ func (bt *BasicType) NeedsQ() bool {
                BtPkgRevision,
                BtPrefixPathname,
                BtPythonDependency,
+               BtRPkgName,
+               BtRPkgVer,
                BtRelativePkgDir,
                BtRelativePkgPath,
                BtStage,
@@ -338,6 +340,8 @@ var (
        BtPkgRevision            = &BasicType{"PkgRevision", (*VartypeCheck).PkgRevision}
        BtPrefixPathname         = &BasicType{"PrefixPathname", (*VartypeCheck).PrefixPathname}
        BtPythonDependency       = &BasicType{"PythonDependency", (*VartypeCheck).PythonDependency}
+       BtRPkgName               = &BasicType{"RPkgName", (*VartypeCheck).RPkgName}
+       BtRPkgVer                = &BasicType{"RPkgVer", (*VartypeCheck).RPkgVer}
        BtRelativePkgDir         = &BasicType{"RelativePkgDir", (*VartypeCheck).RelativePkgDir}
        BtRelativePkgPath        = &BasicType{"RelativePkgPath", (*VartypeCheck).RelativePkgPath}
        BtRestricted             = &BasicType{"Restricted", (*VartypeCheck).Restricted}

Index: pkgsrc/pkgtools/pkglint/files/vartypecheck.go
diff -u pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.61 pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.62
--- pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.61  Thu Aug  1 22:38:49 2019
+++ pkgsrc/pkgtools/pkglint/files/vartypecheck.go       Wed Aug 21 16:45:17 2019
@@ -793,12 +793,30 @@ func (cv *VartypeCheck) Option() {
                return
        }
 
-       if m, optname := match1(value, `^-?([a-z][-0-9a-z+]*)$`); m {
-               if !cv.MkLines.once.FirstTimeSlice("option:", optname) {
-                       return
-               }
+       validName := regex.Pattern(`^-?([a-z][-0-9a-z_+]*)$`)
+       if cv.Op == opUseMatch {
+               validName = `^-?([a-z][*+\-0-9?\[\]_a-z]*)$`
+       }
+
+       // TODO: Distinguish between:
+       //  - a bare option name (must start with a letter),
+       //  - an option selection (may have a leading hyphen).
+       m, optname := match1(value, validName)
+       if !m {
+               cv.Errorf("Invalid option name %q. Option names must start with a lowercase letter and be all-lowercase.", value)
+               return
+       }
+
+       if !cv.MkLines.once.FirstTimeSlice("option:", optname) {
+               return
+       }
 
-               // There's a difference between empty and absent here.
+       if contains(optname, "_") {
+               cv.Warnf("Use of the underscore character in option names is deprecated.")
+               return
+       }
+
+       if !strings.ContainsAny(optname, "*?[") {
                if _, found := G.Pkgsrc.PkgOptions[optname]; !found {
                        cv.Warnf("Unknown option %q.", optname)
                        cv.Explain(
@@ -807,15 +825,7 @@ func (cv *VartypeCheck) Option() {
                                "update that file yourself or suggest a description for this option",
                                "on the tech-pkg%NetBSD.org@localhost mailing list.")
                }
-               return
        }
-
-       if matches(value, `^-?([a-z][-0-9a-z_\+]*)$`) {
-               cv.Warnf("Use of the underscore character in option names is deprecated.")
-               return
-       }
-
-       cv.Errorf("Invalid option name %q. Option names must start with a lowercase letter and be all-lowercase.", value)
 }
 
 // Pathlist checks variables like the PATH environment variable.
@@ -911,6 +921,10 @@ func (cv *VartypeCheck) Pkgname() {
                        "A valid package name has the form packagename-version, where version",
                        "consists only of digits, letters and dots.")
        }
+
+       if matches(value, `nb\d+$`) {
+               cv.Errorf("The \"nb\" part of the version number belongs in PKGREVISION.")
+       }
 }
 
 func (cv *VartypeCheck) PkgOptionsVar() {
@@ -1019,6 +1033,33 @@ func (cv *VartypeCheck) PythonDependency
        }
 }
 
+func (cv *VartypeCheck) RPkgName() {
+       if cv.Op == opUseMatch {
+               return
+       }
+
+       if cv.Value != cv.ValueNoVar {
+               cv.Warnf("The R package name should not contain variables.")
+               return
+       }
+
+       if hasPrefix(cv.Value, "R-") {
+               cv.Warnf("The %s does not need the %q prefix.", cv.Varname, "R-")
+       }
+
+       invalid := replaceAll(cv.Value, `[\w\-.+]`, "")
+       if invalid != "" {
+               cv.Warnf("The R package name contains the invalid characters %q.", invalid)
+       }
+}
+
+func (cv *VartypeCheck) RPkgVer() {
+       value := cv.Value
+       if cv.Op != opUseMatch && value == cv.ValueNoVar && !matches(value, `^\d[\w-.]*$`) {
+               cv.Warnf("Invalid R version number %q.", value)
+       }
+}
+
 // RelativePkgDir refers to a package directory, e.g. ../../category/pkgbase.
 func (cv *VartypeCheck) RelativePkgDir() {
        MkLineChecker{cv.MkLines, cv.MkLine}.CheckRelativePkgdir(cv.Value)

Index: pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go
diff -u pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.53 pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.54
--- pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.53     Tue Jul 30 18:16:13 2019
+++ pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go  Wed Aug 21 16:45:17 2019
@@ -1063,6 +1063,12 @@ func (s *Suite) Test_VartypeCheck_Pkgnam
        vt.Output(
                "WARN: filename.mk:8: \"pkgbase-z1\" is not a valid package name.")
 
+       vt.Values(
+               "pkgbase-1.0nb17")
+
+       vt.Output(
+               "ERROR: filename.mk:11: The \"nb\" part of the version number belongs in PKGREVISION.")
+
        vt.Op(opUseMatch)
        vt.Values(
                "pkgbase-[0-9]*")
@@ -1124,6 +1130,18 @@ func (s *Suite) Test_VartypeCheck_PkgRev
        vt.OutputEmpty()
 }
 
+func (s *Suite) Test_VartypeCheck_PrefixPathname(c *check.C) {
+       vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).PrefixPathname)
+
+       vt.Varname("PKGMANDIR")
+       vt.Values(
+               "man/man1",
+               "share/locale")
+
+       vt.Output(
+               "WARN: filename.mk:1: Please use \"${PKGMANDIR}/man1\" instead of \"man/man1\".")
+}
+
 func (s *Suite) Test_VartypeCheck_PythonDependency(c *check.C) {
        vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).PythonDependency)
 
@@ -1138,16 +1156,47 @@ func (s *Suite) Test_VartypeCheck_Python
                "WARN: filename.mk:3: Invalid Python dependency \"cairo,X\".")
 }
 
-func (s *Suite) Test_VartypeCheck_PrefixPathname(c *check.C) {
-       vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).PrefixPathname)
+func (s *Suite) Test_VartypeCheck_RPkgName(c *check.C) {
+       vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).RPkgName)
 
-       vt.Varname("PKGMANDIR")
+       vt.Varname("R_PKGNAME")
        vt.Values(
-               "man/man1",
-               "share/locale")
+               "package",
+               "${VAR}",
+               "a,b,c",
+               "under_score",
+               "R-package")
 
        vt.Output(
-               "WARN: filename.mk:1: Please use \"${PKGMANDIR}/man1\" instead of \"man/man1\".")
+               "WARN: filename.mk:2: The R package name should not contain variables.",
+               "WARN: filename.mk:3: The R package name contains the invalid characters \",,\".",
+               "WARN: filename.mk:5: The R_PKGNAME does not need the \"R-\" prefix.")
+
+       vt.Op(opUseMatch)
+       vt.Values(
+               "R-package")
+
+       vt.OutputEmpty()
+}
+
+func (s *Suite) Test_VartypeCheck_RPkgVer(c *check.C) {
+       vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).RPkgVer)
+
+       vt.Varname("R_PKGVER")
+       vt.Values(
+               "1.0",
+               "1-2-3",
+               "${VERSION}",
+               "1-:")
+
+       vt.Output(
+               "WARN: filename.mk:4: Invalid R version number \"1-:\".")
+
+       vt.Op(opUseMatch)
+       vt.Values(
+               "1-:")
+
+       vt.OutputEmpty()
 }
 
 func (s *Suite) Test_VartypeCheck_RelativePkgPath(c *check.C) {
@@ -1688,6 +1737,6 @@ func (vt *VartypeCheckTester) OutputEmpt
 
 func (vt *VartypeCheckTester) nextSection() {
        if vt.lineno%10 != 1 {
-               vt.lineno = vt.lineno - vt.lineno%10 + 11
+               vt.lineno += 9 - (vt.lineno+8)%10
        }
 }



Home | Main Index | Thread Index | Old Index