[BACK]Return to mklinechecker_test.go CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / pkgsrc / pkgtools / pkglint / files

Annotation of pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go, Revision 1.50

1.21      rillig      1: package pkglint
1.1       rillig      2:
1.27      rillig      3: import (
                      4:        "gopkg.in/check.v1"
                      5:        "runtime"
                      6: )
1.1       rillig      7:
1.48      rillig      8: // PR pkg/46570, item 2
                      9: func (s *Suite) Test_MkLineChecker__unclosed_varuse(c *check.C) {
                     10:        t := s.Init(c)
                     11:
                     12:        mklines := t.NewMkLines("Makefile",
                     13:                MkCvsID,
                     14:                "EGDIRS=\t${EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d")
                     15:
                     16:        mklines.Check()
                     17:
                     18:        t.CheckOutputLines(
                     19:                "WARN: Makefile:2: Missing closing \"}\" for \"EGDIR/pam.d\".",
                     20:                "WARN: Makefile:2: Invalid part \"/pam.d\" after variable name \"EGDIR\".",
                     21:                "WARN: Makefile:2: Missing closing \"}\" for \"EGDIR/dbus-1/system.d ${EGDIR/pam.d\".",
                     22:                "WARN: Makefile:2: Invalid part \"/dbus-1/system.d ${EGDIR/pam.d\" after variable name \"EGDIR\".",
                     23:                "WARN: Makefile:2: Missing closing \"}\" for \"EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d\".",
                     24:                "WARN: Makefile:2: Invalid part \"/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d\" after variable name \"EGDIR\".",
                     25:                "WARN: Makefile:2: EGDIRS is defined but not used.",
                     26:                "WARN: Makefile:2: EGDIR/pam.d is used but not defined.")
                     27: }
                     28:
                     29: func (s *Suite) Test_MkLineChecker_Check__url2pkg(c *check.C) {
                     30:        t := s.Init(c)
                     31:
                     32:        t.SetUpVartypes()
                     33:
                     34:        mklines := t.NewMkLines("filename.mk",
                     35:                MkCvsID,
                     36:                "# url2pkg-marker")
                     37:
                     38:        mklines.Check()
                     39:
                     40:        t.CheckOutputLines(
                     41:                "ERROR: filename.mk:2: This comment indicates unfinished work (url2pkg).")
                     42: }
                     43:
                     44: func (s *Suite) Test_MkLineChecker_Check__buildlink3_include_prefs(c *check.C) {
                     45:        t := s.Init(c)
                     46:
                     47:        t.SetUpVartypes()
                     48:
                     49:        t.CreateFileLines("mk/bsd.prefs.mk")
                     50:        t.CreateFileLines("mk/bsd.fast.prefs.mk")
                     51:        mklines := t.SetUpFileMkLines("category/package/buildlink3.mk",
                     52:                MkCvsID,
                     53:                ".include \"../../mk/bsd.prefs.mk\"",
                     54:                ".include \"../../mk/bsd.fast.prefs.mk\"")
                     55:
                     56:        // If the buildlink3.mk file doesn't actually exist, resolving the
                     57:        // relative path fails since that depends on the actual file system,
                     58:        // not on syntactical paths; see os.Stat in CheckRelativePath.
                     59:        //
1.50    ! rillig     60:        // TODO: Refactor Relpath to be independent of a filesystem.
1.48      rillig     61:
                     62:        mklines.Check()
                     63:
                     64:        t.CheckOutputLines(
                     65:                "NOTE: ~/category/package/buildlink3.mk:2: For efficiency reasons, " +
                     66:                        "please include bsd.fast.prefs.mk instead of bsd.prefs.mk.")
                     67: }
                     68:
                     69: func (s *Suite) Test_MkLineChecker_Check__warn_varuse_LOCALBASE(c *check.C) {
                     70:        t := s.Init(c)
                     71:
                     72:        t.SetUpVartypes()
                     73:        mklines := t.NewMkLines("options.mk",
                     74:                MkCvsID,
                     75:                "PKGNAME=\t${LOCALBASE}")
                     76:
                     77:        mklines.Check()
                     78:
                     79:        t.CheckOutputLines(
                     80:                "WARN: options.mk:2: Please use PREFIX instead of LOCALBASE.")
                     81: }
                     82:
                     83: func (s *Suite) Test_MkLineChecker_Check__varuse_modifier_L(c *check.C) {
                     84:        t := s.Init(c)
                     85:
                     86:        t.SetUpVartypes()
                     87:        mklines := t.NewMkLines("x11/xkeyboard-config/Makefile",
                     88:                MkCvsID,
                     89:                "FILES_SUBST+=\tXKBCOMP_SYMLINK=${${XKBBASE}/xkbcomp:L:Q}",
                     90:                "FILES_SUBST+=\tXKBCOMP_SYMLINK=${${XKBBASE}/xkbcomp:Q}")
                     91:
                     92:        mklines.Check()
                     93:
                     94:        // In line 2, don't warn that ${XKBBASE}/xkbcomp is used but not defined.
                     95:        // This is because the :L modifier interprets everything before as an expression
                     96:        // instead of a variable name.
                     97:        //
                     98:        // In line 3 the :L modifier is missing, therefore ${XKBBASE}/xkbcomp is the
                     99:        // name of another variable, and that variable is not known. Only XKBBASE is known.
                    100:        //
                    101:        // In line 3, warn about the invalid "/" as part of the variable name.
                    102:        t.CheckOutputLines(
                    103:                "WARN: x11/xkeyboard-config/Makefile:3: "+
                    104:                        "Invalid part \"/xkbcomp\" after variable name \"${XKBBASE}\".",
1.50    ! rillig    105:                // TODO: Avoid these duplicate diagnostics.
        !           106:                "WARN: x11/xkeyboard-config/Makefile:3: "+
        !           107:                        "Invalid part \"/xkbcomp\" after variable name \"${XKBBASE}\".",
1.49      rillig    108:                "WARN: x11/xkeyboard-config/Makefile:3: "+
                    109:                        "Invalid part \"/xkbcomp\" after variable name \"${XKBBASE}\".",
1.48      rillig    110:                "WARN: x11/xkeyboard-config/Makefile:3: XKBBASE is used but not defined.")
                    111: }
                    112:
1.41      rillig    113: func (s *Suite) Test_MkLineChecker_checkEmptyContinuation(c *check.C) {
                    114:        t := s.Init(c)
                    115:
                    116:        mklines := t.SetUpFileMkLines("filename.mk",
                    117:                MkCvsID,
                    118:                "# line 1 \\",
                    119:                "",
                    120:                "# line 2")
                    121:
                    122:        // Don't check this when loading a file, since otherwise the infrastructure
                    123:        // files could possibly get this warning. Sure, they should be fixed, but
                    124:        // it's not in the focus of the package maintainer.
                    125:        t.CheckOutputEmpty()
                    126:
                    127:        mklines.Check()
                    128:
                    129:        t.CheckOutputLines(
1.50    ! rillig    130:                "NOTE: ~/filename.mk:2--3: Trailing whitespace.",
1.41      rillig    131:                "WARN: ~/filename.mk:3: This line looks empty but continues the previous line.")
                    132: }
                    133:
1.48      rillig    134: // Pkglint once interpreted all lists as consisting of shell tokens,
                    135: // splitting this URL at the ampersand.
                    136: func (s *Suite) Test_MkLineChecker_checkVarassign__URL_with_shell_special_characters(c *check.C) {
                    137:        t := s.Init(c)
                    138:
                    139:        G.Pkg = NewPackage(t.File("graphics/gimp-fix-ca"))
                    140:        t.SetUpVartypes()
                    141:        mklines := t.NewMkLines("filename.mk",
                    142:                MkCvsID,
                    143:                "MASTER_SITES=\thttp://registry.gimp.org/file/fix-ca.c?action=download&id=9884&file=")
                    144:
                    145:        mklines.Check()
                    146:
                    147:        t.CheckOutputEmpty()
                    148: }
                    149:
                    150: func (s *Suite) Test_MkLineChecker_checkVarassign__list(c *check.C) {
1.47      rillig    151:        t := s.Init(c)
                    152:
1.48      rillig    153:        t.SetUpMasterSite("MASTER_SITE_GITHUB", "https://github.com/")
                    154:        t.SetUpVartypes()
                    155:        t.SetUpCommandLine("-Wall", "--explain")
                    156:        mklines := t.NewMkLines("filename.mk",
1.47      rillig    157:                MkCvsID,
1.48      rillig    158:                "SITES.distfile=\t-${MASTER_SITE_GITHUB:=project/}")
                    159:
                    160:        mklines.Check()
                    161:
                    162:        t.CheckOutputLines(
                    163:                "WARN: filename.mk:2: The list variable MASTER_SITE_GITHUB should not be embedded in a word.",
                    164:                "",
                    165:                "\tWhen a list variable has multiple elements, this expression expands",
                    166:                "\tto something unexpected:",
                    167:                "",
                    168:                "\tExample: ${MASTER_SITE_SOURCEFORGE}directory/ expands to",
                    169:                "",
                    170:                "\t\thttps://mirror1.sf.net/ https://mirror2.sf.net/directory/",
                    171:                "",
                    172:                "\tThe first URL is missing the directory. To fix this, write",
                    173:                "\t\t${MASTER_SITE_SOURCEFORGE:=directory/}.",
                    174:                "",
                    175:                "\tExample: -l${LIBS} expands to",
                    176:                "",
                    177:                "\t\t-llib1 lib2",
1.47      rillig    178:                "",
1.48      rillig    179:                "\tThe second library is missing the -l. To fix this, write",
                    180:                "\t${LIBS:S,^,-l,}.",
                    181:                "")
                    182: }
                    183:
                    184: func (s *Suite) Test_MkLineChecker_checkVarassign(c *check.C) {
                    185:        t := s.Init(c)
                    186:
                    187:        t.SetUpVartypes()
                    188:
                    189:        mklines := t.NewMkLines("Makefile",
                    190:                MkCvsID,
                    191:                "ac_cv_libpari_libs+=\t-L${BUILDLINK_PREFIX.pari}/lib") // From math/clisp-pari/Makefile, rev. 1.8
1.47      rillig    192:
                    193:        mklines.Check()
                    194:
                    195:        t.CheckOutputLines(
1.48      rillig    196:                "WARN: Makefile:2: ac_cv_libpari_libs is defined but not used.")
1.47      rillig    197: }
                    198:
1.22      rillig    199: func (s *Suite) Test_MkLineChecker_checkVarassignLeft(c *check.C) {
                    200:        t := s.Init(c)
                    201:
1.28      rillig    202:        mklines := t.NewMkLines("module.mk",
1.38      rillig    203:                MkCvsID,
1.28      rillig    204:                "_VARNAME=\tvalue")
1.29      rillig    205:        // Only to prevent "defined but not used".
1.38      rillig    206:        mklines.vars.Use("_VARNAME", mklines.mklines[1], VucRunTime)
1.22      rillig    207:
1.28      rillig    208:        mklines.Check()
1.22      rillig    209:
                    210:        t.CheckOutputLines(
1.28      rillig    211:                "WARN: module.mk:2: Variable names starting with an underscore " +
                    212:                        "(_VARNAME) are reserved for internal pkgsrc use.")
1.22      rillig    213: }
                    214:
1.48      rillig    215: // Files from the pkgsrc infrastructure may define and use variables
                    216: // whose name starts with an underscore.
                    217: func (s *Suite) Test_MkLineChecker_checkVarassignLeft__infrastructure(c *check.C) {
                    218:        t := s.Init(c)
                    219:
                    220:        t.SetUpPkgsrc()
                    221:        t.CreateFileLines("mk/infra.mk",
                    222:                MkCvsID,
                    223:                "_VARNAME=\t\tvalue",
                    224:                "_SORTED_VARS.group=\tVARNAME")
                    225:        t.FinishSetUp()
                    226:
                    227:        G.Check(t.File("mk/infra.mk"))
                    228:
                    229:        t.CheckOutputLines(
                    230:                "WARN: ~/mk/infra.mk:2: _VARNAME is defined but not used.")
                    231: }
                    232:
                    233: func (s *Suite) Test_MkLineChecker_checkVarassignLeft__documented_underscore(c *check.C) {
                    234:        t := s.Init(c)
                    235:
                    236:        t.SetUpPkgsrc()
                    237:        t.CreateFileLines("category/package/filename.mk",
                    238:                MkCvsID,
                    239:                "_SORTED_VARS.group=\tVARNAME")
                    240:        t.FinishSetUp()
                    241:
                    242:        G.Check(t.File("category/package/filename.mk"))
                    243:
                    244:        t.CheckOutputEmpty()
                    245: }
                    246:
1.27      rillig    247: func (s *Suite) Test_MkLineChecker_checkVarassignLeftNotUsed__procedure_call(c *check.C) {
                    248:        t := s.Init(c)
                    249:
                    250:        t.CreateFileLines("mk/pkg-build-options.mk")
                    251:        mklines := t.SetUpFileMkLines("category/package/filename.mk",
1.38      rillig    252:                MkCvsID,
1.27      rillig    253:                "",
                    254:                "pkgbase := glib2",
                    255:                ".include \"../../mk/pkg-build-options.mk\"",
                    256:                "",
                    257:                "VAR=\tvalue")
                    258:
                    259:        mklines.Check()
                    260:
                    261:        // There is no warning for pkgbase although it looks unused as well.
                    262:        // The file pkg-build-options.mk is essentially a procedure call,
                    263:        // and pkgbase is its parameter.
                    264:        //
                    265:        // To distinguish these parameters from ordinary variables, they are
                    266:        // usually written with the := operator instead of the = operator.
                    267:        // This has the added benefit that the parameter is only evaluated
                    268:        // once, especially if it contains references to other variables.
                    269:        t.CheckOutputLines(
                    270:                "WARN: ~/category/package/filename.mk:6: VAR is defined but not used.")
                    271: }
                    272:
1.37      rillig    273: func (s *Suite) Test_MkLineChecker_checkVarassignLeftNotUsed__procedure_call_no_tracing(c *check.C) {
                    274:        t := s.Init(c)
                    275:
                    276:        t.DisableTracing() // Just for code coverage
                    277:        t.CreateFileLines("mk/pkg-build-options.mk")
                    278:        mklines := t.SetUpFileMkLines("category/package/filename.mk",
1.38      rillig    279:                MkCvsID,
1.37      rillig    280:                "",
                    281:                "pkgbase := glib2",
                    282:                ".include \"../../mk/pkg-build-options.mk\"")
                    283:
                    284:        mklines.Check()
                    285:
                    286:        t.CheckOutputEmpty()
                    287: }
                    288:
1.28      rillig    289: func (s *Suite) Test_MkLineChecker_checkVarassignLeftNotUsed__infra(c *check.C) {
                    290:        t := s.Init(c)
                    291:
                    292:        t.CreateFileLines("mk/infra.mk",
1.38      rillig    293:                MkCvsID,
1.28      rillig    294:                "#",
                    295:                "# Package-settable variables:",
                    296:                "#",
                    297:                "# SHORT_DOCUMENTATION",
                    298:                "#\tIf set to no, ...",
                    299:                "#\tsecond line.",
                    300:                "#",
                    301:                "#",
                    302:                ".if ${USED_IN_INFRASTRUCTURE:Uyes:tl} == yes",
                    303:                ".endif")
                    304:        t.SetUpPackage("category/package",
                    305:                "USED_IN_INFRASTRUCTURE=\t${SHORT_DOCUMENTATION}",
                    306:                "",
                    307:                "UNUSED_INFRA=\t${UNDOCUMENTED}")
1.30      rillig    308:        t.FinishSetUp()
1.28      rillig    309:
                    310:        G.Check(t.File("category/package"))
                    311:
                    312:        t.CheckOutputLines(
                    313:                "WARN: ~/category/package/Makefile:22: UNUSED_INFRA is defined but not used.",
                    314:                "WARN: ~/category/package/Makefile:22: UNDOCUMENTED is used but not defined.")
                    315: }
                    316:
1.48      rillig    317: func (s *Suite) Test_MkLineChecker_checkVarassignLeftBsdPrefs__vartype_nil(c *check.C) {
1.27      rillig    318:        t := s.Init(c)
                    319:
1.48      rillig    320:        mklines := t.NewMkLines("builtin.mk",
1.38      rillig    321:                MkCvsID,
1.48      rillig    322:                "VAR_SH?=\tvalue")
1.27      rillig    323:
1.48      rillig    324:        mklines.Check()
1.27      rillig    325:
                    326:        t.CheckOutputLines(
1.48      rillig    327:                "WARN: builtin.mk:2: VAR_SH is defined but not used.",
                    328:                "WARN: builtin.mk:2: Please include \"../../mk/bsd.prefs.mk\" before using \"?=\".")
1.38      rillig    329: }
                    330:
1.32      rillig    331: func (s *Suite) Test_MkLineChecker_checkVarassignLeftUserSettable(c *check.C) {
                    332:        t := s.Init(c)
                    333:
                    334:        // TODO: Allow CreateFileLines before SetUpPackage, since it matches
                    335:        //  the expected reading order of human readers.
                    336:
                    337:        t.SetUpPackage("category/package",
1.45      rillig    338:                "ASSIGN_DIFF=\t\tpkg",          // assignment, differs from default value
                    339:                "ASSIGN_DIFF2=\t\treally # ok", // ok because of the rationale in the comment
                    340:                "ASSIGN_SAME=\t\tdefault",      // assignment, same value as default
                    341:                "DEFAULT_DIFF?=\t\tpkg",        // default, differs from default value
                    342:                "DEFAULT_SAME?=\t\tdefault",    // same value as default
                    343:                "FETCH_USING=\t\tcurl",         // both user-settable and package-settable
                    344:                "APPEND_DIRS+=\t\tdir3",        // appending requires a separate diagnostic
                    345:                "COMMENTED_SAME?=\tdefault",    // commented default, same value as default
                    346:                "COMMENTED_DIFF?=\tpkg")        // commented default, differs from default value
1.32      rillig    347:        t.CreateFileLines("mk/defaults/mk.conf",
1.38      rillig    348:                MkCvsID,
1.32      rillig    349:                "ASSIGN_DIFF?=default",
                    350:                "ASSIGN_DIFF2?=default",
                    351:                "ASSIGN_SAME?=default",
                    352:                "DEFAULT_DIFF?=\tdefault",
                    353:                "DEFAULT_SAME?=\tdefault",
                    354:                "FETCH_USING=\tauto",
                    355:                "APPEND_DIRS=\tdefault",
                    356:                "#COMMENTED_SAME?=\tdefault",
                    357:                "#COMMENTED_DIFF?=\tdefault")
                    358:        t.Chdir("category/package")
                    359:        t.FinishSetUp()
                    360:
                    361:        G.Check(".")
                    362:
                    363:        t.CheckOutputLines(
                    364:                "WARN: Makefile:20: Package sets user-defined \"ASSIGN_DIFF\" to \"pkg\", "+
                    365:                        "which differs from the default value \"default\" from mk/defaults/mk.conf.",
                    366:                "NOTE: Makefile:22: Redundant definition for ASSIGN_SAME from mk/defaults/mk.conf.",
                    367:                "WARN: Makefile:23: Please include \"../../mk/bsd.prefs.mk\" before using \"?=\".",
                    368:                "WARN: Makefile:23: Package sets user-defined \"DEFAULT_DIFF\" to \"pkg\", "+
                    369:                        "which differs from the default value \"default\" from mk/defaults/mk.conf.",
                    370:                "NOTE: Makefile:24: Redundant definition for DEFAULT_SAME from mk/defaults/mk.conf.",
                    371:                "WARN: Makefile:26: Packages should not append to user-settable APPEND_DIRS.",
                    372:                "WARN: Makefile:28: Package sets user-defined \"COMMENTED_DIFF\" to \"pkg\", "+
                    373:                        "which differs from the default value \"default\" from mk/defaults/mk.conf.")
                    374: }
                    375:
1.37      rillig    376: func (s *Suite) Test_MkLineChecker_checkVarassignLeftUserSettable__before_prefs(c *check.C) {
                    377:        t := s.Init(c)
                    378:
                    379:        t.SetUpCommandLine("-Wall", "--explain")
                    380:        t.SetUpPackage("category/package",
                    381:                "BEFORE=\tvalue",
                    382:                ".include \"../../mk/bsd.prefs.mk\"")
                    383:        t.CreateFileLines("mk/defaults/mk.conf",
1.38      rillig    384:                MkCvsID,
1.37      rillig    385:                "BEFORE?=\tvalue")
                    386:        t.Chdir("category/package")
                    387:        t.FinishSetUp()
                    388:
                    389:        G.Check(".")
                    390:
                    391:        t.CheckOutputLines(
                    392:                "NOTE: Makefile:20: Redundant definition for BEFORE from mk/defaults/mk.conf.",
                    393:                "",
                    394:                "\tInstead of defining the variable redundantly, it suffices to include",
                    395:                "\t../../mk/bsd.prefs.mk, which provides all user-settable variables.",
                    396:                "")
                    397: }
                    398:
                    399: func (s *Suite) Test_MkLineChecker_checkVarassignLeftUserSettable__after_prefs(c *check.C) {
                    400:        t := s.Init(c)
                    401:
                    402:        t.SetUpCommandLine("-Wall", "--explain")
                    403:        t.SetUpPackage("category/package",
                    404:                ".include \"../../mk/bsd.prefs.mk\"",
                    405:                "AFTER=\tvalue")
                    406:        t.CreateFileLines("mk/defaults/mk.conf",
1.38      rillig    407:                MkCvsID,
1.37      rillig    408:                "AFTER?=\t\tvalue")
                    409:        t.Chdir("category/package")
                    410:        t.FinishSetUp()
                    411:
                    412:        G.Check(".")
                    413:
                    414:        t.CheckOutputLines(
                    415:                "NOTE: Makefile:21: Redundant definition for AFTER from mk/defaults/mk.conf.")
                    416: }
                    417:
                    418: func (s *Suite) Test_MkLineChecker_checkVarassignLeftUserSettable__vartype_nil(c *check.C) {
                    419:        t := s.Init(c)
                    420:
                    421:        t.CreateFileLines("category/package/vars.mk",
1.38      rillig    422:                MkCvsID,
1.37      rillig    423:                "#",
                    424:                "# User-settable variables:",
                    425:                "#",
                    426:                "# USER_SETTABLE",
                    427:                "#\tDocumentation for USER_SETTABLE.",
                    428:                "",
                    429:                ".include \"../../mk/bsd.prefs.mk\"",
                    430:                "",
                    431:                "USER_SETTABLE?=\tdefault")
                    432:        t.SetUpPackage("category/package",
                    433:                "USER_SETTABLE=\tvalue")
                    434:        t.Chdir("category/package")
                    435:        t.FinishSetUp()
                    436:
                    437:        G.Check(".")
                    438:
                    439:        // TODO: As of June 2019, pkglint doesn't parse the "User-settable variables"
                    440:        //  comment. Therefore it doesn't know that USER_SETTABLE is intended to be
                    441:        //  used by other packages. There should be no warning.
                    442:        t.CheckOutputLines(
                    443:                "WARN: Makefile:20: USER_SETTABLE is defined but not used.")
                    444: }
                    445:
1.48      rillig    446: func (s *Suite) Test_MkLineChecker_checkVarassignLeftPermissions__hacks_mk(c *check.C) {
1.37      rillig    447:        t := s.Init(c)
                    448:
1.48      rillig    449:        t.SetUpVartypes()
                    450:
                    451:        mklines := t.NewMkLines("hacks.mk",
1.38      rillig    452:                MkCvsID,
1.48      rillig    453:                "OPSYS=\t${PKGREVISION}")
1.37      rillig    454:
                    455:        mklines.Check()
                    456:
1.48      rillig    457:        // No matter how strange the definition or use of a variable sounds,
                    458:        // in hacks.mk it is allowed. Special problems sometimes need solutions
                    459:        // that violate all standards.
                    460:        t.CheckOutputEmpty()
1.37      rillig    461: }
                    462:
1.48      rillig    463: func (s *Suite) Test_MkLineChecker_checkVarassignLeftPermissions(c *check.C) {
1.15      rillig    464:        t := s.Init(c)
                    465:
1.23      rillig    466:        t.SetUpVartypes()
1.48      rillig    467:        t.SetUpTool("awk", "AWK", AtRunTime)
                    468:        G.Pkgsrc.vartypes.DefineParse("SET_ONLY", BtUnknown, NoVartypeOptions,
                    469:                "options.mk: set")
                    470:        G.Pkgsrc.vartypes.DefineParse("SET_ONLY_DEFAULT_ELSEWHERE", BtUnknown, NoVartypeOptions,
                    471:                "options.mk: set",
                    472:                "*.mk: default, set")
                    473:        mklines := t.NewMkLines("options.mk",
1.38      rillig    474:                MkCvsID,
1.48      rillig    475:                "PKG_DEVELOPER?=\tyes",
                    476:                "BUILD_DEFS?=\tVARBASE",
                    477:                "USE_TOOLS:=\t${USE_TOOLS:Nunwanted-tool}",
                    478:                "USE_TOOLS:=\t${MY_TOOLS}",
                    479:                "USE_TOOLS:=\tawk",
                    480:                "",
                    481:                "SET_ONLY=\tset",
                    482:                "SET_ONLY:=\teval",
                    483:                "SET_ONLY?=\tdefault",
                    484:                "",
                    485:                "SET_ONLY_DEFAULT_ELSEWHERE=\tset",
                    486:                "SET_ONLY_DEFAULT_ELSEWHERE:=\teval",
                    487:                "SET_ONLY_DEFAULT_ELSEWHERE?=\tdefault")
1.15      rillig    488:
1.28      rillig    489:        mklines.Check()
1.15      rillig    490:
                    491:        t.CheckOutputLines(
1.48      rillig    492:                "WARN: options.mk:2: Please include \"../../mk/bsd.prefs.mk\" before using \"?=\".",
                    493:                "WARN: options.mk:2: The variable PKG_DEVELOPER should not be given a default value by any package.",
                    494:                "WARN: options.mk:3: The variable BUILD_DEFS should not be given a default value (only appended to) in this file.",
                    495:                "WARN: options.mk:4: USE_TOOLS should not be used at load time in this file; "+
                    496:                        "it would be ok in Makefile.common or builtin.mk, but not buildlink3.mk or *.",
                    497:                "WARN: options.mk:5: MY_TOOLS is used but not defined.",
                    498:                "WARN: options.mk:10: "+
                    499:                        "The variable SET_ONLY should not be given a default value "+
                    500:                        "(only set) in this file.",
                    501:                "WARN: options.mk:14: "+
                    502:                        "The variable SET_ONLY_DEFAULT_ELSEWHERE should not be given a "+
                    503:                        "default value (only set) in this file; it would be ok in *.mk, "+
                    504:                        "but not options.mk.")
1.15      rillig    505: }
                    506:
1.48      rillig    507: func (s *Suite) Test_MkLineChecker_checkVarassignLeftPermissions__no_tracing(c *check.C) {
1.15      rillig    508:        t := s.Init(c)
                    509:
1.23      rillig    510:        t.SetUpVartypes()
1.48      rillig    511:        t.DisableTracing() // Just to reach branch coverage for unknown permissions.
                    512:        mklines := t.NewMkLines("options.mk",
1.38      rillig    513:                MkCvsID,
1.48      rillig    514:                "COMMENT=\tShort package description")
1.15      rillig    515:
1.25      rillig    516:        mklines.Check()
1.15      rillig    517: }
                    518:
1.48      rillig    519: // Setting a default license is typical for big software projects
                    520: // like GNOME or KDE that consist of many packages, or for programming
                    521: // languages like Perl or Python that suggest certain licenses.
                    522: //
                    523: // The default license is typically set in a Makefile.common or module.mk.
                    524: func (s *Suite) Test_MkLineChecker_checkVarassignLeftPermissions__license_default(c *check.C) {
1.15      rillig    525:        t := s.Init(c)
                    526:
1.48      rillig    527:        t.SetUpPkgsrc()
                    528:        mklines := t.NewMkLines("filename.mk",
1.38      rillig    529:                MkCvsID,
1.48      rillig    530:                "LICENSE?=\tgnu-gpl-v2")
                    531:        t.FinishSetUp()
1.15      rillig    532:
                    533:        mklines.Check()
                    534:
1.48      rillig    535:        // LICENSE is a package-settable variable. Therefore bsd.prefs.mk
                    536:        // does not need to be included before setting a default for this
                    537:        // variable. Including bsd.prefs.mk is only necessary when setting a
                    538:        // default value for user-settable or system-defined variables.
                    539:        t.CheckOutputEmpty()
1.15      rillig    540: }
                    541:
1.48      rillig    542: // Don't check the permissions for infrastructure files since they have their own rules.
                    543: func (s *Suite) Test_MkLineChecker_checkVarassignLeftPermissions__infrastructure(c *check.C) {
1.18      rillig    544:        t := s.Init(c)
                    545:
1.48      rillig    546:        t.SetUpVartypes()
                    547:        t.CreateFileLines("mk/infra.mk",
1.38      rillig    548:                MkCvsID,
1.48      rillig    549:                "",
                    550:                "PKG_DEVELOPER?=\tyes")
                    551:        t.CreateFileLines("mk/bsd.pkg.mk")
1.18      rillig    552:
1.48      rillig    553:        G.Check(t.File("mk/infra.mk"))
1.18      rillig    554:
1.48      rillig    555:        t.CheckOutputEmpty()
1.18      rillig    556: }
                    557:
1.48      rillig    558: func (s *Suite) Test_MkLineChecker_explainPermissions(c *check.C) {
1.20      rillig    559:        t := s.Init(c)
                    560:
1.48      rillig    561:        t.SetUpCommandLine("-Wall", "--explain")
                    562:        t.SetUpVartypes()
1.20      rillig    563:
1.48      rillig    564:        mklines := t.NewMkLines("buildlink3.mk",
1.38      rillig    565:                MkCvsID,
1.48      rillig    566:                "AUTO_MKDIRS=\tyes")
1.31      rillig    567:
1.48      rillig    568:        mklines.Check()
1.31      rillig    569:
                    570:        t.CheckOutputLines(
1.48      rillig    571:                "WARN: buildlink3.mk:2: The variable AUTO_MKDIRS should not be set in this file; "+
                    572:                        "it would be ok in Makefile, Makefile.* or *.mk, "+
                    573:                        "but not buildlink3.mk or builtin.mk.",
                    574:                "",
                    575:                "\tThe allowed actions for a variable are determined based on the file",
                    576:                "\tname in which the variable is used or defined. The rules for",
                    577:                "\tAUTO_MKDIRS are:",
                    578:                "",
                    579:                "\t* in buildlink3.mk, it should not be accessed at all",
                    580:                "\t* in builtin.mk, it should not be accessed at all",
                    581:                "\t* in Makefile, it may be set, given a default value, or used",
                    582:                "\t* in Makefile.*, it may be set, given a default value, or used",
                    583:                "\t* in *.mk, it may be set, given a default value, or used",
                    584:                // TODO: Add a check for infrastructure permissions
                    585:                //  when the "infra:" prefix is added.
                    586:                "",
                    587:                "\tIf these rules seem to be incorrect, please ask on the",
                    588:                "\ttech-pkg@NetBSD.org mailing list.",
                    589:                "")
1.31      rillig    590: }
                    591:
1.48      rillig    592: func (s *Suite) Test_MkLineChecker_checkVarassignLeftRationale(c *check.C) {
1.44      rillig    593:        t := s.Init(c)
                    594:
1.48      rillig    595:        t.SetUpVartypes()
1.44      rillig    596:
1.48      rillig    597:        testLines := func(lines []string, diagnostics ...string) {
                    598:                mklines := t.NewMkLines("filename.mk",
                    599:                        lines...)
1.44      rillig    600:
1.48      rillig    601:                mklines.Check()
1.44      rillig    602:
1.48      rillig    603:                t.CheckOutput(diagnostics)
                    604:        }
                    605:        test := func(lines []string, diagnostics ...string) {
                    606:                testLines(append([]string{MkCvsID, ""}, lines...), diagnostics...)
                    607:        }
                    608:        lines := func(lines ...string) []string { return lines }
1.45      rillig    609:
1.48      rillig    610:        test(
                    611:                lines(
                    612:                        MkCvsID,
                    613:                        "ONLY_FOR_PLATFORM=\t*-*-*", // The CVS Id above is not a rationale.
                    614:                        "NOT_FOR_PLATFORM=\t*-*-*",  // Neither does this line have a rationale.
                    615:                ),
                    616:                "WARN: filename.mk:4: Setting variable ONLY_FOR_PLATFORM should have a rationale.",
                    617:                "WARN: filename.mk:5: Setting variable NOT_FOR_PLATFORM should have a rationale.")
1.45      rillig    618:
1.48      rillig    619:        test(
                    620:                lines(
                    621:                        "ONLY_FOR_PLATFORM+=\t*-*-* # rationale in the same line"),
                    622:                nil...)
1.45      rillig    623:
1.48      rillig    624:        test(
                    625:                lines(
                    626:                        "",
                    627:                        "# rationale in the line above",
                    628:                        "ONLY_FOR_PLATFORM+=\t*-*-*"),
                    629:                nil...)
1.45      rillig    630:
1.48      rillig    631:        // A commented variable assignment does not count as a rationale,
                    632:        // since it is not in plain text.
                    633:        test(
                    634:                lines(
                    635:                        "#VAR=\tvalue",
                    636:                        "ONLY_FOR_PLATFORM+=\t*-*-*"),
                    637:                "WARN: filename.mk:4: Setting variable ONLY_FOR_PLATFORM should have a rationale.")
1.31      rillig    638:
1.48      rillig    639:        // Another variable assignment with comment does not count as a rationale.
                    640:        test(
                    641:                lines(
                    642:                        "PKGNAME=\t\tpackage-1.0 # this is not a rationale",
                    643:                        "ONLY_FOR_PLATFORM+=\t*-*-*"),
                    644:                "WARN: filename.mk:4: Setting variable ONLY_FOR_PLATFORM should have a rationale.")
1.31      rillig    645:
1.48      rillig    646:        // A rationale applies to all variable assignments directly below it.
                    647:        test(
                    648:                lines(
                    649:                        "# rationale",
                    650:                        "BROKEN_ON_PLATFORM+=\t*-*-*",
                    651:                        "BROKEN_ON_PLATFORM+=\t*-*-*"), // The rationale applies to this line, too.
                    652:                nil...)
1.31      rillig    653:
1.48      rillig    654:        // Just for code coverage.
                    655:        test(
                    656:                lines(
                    657:                        "PKGNAME=\tpackage-1.0", // Does not need a rationale.
                    658:                        "UNKNOWN=\t${UNKNOWN}"), // Unknown type, does not need a rationale.
                    659:                nil...)
1.31      rillig    660:
1.48      rillig    661:        // When a line requiring a rationale appears in the very first line
                    662:        // or in the second line of a file, there is no index out of bounds error.
                    663:        testLines(
                    664:                lines(
                    665:                        "NOT_FOR_PLATFORM=\t*-*-*",
                    666:                        "NOT_FOR_PLATFORM=\t*-*-*"),
                    667:                sprintf("ERROR: filename.mk:1: Expected %q.", MkCvsID),
                    668:                "WARN: filename.mk:1: Setting variable NOT_FOR_PLATFORM should have a rationale.",
                    669:                "WARN: filename.mk:2: Setting variable NOT_FOR_PLATFORM should have a rationale.")
                    670:
                    671:        // The whole rationale check is only enabled when -Wextra is given.
                    672:        t.SetUpCommandLine()
                    673:
                    674:        test(
                    675:                lines(
                    676:                        MkCvsID,
                    677:                        "ONLY_FOR_PLATFORM=\t*-*-*", // The CVS Id above is not a rationale.
                    678:                        "NOT_FOR_PLATFORM=\t*-*-*",  // Neither does this line have a rationale.
                    679:                ),
                    680:                nil...)
1.31      rillig    681: }
                    682:
1.48      rillig    683: // The ${VARNAME:=suffix} expression should only be used with lists.
                    684: // It typically appears in MASTER_SITE definitions.
                    685: func (s *Suite) Test_MkLineChecker_CheckVaruse__eq_nonlist(c *check.C) {
1.15      rillig    686:        t := s.Init(c)
                    687:
1.23      rillig    688:        t.SetUpVartypes()
1.48      rillig    689:        t.SetUpMasterSite("MASTER_SITE_GITHUB", "https://github.com/")
                    690:        mklines := t.SetUpFileMkLines("options.mk",
1.38      rillig    691:                MkCvsID,
1.48      rillig    692:                "WRKSRC=\t\t${WRKDIR:=/subdir}",
                    693:                "MASTER_SITES=\t${MASTER_SITE_GITHUB:=organization/}")
1.20      rillig    694:
                    695:        mklines.Check()
                    696:
                    697:        t.CheckOutputLines(
1.48      rillig    698:                "WARN: ~/options.mk:2: The :from=to modifier should only be used with lists, not with WRKDIR.")
1.20      rillig    699: }
                    700:
1.48      rillig    701: func (s *Suite) Test_MkLineChecker_CheckVaruse__for(c *check.C) {
1.20      rillig    702:        t := s.Init(c)
                    703:
1.23      rillig    704:        t.SetUpVartypes()
1.48      rillig    705:        t.SetUpMasterSite("MASTER_SITE_GITHUB", "https://github.com/")
                    706:        mklines := t.SetUpFileMkLines("options.mk",
1.38      rillig    707:                MkCvsID,
1.48      rillig    708:                ".for var in a b c",
                    709:                "\t: ${var}",
1.15      rillig    710:                ".endfor")
                    711:
                    712:        mklines.Check()
                    713:
1.48      rillig    714:        t.CheckOutputEmpty()
1.15      rillig    715: }
                    716:
1.48      rillig    717: // When a parameterized variable is defined in the pkgsrc infrastructure,
                    718: // it does not generate a warning about being "used but not defined".
                    719: // Even if the variable parameter differs, like .Linux and .SunOS in this
                    720: // case. This pattern is typical for pkgsrc, therefore pkglint doesn't
                    721: // check that the variable names match exactly.
                    722: func (s *Suite) Test_MkLineChecker_CheckVaruse__varcanon(c *check.C) {
1.25      rillig    723:        t := s.Init(c)
1.48      rillig    724:        b := NewMkTokenBuilder()
                    725:
                    726:        t.SetUpPkgsrc()
                    727:        t.CreateFileLines("mk/sys-vars.mk",
                    728:                MkCvsID,
                    729:                "CPPPATH.Linux=\t/usr/bin/cpp")
                    730:        t.FinishSetUp()
1.25      rillig    731:
1.48      rillig    732:        mklines := t.NewMkLines("module.mk",
1.38      rillig    733:                MkCvsID,
1.48      rillig    734:                "COMMENT=\t${CPPPATH.SunOS}")
                    735:
                    736:        ck := MkLineChecker{mklines, mklines.mklines[1]}
1.25      rillig    737:
1.48      rillig    738:        ck.CheckVaruse(b.VarUse("CPPPATH.SunOS"), &VarUseContext{
                    739:                vartype: &Vartype{
                    740:                        basicType:  BtPathname,
                    741:                        options:    Guessed,
                    742:                        aclEntries: nil,
                    743:                },
                    744:                time:       VucRunTime,
                    745:                quoting:    VucQuotPlain,
                    746:                IsWordPart: false,
                    747:        })
1.25      rillig    748:
1.48      rillig    749:        t.CheckOutputEmpty()
1.25      rillig    750: }
                    751:
1.48      rillig    752: // Any variable that is defined in the pkgsrc infrastructure in mk/**/*.mk is
                    753: // considered defined, and no "used but not defined" warning is logged for it.
                    754: //
                    755: // See Pkgsrc.loadUntypedVars.
                    756: func (s *Suite) Test_MkLineChecker_CheckVaruse__defined_in_infrastructure(c *check.C) {
1.38      rillig    757:        t := s.Init(c)
                    758:
1.48      rillig    759:        t.SetUpPkgsrc()
                    760:        t.CreateFileLines("mk/deeply/nested/infra.mk",
                    761:                MkCvsID,
                    762:                "INFRA_VAR?=\tvalue")
                    763:        t.FinishSetUp()
                    764:        mklines := t.SetUpFileMkLines("category/package/module.mk",
1.38      rillig    765:                MkCvsID,
1.48      rillig    766:                "do-fetch:",
                    767:                "\t: ${INFRA_VAR} ${UNDEFINED}")
1.38      rillig    768:
                    769:        mklines.Check()
                    770:
                    771:        t.CheckOutputLines(
1.48      rillig    772:                "WARN: ~/category/package/module.mk:3: UNDEFINED is used but not defined.")
1.38      rillig    773: }
                    774:
1.48      rillig    775: func (s *Suite) Test_MkLineChecker_CheckVaruse__build_defs(c *check.C) {
1.25      rillig    776:        t := s.Init(c)
                    777:
1.48      rillig    778:        // XXX: This paragraph should not be necessary since VARBASE and X11_TYPE
                    779:        // are also defined in vardefs.go.
                    780:        t.SetUpPkgsrc()
                    781:        t.CreateFileLines("mk/defaults/mk.conf",
                    782:                "VARBASE?= /usr/pkg/var")
                    783:        t.SetUpCommandLine("-Wall,no-space")
                    784:        t.FinishSetUp()
                    785:
                    786:        mklines := t.SetUpFileMkLines("options.mk",
1.38      rillig    787:                MkCvsID,
1.48      rillig    788:                "COMMENT=                ${VARBASE} ${X11_TYPE}",
                    789:                "PKG_FAIL_REASON+=       ${VARBASE} ${X11_TYPE}",
                    790:                "BUILD_DEFS+=            X11_TYPE")
1.29      rillig    791:
                    792:        mklines.Check()
                    793:
                    794:        t.CheckOutputLines(
1.48      rillig    795:                "WARN: ~/options.mk:2: The user-defined variable VARBASE is used but not added to BUILD_DEFS.",
                    796:                "WARN: ~/options.mk:3: PKG_FAIL_REASON should only get one item per line.")
1.29      rillig    797: }
                    798:
1.48      rillig    799: // The LOCALBASE variable may be defined and used in the infrastructure.
                    800: // It is always equivalent to PREFIX and only exists for historic reasons.
                    801: func (s *Suite) Test_MkLineChecker_CheckVaruse__LOCALBASE_in_infrastructure(c *check.C) {
1.29      rillig    802:        t := s.Init(c)
                    803:
1.25      rillig    804:        t.SetUpPkgsrc()
1.48      rillig    805:        t.CreateFileLines("mk/infra.mk",
1.38      rillig    806:                MkCvsID,
1.48      rillig    807:                "LOCALBASE?=\t${PREFIX}",
                    808:                "DEFAULT_PREFIX=\t${LOCALBASE}")
1.30      rillig    809:        t.FinishSetUp()
1.25      rillig    810:
1.48      rillig    811:        G.Check(t.File("mk/infra.mk"))
                    812:
                    813:        // No warnings about LOCALBASE being used; the infrastructure files may
                    814:        // do this. In packages though, LOCALBASE is deprecated.
1.25      rillig    815:
1.48      rillig    816:        // There is no warning about DEFAULT_PREFIX being "defined but not used"
                    817:        // since Pkgsrc.loadUntypedVars calls Pkgsrc.vartypes.DefineType, which
                    818:        // registers that variable globally.
1.25      rillig    819:        t.CheckOutputEmpty()
                    820: }
                    821:
1.48      rillig    822: func (s *Suite) Test_MkLineChecker_CheckVaruse__user_defined_variable_and_BUILD_DEFS(c *check.C) {
1.15      rillig    823:        t := s.Init(c)
                    824:
1.48      rillig    825:        t.SetUpPkgsrc()
                    826:        t.CreateFileLines("mk/defaults/mk.conf",
                    827:                "VARBASE?=\t${PREFIX}/var",
                    828:                "PYTHON_VER?=\t36")
                    829:        mklines := t.NewMkLines("file.mk",
1.38      rillig    830:                MkCvsID,
1.48      rillig    831:                "BUILD_DEFS+=\tPYTHON_VER",
                    832:                "\t: ${VARBASE}",
                    833:                "\t: ${VARBASE}",
                    834:                "\t: ${PYTHON_VER}")
                    835:        t.FinishSetUp()
1.15      rillig    836:
                    837:        mklines.Check()
                    838:
                    839:        t.CheckOutputLines(
1.48      rillig    840:                "WARN: file.mk:3: The user-defined variable VARBASE is used but not added to BUILD_DEFS.")
1.15      rillig    841: }
                    842:
1.48      rillig    843: func (s *Suite) Test_MkLineChecker_CheckVaruse__deprecated_PKG_DEBUG(c *check.C) {
1.5       rillig    844:        t := s.Init(c)
                    845:
1.23      rillig    846:        t.SetUpVartypes()
1.48      rillig    847:        G.Pkgsrc.initDeprecatedVars()
                    848:
                    849:        mklines := t.NewMkLines("module.mk",
                    850:                MkCvsID,
                    851:                "\t${_PKG_SILENT}${_PKG_DEBUG} :")
1.1       rillig    852:
1.48      rillig    853:        mklines.Check()
                    854:
                    855:        t.CheckOutputLines(
                    856:                "WARN: module.mk:2: Use of _PKG_SILENT and _PKG_DEBUG is deprecated. Use ${RUN} instead.")
                    857: }
                    858:
                    859: func (s *Suite) Test_MkLineChecker_checkVaruseUndefined(c *check.C) {
                    860:        t := s.Init(c)
1.1       rillig    861:
1.48      rillig    862:        t.SetUpPkgsrc()
                    863:        t.CreateFileLines("mk/infra.mk",
                    864:                MkCvsID,
                    865:                "#",
                    866:                "# User-settable variables:",
                    867:                "#",
                    868:                "# DOCUMENTED",
                    869:                "",
                    870:                "ASSIGNED=\tassigned",
                    871:                "#COMMENTED=\tcommented")
                    872:        t.FinishSetUp()
1.1       rillig    873:
1.48      rillig    874:        mklines := t.NewMkLines("filename.mk",
1.38      rillig    875:                MkCvsID,
1.48      rillig    876:                "",
                    877:                "do-build:",
                    878:                "\t: ${ASSIGNED} ${COMMENTED} ${DOCUMENTED} ${UNKNOWN}")
                    879:
1.28      rillig    880:        mklines.Check()
1.1       rillig    881:
1.5       rillig    882:        t.CheckOutputLines(
1.48      rillig    883:                "WARN: filename.mk:4: UNKNOWN is used but not defined.")
1.1       rillig    884: }
                    885:
1.48      rillig    886: // PR 46570, item "15. net/uucp/Makefile has a make loop"
                    887: func (s *Suite) Test_MkLineChecker_checkVaruseUndefined__indirect_variables(c *check.C) {
1.5       rillig    888:        t := s.Init(c)
                    889:
1.48      rillig    890:        t.SetUpTool("echo", "ECHO", AfterPrefsMk)
                    891:        mklines := t.NewMkLines("net/uucp/Makefile",
1.38      rillig    892:                MkCvsID,
1.48      rillig    893:                "\techo ${UUCP_${var}}")
1.1       rillig    894:
1.28      rillig    895:        mklines.Check()
1.5       rillig    896:
1.48      rillig    897:        // No warning about UUCP_${var} being used but not defined.
                    898:        //
                    899:        // Normally, parameterized variables use a dot instead of an underscore as separator.
                    900:        // This is one of the few other cases. Pkglint doesn't warn about dynamic variable
                    901:        // names like UUCP_${var} or SITES_${distfile}.
                    902:        //
                    903:        // It does warn about simple variable names though, like ${var} in this example.
                    904:        t.CheckOutputLines(
                    905:                "WARN: net/uucp/Makefile:2: var is used but not defined.")
1.1       rillig    906: }
                    907:
1.48      rillig    908: // Documented variables are declared as both defined and used since, as
                    909: // of April 2019, pkglint doesn't yet interpret the "Package-settable
                    910: // variables" comment.
                    911: func (s *Suite) Test_MkLineChecker_checkVaruseUndefined__documented(c *check.C) {
1.15      rillig    912:        t := s.Init(c)
                    913:
1.23      rillig    914:        t.SetUpVartypes()
1.48      rillig    915:
                    916:        mklines := t.NewMkLines("interpreter.mk",
1.38      rillig    917:                MkCvsID,
1.48      rillig    918:                "#",
                    919:                "# Package-settable variables:",
                    920:                "#",
                    921:                "# REPLACE_INTERP",
                    922:                "#\tThe list of files whose interpreter will be corrected.",
                    923:                "",
                    924:                "REPLACE_INTERPRETER+=\tinterp",
                    925:                "REPLACE.interp.old=\t.*/interp",
                    926:                "REPLACE.interp.new=\t${PREFIX}/bin/interp",
                    927:                "REPLACE_FILES.interp=\t${REPLACE_INTERP}")
1.15      rillig    928:
1.16      rillig    929:        mklines.Check()
1.15      rillig    930:
1.48      rillig    931:        t.CheckOutputEmpty()
1.15      rillig    932: }
                    933:
1.48      rillig    934: func (s *Suite) Test_MkLineChecker_checkVaruseModifiersSuffix(c *check.C) {
1.37      rillig    935:        t := s.Init(c)
                    936:
                    937:        t.SetUpVartypes()
1.48      rillig    938:        mklines := t.NewMkLines("file.mk",
1.38      rillig    939:                MkCvsID,
1.48      rillig    940:                "\t: ${HOMEPAGE:=subdir/:Q}", // wrong
                    941:                "\t: ${BUILD_DIRS:=subdir/}", // correct
                    942:                "\t: ${BIN_PROGRAMS:=.exe}")  // unknown since BIN_PROGRAMS doesn't have a type
1.37      rillig    943:
                    944:        mklines.Check()
                    945:
                    946:        t.CheckOutputLines(
1.48      rillig    947:                "WARN: file.mk:2: The :from=to modifier should only be used with lists, not with HOMEPAGE.",
                    948:                "WARN: file.mk:4: BIN_PROGRAMS is used but not defined.")
1.37      rillig    949: }
                    950:
1.48      rillig    951: func (s *Suite) Test_MkLineChecker_checkVaruseModifiersRange(c *check.C) {
1.47      rillig    952:        t := s.Init(c)
                    953:
1.48      rillig    954:        t.SetUpCommandLine("--show-autofix", "--source")
1.47      rillig    955:        t.SetUpVartypes()
1.48      rillig    956:        mklines := t.NewMkLines("mk/compiler/gcc.mk",
1.47      rillig    957:                MkCvsID,
1.48      rillig    958:                "CC:=\t${CC:C/^/_asdf_/1:M_asdf_*:S/^_asdf_//}")
1.47      rillig    959:
                    960:        mklines.Check()
                    961:
                    962:        t.CheckOutputLines(
1.48      rillig    963:                "NOTE: mk/compiler/gcc.mk:2: "+
                    964:                        "The modifier \":C/^/_asdf_/1:M_asdf_*:S/^_asdf_//\" can be written as \":[1]\".",
                    965:                "AUTOFIX: mk/compiler/gcc.mk:2: "+
                    966:                        "Replacing \":C/^/_asdf_/1:M_asdf_*:S/^_asdf_//\" with \":[1]\".",
                    967:                "-\tCC:=\t${CC:C/^/_asdf_/1:M_asdf_*:S/^_asdf_//}",
                    968:                "+\tCC:=\t${CC:[1]}")
                    969:
                    970:        // Now go through all the "almost" cases, to reach full branch coverage.
                    971:        mklines = t.NewMkLines("gcc.mk",
                    972:                MkCvsID,
                    973:                "\t: ${CC:M1:M2:M3}",
                    974:                "\t: ${CC:C/^begin//:M2:M3}",                    // M1 pattern not exactly ^
                    975:                "\t: ${CC:C/^/_asdf_/g:M2:M3}",                  // M1 options != "1"
                    976:                "\t: ${CC:C/^/....../g:M2:M3}",                  // M1 replacement doesn't match \w+
                    977:                "\t: ${CC:C/^/_asdf_/1:O:M3}",                   // M2 is not a match modifier
                    978:                "\t: ${CC:C/^/_asdf_/1:N2:M3}",                  // M2 is :N instead of :M
                    979:                "\t: ${CC:C/^/_asdf_/1:M_asdf_:M3}",             // M2 pattern is missing the * at the end
                    980:                "\t: ${CC:C/^/_asdf_/1:Mother:M3}",              // M2 pattern differs from the M1 pattern
                    981:                "\t: ${CC:C/^/_asdf_/1:M_asdf_*:M3}",            // M3 ist not a substitution modifier
                    982:                "\t: ${CC:C/^/_asdf_/1:M_asdf_*:S,from,to,}",    // M3 pattern differs from the M1 pattern
                    983:                "\t: ${CC:C/^/_asdf_/1:M_asdf_*:S,^_asdf_,to,}", // M3 replacement is not empty
                    984:                "\t: ${CC:C/^/_asdf_/1:M_asdf_*:S,^_asdf_,,g}")  // M3 modifier has options
                    985:
                    986:        mklines.Check()
1.47      rillig    987: }
                    988:
1.48      rillig    989: func (s *Suite) Test_MkLineChecker_checkVarusePermissions(c *check.C) {
1.5       rillig    990:        t := s.Init(c)
                    991:
1.23      rillig    992:        t.SetUpVartypes()
1.48      rillig    993:        mklines := t.NewMkLines("options.mk",
1.38      rillig    994:                MkCvsID,
1.48      rillig    995:                "COMMENT=\t${GAMES_USER}",
                    996:                "COMMENT:=\t${PKGBASE}",
                    997:                "PYPKGPREFIX=\t${PKGBASE}")
                    998:        G.Pkgsrc.loadDefaultBuildDefs()
                    999:        G.Pkgsrc.UserDefinedVars.Define("GAMES_USER", mklines.mklines[0])
1.1       rillig   1000:
1.28      rillig   1001:        mklines.Check()
1.1       rillig   1002:
1.48      rillig   1003:        t.CheckOutputLines(
                   1004:                "WARN: options.mk:3: PKGBASE should not be used at load time in any file.",
                   1005:                "WARN: options.mk:4: The variable PYPKGPREFIX should not be set in this file; "+
                   1006:                        "it would be ok in pyversion.mk only.")
1.1       rillig   1007: }
                   1008:
1.48      rillig   1009: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__explain(c *check.C) {
1.35      rillig   1010:        t := s.Init(c)
                   1011:
1.48      rillig   1012:        t.SetUpCommandLine("-Wall", "--explain")
1.35      rillig   1013:        t.SetUpVartypes()
1.48      rillig   1014:        mklines := t.NewMkLines("options.mk",
1.38      rillig   1015:                MkCvsID,
1.48      rillig   1016:                "COMMENT=\t${GAMES_USER}",
                   1017:                "COMMENT:=\t${PKGBASE}",
                   1018:                "PYPKGPREFIX=\t${PKGBASE}")
                   1019:        G.Pkgsrc.loadDefaultBuildDefs()
                   1020:        G.Pkgsrc.UserDefinedVars.Define("GAMES_USER", mklines.mklines[0])
1.35      rillig   1021:
                   1022:        mklines.Check()
                   1023:
                   1024:        t.CheckOutputLines(
1.48      rillig   1025:                "WARN: options.mk:3: PKGBASE should not be used at load time in any file.",
                   1026:                "",
                   1027:                "\tMany variables, especially lists of something, get their values",
                   1028:                "\tincrementally. Therefore it is generally unsafe to rely on their",
                   1029:                "\tvalue until it is clear that it will never change again. This point",
                   1030:                "\tis reached when the whole package Makefile is loaded and execution",
                   1031:                "\tof the shell commands starts; in some cases earlier.",
                   1032:                "",
                   1033:                "\tAdditionally, when using the \":=\" operator, each $$ is replaced with",
                   1034:                "\ta single $, so variables that have references to shell variables or",
                   1035:                "\tregular expressions are modified in a subtle way.",
1.35      rillig   1036:                "",
1.48      rillig   1037:                "\tThe allowed actions for a variable are determined based on the file",
                   1038:                "\tname in which the variable is used or defined. The rules for PKGBASE",
                   1039:                "\tare:",
1.35      rillig   1040:                "",
1.48      rillig   1041:                "\t* in buildlink3.mk, it should not be accessed at all",
                   1042:                "\t* in any file, it may be used",
1.35      rillig   1043:                "",
1.48      rillig   1044:                "\tIf these rules seem to be incorrect, please ask on the",
                   1045:                "\ttech-pkg@NetBSD.org mailing list.",
1.35      rillig   1046:                "",
1.48      rillig   1047:                "WARN: options.mk:4: The variable PYPKGPREFIX should not be set in this file; "+
                   1048:                        "it would be ok in pyversion.mk only.",
1.35      rillig   1049:                "",
1.48      rillig   1050:                "\tThe allowed actions for a variable are determined based on the file",
                   1051:                "\tname in which the variable is used or defined. The rules for",
                   1052:                "\tPYPKGPREFIX are:",
1.35      rillig   1053:                "",
1.48      rillig   1054:                "\t* in pyversion.mk, it may be set",
                   1055:                "\t* in any file, it may be used at load time, or used",
1.35      rillig   1056:                "",
1.48      rillig   1057:                "\tIf these rules seem to be incorrect, please ask on the",
                   1058:                "\ttech-pkg@NetBSD.org mailing list.", "")
1.35      rillig   1059: }
                   1060:
1.48      rillig   1061: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__load_time(c *check.C) {
1.5       rillig   1062:        t := s.Init(c)
                   1063:
1.23      rillig   1064:        t.SetUpVartypes()
1.48      rillig   1065:        mklines := t.NewMkLines("options.mk",
                   1066:                MkCvsID,
                   1067:                "WRKSRC:=${.CURDIR}",
                   1068:                ".if ${PKG_SYSCONFDIR.gdm} != \"etc\"",
                   1069:                ".endif")
1.1       rillig   1070:
1.48      rillig   1071:        mklines.Check()
1.1       rillig   1072:
1.48      rillig   1073:        // Evaluating PKG_SYSCONFDIR.* at load time is probably ok,
                   1074:        // though pkglint cannot prove anything here.
                   1075:        //
                   1076:        // Evaluating .CURDIR at load time is definitely ok since it is defined from the beginning.
                   1077:        t.CheckOutputLines(
                   1078:                "NOTE: options.mk:2: This variable value should be aligned to column 17.")
                   1079: }
1.1       rillig   1080:
1.48      rillig   1081: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__load_time_in_condition(c *check.C) {
                   1082:        t := s.Init(c)
1.1       rillig   1083:
1.48      rillig   1084:        G.Pkgsrc.vartypes.DefineParse("LOAD_TIME", BtPathPattern, List,
                   1085:                "special:filename.mk: use-loadtime")
                   1086:        G.Pkgsrc.vartypes.DefineParse("RUN_TIME", BtPathPattern, List,
                   1087:                "special:filename.mk: use")
1.1       rillig   1088:
1.48      rillig   1089:        mklines := t.NewMkLines("filename.mk",
                   1090:                MkCvsID,
                   1091:                ".if ${LOAD_TIME} && ${RUN_TIME}",
                   1092:                ".endif")
1.1       rillig   1093:
1.48      rillig   1094:        mklines.Check()
1.1       rillig   1095:
1.48      rillig   1096:        t.CheckOutputLines(
                   1097:                "WARN: filename.mk:2: RUN_TIME should not be used at load time in any file.")
                   1098: }
1.1       rillig   1099:
1.48      rillig   1100: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__load_time_in_for_loop(c *check.C) {
                   1101:        t := s.Init(c)
1.5       rillig   1102:
1.48      rillig   1103:        G.Pkgsrc.vartypes.DefineParse("LOAD_TIME", BtPathPattern, List,
                   1104:                "special:filename.mk: use-loadtime")
                   1105:        G.Pkgsrc.vartypes.DefineParse("RUN_TIME", BtPathPattern, List,
                   1106:                "special:filename.mk: use")
1.5       rillig   1107:
1.48      rillig   1108:        mklines := t.NewMkLines("filename.mk",
                   1109:                MkCvsID,
                   1110:                ".for pattern in ${LOAD_TIME} ${RUN_TIME}",
                   1111:                ".endfor")
1.14      rillig   1112:
1.48      rillig   1113:        mklines.Check()
1.39      rillig   1114:
1.48      rillig   1115:        t.CheckOutputLines(
                   1116:                "WARN: filename.mk:2: RUN_TIME should not be used at load time in any file.")
1.39      rillig   1117: }
                   1118:
1.48      rillig   1119: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__load_time_guessed(c *check.C) {
1.39      rillig   1120:        t := s.Init(c)
                   1121:
                   1122:        t.SetUpVartypes()
1.48      rillig   1123:        t.SetUpTool("install", "", AtRunTime)
                   1124:        mklines := t.NewMkLines("install-docfiles.mk",
                   1125:                MkCvsID,
                   1126:                "DOCFILES=\ta b c",
                   1127:                "do-install:",
                   1128:                ".for f in ${DOCFILES}",
                   1129:                "\tinstall -c ${WRKSRC}/${f} ${DESTDIR}${PREFIX}/${f}",
                   1130:                ".endfor")
1.39      rillig   1131:
1.48      rillig   1132:        mklines.Check()
1.39      rillig   1133:
1.48      rillig   1134:        // No warning for using DOCFILES at compile-time. Since the variable
                   1135:        // name is not one of the predefined names from vardefs.go, the
                   1136:        // variable's type is guessed based on the name (see
                   1137:        // Pkgsrc.VariableType).
1.39      rillig   1138:        //
1.48      rillig   1139:        // These guessed variables are typically defined and used only in
                   1140:        // a single file, and in this context, mistakes are usually found
                   1141:        // quickly.
                   1142:        t.CheckOutputEmpty()
1.39      rillig   1143: }
                   1144:
1.48      rillig   1145: // Ensures that the warning "should not be evaluated at load time" is issued
                   1146: // only if using the variable at run time is allowed. If the latter were not
                   1147: // allowed, this warning would be confusing.
                   1148: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__load_time_run_time(c *check.C) {
1.39      rillig   1149:        t := s.Init(c)
1.23      rillig   1150:
1.48      rillig   1151:        G.Pkgsrc.vartypes.DefineParse("LOAD_TIME", BtUnknown, NoVartypeOptions,
                   1152:                "*.mk: use, use-loadtime")
                   1153:        G.Pkgsrc.vartypes.DefineParse("RUN_TIME", BtUnknown, NoVartypeOptions,
                   1154:                "*.mk: use")
                   1155:        G.Pkgsrc.vartypes.DefineParse("WRITE_ONLY", BtUnknown, NoVartypeOptions,
                   1156:                "*.mk: set")
                   1157:        G.Pkgsrc.vartypes.DefineParse("LOAD_TIME_ELSEWHERE", BtUnknown, NoVartypeOptions,
                   1158:                "Makefile: use-loadtime",
                   1159:                "*.mk: set")
                   1160:        G.Pkgsrc.vartypes.DefineParse("RUN_TIME_ELSEWHERE", BtUnknown, NoVartypeOptions,
                   1161:                "Makefile: use",
                   1162:                "*.mk: set")
                   1163:
1.39      rillig   1164:        mklines := t.NewMkLines("filename.mk",
1.48      rillig   1165:                MkCvsID,
                   1166:                ".if ${LOAD_TIME} && ${RUN_TIME} && ${WRITE_ONLY}",
                   1167:                ".elif ${LOAD_TIME_ELSEWHERE} && ${RUN_TIME_ELSEWHERE}",
                   1168:                ".endif")
1.39      rillig   1169:
1.48      rillig   1170:        mklines.Check()
1.39      rillig   1171:
1.48      rillig   1172:        t.CheckOutputLines(
                   1173:                "WARN: filename.mk:2: RUN_TIME should not be used at load time in any file.",
                   1174:                "WARN: filename.mk:2: "+
                   1175:                        "WRITE_ONLY should not be used in any file; "+
                   1176:                        "it is a write-only variable.",
                   1177:                "WARN: filename.mk:3: "+
                   1178:                        "LOAD_TIME_ELSEWHERE should not be used at load time in this file; "+
                   1179:                        "it would be ok in Makefile, but not *.mk.",
                   1180:                "WARN: filename.mk:3: "+
                   1181:                        "RUN_TIME_ELSEWHERE should not be used at load time in any file.")
1.1       rillig   1182: }
                   1183:
1.48      rillig   1184: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__PKGREVISION(c *check.C) {
1.5       rillig   1185:        t := s.Init(c)
                   1186:
1.23      rillig   1187:        t.SetUpVartypes()
1.48      rillig   1188:        mklines := t.NewMkLines("any.mk",
1.38      rillig   1189:                MkCvsID,
1.48      rillig   1190:                ".if defined(PKGREVISION)",
                   1191:                ".endif")
1.1       rillig   1192:
1.28      rillig   1193:        mklines.Check()
1.1       rillig   1194:
1.48      rillig   1195:        // Since PKGREVISION may only be set in the package Makefile directly,
                   1196:        // there is no other file that could be mentioned as "it would be ok in".
1.5       rillig   1197:        t.CheckOutputLines(
1.48      rillig   1198:                "WARN: any.mk:2: PKGREVISION should not be used in any file; it is a write-only variable.")
1.1       rillig   1199: }
                   1200:
1.48      rillig   1201: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__indirectly(c *check.C) {
1.5       rillig   1202:        t := s.Init(c)
                   1203:
1.23      rillig   1204:        t.SetUpVartypes()
1.48      rillig   1205:        mklines := t.NewMkLines("file.mk",
1.38      rillig   1206:                MkCvsID,
1.48      rillig   1207:                "IGNORE_PKG.package=\t${ONLY_FOR_UNPRIVILEGED}")
1.1       rillig   1208:
1.15      rillig   1209:        mklines.Check()
1.1       rillig   1210:
1.5       rillig   1211:        t.CheckOutputLines(
1.48      rillig   1212:                "WARN: file.mk:2: IGNORE_PKG.package should be set to YES or yes.",
                   1213:                "WARN: file.mk:2: ONLY_FOR_UNPRIVILEGED should not be used indirectly at load time (via IGNORE_PKG.package).")
1.25      rillig   1214: }
                   1215:
1.48      rillig   1216: // This test is only here for branch coverage.
                   1217: // It does not demonstrate anything useful.
                   1218: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__indirectly_tool(c *check.C) {
1.25      rillig   1219:        t := s.Init(c)
                   1220:
                   1221:        t.SetUpVartypes()
1.48      rillig   1222:        mklines := t.NewMkLines("file.mk",
1.38      rillig   1223:                MkCvsID,
1.48      rillig   1224:                "USE_TOOLS+=\t${PKGREVISION}")
1.25      rillig   1225:
                   1226:        mklines.Check()
1.48      rillig   1227:
                   1228:        t.CheckOutputLines(
                   1229:                "WARN: file.mk:2: PKGREVISION should not be used in any file; it is a write-only variable.")
1.1       rillig   1230: }
                   1231:
1.48      rillig   1232: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__write_only_usable_in_other_file(c *check.C) {
1.27      rillig   1233:        t := s.Init(c)
                   1234:
1.48      rillig   1235:        t.SetUpVartypes()
                   1236:        mklines := t.NewMkLines("buildlink3.mk",
1.38      rillig   1237:                MkCvsID,
1.48      rillig   1238:                "VAR=\t${VAR} ${AUTO_MKDIRS}")
1.27      rillig   1239:
1.28      rillig   1240:        mklines.Check()
1.27      rillig   1241:
1.48      rillig   1242:        t.CheckOutputLines(
                   1243:                "WARN: buildlink3.mk:2: " +
                   1244:                        "AUTO_MKDIRS should not be used in this file; " +
                   1245:                        "it would be ok in Makefile, Makefile.* or *.mk, " +
                   1246:                        "but not buildlink3.mk or builtin.mk.")
1.27      rillig   1247: }
                   1248:
1.48      rillig   1249: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__usable_only_at_loadtime_in_other_file(c *check.C) {
1.12      rillig   1250:        t := s.Init(c)
                   1251:
1.48      rillig   1252:        G.Pkgsrc.vartypes.DefineParse("VAR", BtFilename, NoVartypeOptions,
                   1253:                "*: set, use-loadtime")
                   1254:        mklines := t.NewMkLines("Makefile",
1.38      rillig   1255:                MkCvsID,
1.48      rillig   1256:                "VAR=\t${VAR}")
1.12      rillig   1257:
1.48      rillig   1258:        mklines.Check()
1.12      rillig   1259:
1.48      rillig   1260:        // Since the variable is usable at load time, pkglint assumes it is also
                   1261:        // usable at run time. This is not the case for VAR, but probably doesn't
                   1262:        // happen in practice anyway.
1.12      rillig   1263:        t.CheckOutputEmpty()
                   1264: }
                   1265:
1.48      rillig   1266: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__assigned_to_infrastructure_variable(c *check.C) {
1.33      rillig   1267:        t := s.Init(c)
                   1268:
1.48      rillig   1269:        // This combination of BtUnknown and all permissions is typical for
                   1270:        // otherwise unknown variables from the pkgsrc infrastructure.
                   1271:        G.Pkgsrc.vartypes.Define("INFRA", BtUnknown, NoVartypeOptions,
                   1272:                NewACLEntry("*", aclpAll))
                   1273:        G.Pkgsrc.vartypes.DefineParse("VAR", BtUnknown, NoVartypeOptions,
                   1274:                "buildlink3.mk: none",
                   1275:                "*: use")
                   1276:        mklines := t.NewMkLines("buildlink3.mk",
1.38      rillig   1277:                MkCvsID,
1.48      rillig   1278:                "INFRA=\t${VAR}")
1.29      rillig   1279:
1.48      rillig   1280:        mklines.Check()
1.29      rillig   1281:
1.48      rillig   1282:        // Since INFRA is defined in the infrastructure and pkglint
                   1283:        // knows nothing else about this variable, it assumes that INFRA
                   1284:        // may be used at load time. This is done to prevent wrong warnings.
                   1285:        //
                   1286:        // This in turn has consequences when INFRA is used on the left-hand
                   1287:        // side of an assignment since pkglint assumes that the right-hand
                   1288:        // side may now be evaluated at load time.
                   1289:        //
                   1290:        // Therefore the check is skipped when such a variable appears at the
                   1291:        // left-hand side of an assignment.
1.29      rillig   1292:        //
1.48      rillig   1293:        // Even in this case involving an unknown infrastructure variable,
                   1294:        // it is possible to issue a warning since VAR should not be used at all,
                   1295:        // independent of any properties of INFRA.
                   1296:        t.CheckOutputEmpty()
1.29      rillig   1297: }
                   1298:
1.48      rillig   1299: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__assigned_to_load_time(c *check.C) {
1.19      rillig   1300:        t := s.Init(c)
                   1301:
1.48      rillig   1302:        // LOAD_TIME may be used at load time in other.mk.
                   1303:        // Since VAR must not be used at load time at all, it would be dangerous
                   1304:        // to use its value in LOAD_TIME, as the latter might be evaluated later
                   1305:        // at load time, and at that point VAR would be evaluated as well.
1.19      rillig   1306:
1.48      rillig   1307:        G.Pkgsrc.vartypes.DefineParse("LOAD_TIME", BtMessage, NoVartypeOptions,
                   1308:                "buildlink3.mk: set",
                   1309:                "*.mk: use-loadtime")
                   1310:        G.Pkgsrc.vartypes.DefineParse("VAR", BtUnknown, NoVartypeOptions,
                   1311:                "buildlink3.mk: none",
                   1312:                "*.mk: use")
                   1313:        mklines := t.NewMkLines("buildlink3.mk",
1.38      rillig   1314:                MkCvsID,
1.48      rillig   1315:                "LOAD_TIME=\t${VAR}")
1.19      rillig   1316:
1.28      rillig   1317:        mklines.Check()
1.19      rillig   1318:
                   1319:        t.CheckOutputLines(
1.48      rillig   1320:                "WARN: buildlink3.mk:2: VAR should not be used indirectly " +
                   1321:                        "at load time (via LOAD_TIME).")
1.19      rillig   1322: }
                   1323:
1.48      rillig   1324: func (s *Suite) Test_MkLineChecker_checkVarusePermissions__multiple_times_per_file(c *check.C) {
1.5       rillig   1325:        t := s.Init(c)
                   1326:
1.23      rillig   1327:        t.SetUpVartypes()
1.48      rillig   1328:        mklines := t.NewMkLines("buildlink3.mk",
1.38      rillig   1329:                MkCvsID,
1.48      rillig   1330:                "VAR=\t${VAR} ${AUTO_MKDIRS} ${AUTO_MKDIRS} ${PKGREVISION} ${PKGREVISION}",
                   1331:                "VAR=\t${VAR} ${AUTO_MKDIRS} ${AUTO_MKDIRS} ${PKGREVISION} ${PKGREVISION}")
1.1       rillig   1332:
                   1333:        mklines.Check()
                   1334:
1.48      rillig   1335:        // Since these warnings are valid for the whole file, duplicates are suppressed.
1.5       rillig   1336:        t.CheckOutputLines(
1.48      rillig   1337:                "WARN: buildlink3.mk:2: "+
                   1338:                        "AUTO_MKDIRS should not be used in this file; "+
                   1339:                        "it would be ok in Makefile, Makefile.* or *.mk, "+
                   1340:                        "but not buildlink3.mk or builtin.mk.",
                   1341:                "WARN: buildlink3.mk:2: "+
                   1342:                        "PKGREVISION should not be used in any file; "+
                   1343:                        "it is a write-only variable.")
1.24      rillig   1344: }
                   1345:
1.48      rillig   1346: func (s *Suite) Test_MkLineChecker_warnVarusePermissions__not_directly_and_no_alternative_files(c *check.C) {
1.24      rillig   1347:        t := s.Init(c)
                   1348:
                   1349:        t.SetUpVartypes()
1.48      rillig   1350:        mklines := t.NewMkLines("mk-c.mk",
1.38      rillig   1351:                MkCvsID,
1.48      rillig   1352:                "",
                   1353:                "# GUESSED_FLAGS",
                   1354:                "#\tDocumented here to suppress the \"defined but not used\"",
                   1355:                "#\twarning.",
                   1356:                "",
                   1357:                "TOOL_DEPENDS+=\t${BUILDLINK_API_DEPENDS.mk-c}:${BUILDLINK_PKGSRCDIR.mk-c}",
                   1358:                "GUESSED_FLAGS+=\t${BUILDLINK_CPPFLAGS}")
1.24      rillig   1359:
                   1360:        mklines.Check()
                   1361:
1.48      rillig   1362:        toolDependsType := G.Pkgsrc.VariableType(nil, "TOOL_DEPENDS")
                   1363:        t.CheckEquals(toolDependsType.String(), "DependencyWithPath (list, package-settable)")
                   1364:        t.CheckEquals(toolDependsType.AlternativeFiles(aclpAppend), "Makefile, Makefile.* or *.mk")
                   1365:        t.CheckEquals(toolDependsType.AlternativeFiles(aclpUse), "Makefile, Makefile.* or *.mk")
                   1366:        t.CheckEquals(toolDependsType.AlternativeFiles(aclpUseLoadtime), "")
                   1367:
                   1368:        apiDependsType := G.Pkgsrc.VariableType(nil, "BUILDLINK_API_DEPENDS.*")
                   1369:        t.CheckEquals(apiDependsType.String(), "Dependency (list, package-settable)")
                   1370:        t.CheckEquals(apiDependsType.AlternativeFiles(aclpUse), "")
                   1371:        t.CheckEquals(apiDependsType.AlternativeFiles(aclpUseLoadtime), "buildlink3.mk or builtin.mk only")
                   1372:
1.24      rillig   1373:        t.CheckOutputLines(
1.48      rillig   1374:                "WARN: mk-c.mk:7: BUILDLINK_API_DEPENDS.mk-c should not be used in any file.",
                   1375:                "WARN: mk-c.mk:7: The list variable BUILDLINK_API_DEPENDS.mk-c should not be embedded in a word.",
                   1376:                "WARN: mk-c.mk:7: BUILDLINK_PKGSRCDIR.mk-c should not be used in any file.")
1.24      rillig   1377: }
                   1378:
1.48      rillig   1379: func (s *Suite) Test_MkLineChecker_warnVaruseToolLoadTime(c *check.C) {
1.24      rillig   1380:        t := s.Init(c)
                   1381:
                   1382:        t.SetUpVartypes()
1.48      rillig   1383:        t.SetUpTool("nowhere", "NOWHERE", Nowhere)
                   1384:        t.SetUpTool("after-prefs", "AFTER_PREFS", AfterPrefsMk)
                   1385:        t.SetUpTool("at-runtime", "AT_RUNTIME", AtRunTime)
                   1386:        mklines := t.NewMkLines("Makefile",
1.38      rillig   1387:                MkCvsID,
1.48      rillig   1388:                ".if ${NOWHERE} && ${AFTER_PREFS} && ${AT_RUNTIME} && ${MK_TOOL}",
                   1389:                ".endif",
                   1390:                "",
                   1391:                "TOOLS_CREATE+=\t\tmk-tool",
                   1392:                "_TOOLS_VARNAME.mk-tool=\tMK_TOOL")
1.24      rillig   1393:
1.28      rillig   1394:        mklines.Check()
1.24      rillig   1395:
                   1396:        t.CheckOutputLines(
1.48      rillig   1397:                "WARN: Makefile:2: To use the tool ${NOWHERE} at load time, "+
                   1398:                        "it has to be added to USE_TOOLS before including bsd.prefs.mk.",
                   1399:                "WARN: Makefile:2: To use the tool ${AFTER_PREFS} at load time, "+
                   1400:                        "bsd.prefs.mk has to be included before.",
                   1401:                "WARN: Makefile:2: The tool ${AT_RUNTIME} cannot be used at load time.",
                   1402:                "WARN: Makefile:2: To use the tool ${MK_TOOL} at load time, "+
                   1403:                        "bsd.prefs.mk has to be included before.",
                   1404:                "WARN: Makefile:6: Variable names starting with an underscore "+
                   1405:                        "(_TOOLS_VARNAME.mk-tool) are reserved for internal pkgsrc use.",
                   1406:                "WARN: Makefile:6: _TOOLS_VARNAME.mk-tool is defined but not used.")
1.1       rillig   1407: }
                   1408:
1.48      rillig   1409: // This somewhat unrealistic case demonstrates how there can be a tool in a
                   1410: // Makefile that is not known to the global pkgsrc.
                   1411: //
                   1412: // This test covers the "pkgsrcTool != nil" condition.
                   1413: func (s *Suite) Test_MkLineChecker_warnVaruseToolLoadTime__local_tool(c *check.C) {
1.5       rillig   1414:        t := s.Init(c)
                   1415:
1.23      rillig   1416:        t.SetUpVartypes()
1.48      rillig   1417:        t.CreateFileLines("mk/bsd.prefs.mk")
                   1418:        mklines := t.SetUpFileMkLines("category/package/Makefile",
1.38      rillig   1419:                MkCvsID,
1.48      rillig   1420:                ".include \"../../mk/bsd.prefs.mk\"",
                   1421:                "",
                   1422:                "TOOLS_CREATE+=\t\tmk-tool",
                   1423:                "_TOOLS_VARNAME.mk-tool=\tMK_TOOL",
                   1424:                "",
                   1425:                "TOOL_OUTPUT!=\t${MK_TOOL}")
1.1       rillig   1426:
                   1427:        mklines.Check()
                   1428:
1.5       rillig   1429:        t.CheckOutputLines(
1.48      rillig   1430:                "WARN: ~/category/package/Makefile:5: Variable names starting with an underscore (_TOOLS_VARNAME.mk-tool) are reserved for internal pkgsrc use.",
                   1431:                "WARN: ~/category/package/Makefile:5: _TOOLS_VARNAME.mk-tool is defined but not used.",
                   1432:                "WARN: ~/category/package/Makefile:7: TOOL_OUTPUT is defined but not used.",
                   1433:                "WARN: ~/category/package/Makefile:7: The tool ${MK_TOOL} cannot be used at load time.")
1.1       rillig   1434: }
                   1435:
1.48      rillig   1436: func (s *Suite) Test_MkLineChecker_checkVarUseQuoting(c *check.C) {
1.28      rillig   1437:        t := s.Init(c)
                   1438:
1.48      rillig   1439:        t.SetUpVartypes()
                   1440:        mklines := t.SetUpFileMkLines("options.mk",
1.38      rillig   1441:                MkCvsID,
1.48      rillig   1442:                "GOPATH=\t${WRKDIR}",
                   1443:                "",
                   1444:                "CONFIGURE_ENV+=\tNAME=${R_PKGNAME} VER=${R_PKGVER}",
                   1445:                "",
                   1446:                "do-build:",
                   1447:                "\tcd ${WRKSRC} && GOPATH=${GOPATH} PATH=${PATH} :")
1.28      rillig   1448:
                   1449:        mklines.Check()
                   1450:
1.48      rillig   1451:        // For WRKSRC and GOPATH, no quoting is necessary since pkgsrc directories by
                   1452:        // definition don't contain special characters. Therefore they don't need the
                   1453:        // :Q, not even when used as part of a shell word.
                   1454:
                   1455:        // For PATH, the quoting is necessary because it may contain directories outside
                   1456:        // of pkgsrc, and these may contain special characters.
                   1457:
1.28      rillig   1458:        t.CheckOutputLines(
1.48      rillig   1459:                "WARN: ~/options.mk:7: The variable PATH should be quoted as part of a shell word.")
1.28      rillig   1460: }
                   1461:
1.48      rillig   1462: func (s *Suite) Test_MkLineChecker_checkVarUseQuoting__mstar(c *check.C) {
1.28      rillig   1463:        t := s.Init(c)
                   1464:
1.48      rillig   1465:        t.SetUpCommandLine("-Wall,no-space")
                   1466:        t.SetUpVartypes()
                   1467:        mklines := t.SetUpFileMkLines("options.mk",
1.38      rillig   1468:                MkCvsID,
1.48      rillig   1469:                "CONFIGURE_ARGS+=        CFLAGS=${CFLAGS:Q}",
                   1470:                "CONFIGURE_ARGS+=        CFLAGS=${CFLAGS:M*:Q}",
                   1471:                "CONFIGURE_ARGS+=        ADA_FLAGS=${ADA_FLAGS:Q}",
                   1472:                "CONFIGURE_ARGS+=        ADA_FLAGS=${ADA_FLAGS:M*:Q}",
                   1473:                "CONFIGURE_ENV+=         CFLAGS=${CFLAGS:Q}",
                   1474:                "CONFIGURE_ENV+=         CFLAGS=${CFLAGS:M*:Q}",
                   1475:                "CONFIGURE_ENV+=         ADA_FLAGS=${ADA_FLAGS:Q}",
                   1476:                "CONFIGURE_ENV+=         ADA_FLAGS=${ADA_FLAGS:M*:Q}")
1.28      rillig   1477:
                   1478:        mklines.Check()
                   1479:
                   1480:        t.CheckOutputLines(
1.48      rillig   1481:                "WARN: ~/options.mk:2: Please use ${CFLAGS:M*:Q} instead of ${CFLAGS:Q}.",
                   1482:                "WARN: ~/options.mk:4: ADA_FLAGS is used but not defined.",
                   1483:                "WARN: ~/options.mk:6: Please use ${CFLAGS:M*:Q} instead of ${CFLAGS:Q}.")
1.28      rillig   1484: }
                   1485:
1.48      rillig   1486: func (s *Suite) Test_MkLineChecker_checkVarUseQuoting__mstar_not_needed(c *check.C) {
1.18      rillig   1487:        t := s.Init(c)
                   1488:
1.48      rillig   1489:        t.SetUpCommandLine("-Wall,no-space")
                   1490:        pkg := t.SetUpPackage("category/package",
                   1491:                "MAKE_FLAGS+=\tCFLAGS=${CFLAGS:M*:Q}",
                   1492:                "MAKE_FLAGS+=\tLFLAGS=${LDFLAGS:M*:Q}")
                   1493:        t.FinishSetUp()
1.18      rillig   1494:
1.48      rillig   1495:        // This package is guaranteed to not use GNU_CONFIGURE.
                   1496:        // Since the :M* hack is only needed for GNU_CONFIGURE, it is not necessary here.
                   1497:        G.Check(pkg)
1.18      rillig   1498:
1.48      rillig   1499:        t.CheckOutputLines(
                   1500:                "NOTE: ~/category/package/Makefile:20: The :M* modifier is not needed here.",
                   1501:                "NOTE: ~/category/package/Makefile:21: The :M* modifier is not needed here.")
1.18      rillig   1502: }
                   1503:
1.48      rillig   1504: func (s *Suite) Test_MkLineChecker_checkVarUseQuoting__q_not_needed(c *check.C) {
1.28      rillig   1505:        t := s.Init(c)
                   1506:
1.48      rillig   1507:        pkg := t.SetUpPackage("category/package",
                   1508:                "MASTER_SITES=\t${HOMEPAGE:Q}")
                   1509:        t.FinishSetUp()
1.28      rillig   1510:
1.48      rillig   1511:        G.Check(pkg)
1.28      rillig   1512:
                   1513:        t.CheckOutputLines(
1.48      rillig   1514:                "NOTE: ~/category/package/Makefile:6: The :Q modifier isn't necessary for ${HOMEPAGE} here.")
1.28      rillig   1515: }
                   1516:
1.48      rillig   1517: func (s *Suite) Test_MkLineChecker_checkVarUseQuoting__undefined_list_in_word_in_shell_command(c *check.C) {
1.18      rillig   1518:        t := s.Init(c)
                   1519:
1.48      rillig   1520:        pkg := t.SetUpPackage("category/package",
                   1521:                "\t${ECHO} ./${DISTFILES}")
                   1522:        t.FinishSetUp()
1.18      rillig   1523:
1.48      rillig   1524:        G.Check(pkg)
1.18      rillig   1525:
1.48      rillig   1526:        // The variable DISTFILES is declared by the infrastructure.
                   1527:        // It is not defined by this package, therefore it doesn't
                   1528:        // appear in the RedundantScope.
1.18      rillig   1529:        t.CheckOutputLines(
1.48      rillig   1530:                "WARN: ~/category/package/Makefile:20: The list variable DISTFILES should not be embedded in a word.")
                   1531: }
                   1532:
                   1533: func (s *Suite) Test_MkLineChecker_checkVarUseQuoting__list_variable_with_single_constant_value(c *check.C) {
                   1534:        t := s.Init(c)
                   1535:
                   1536:        pkg := t.SetUpPackage("category/package",
                   1537:                "BUILD_DIRS=\tonly-dir",
                   1538:                "",
                   1539:                "do-install:",
                   1540:                "\t${INSTALL_PROGRAM} ${WRKSRC}/${BUILD_DIRS}/program ${DESTDIR}${PREFIX}/bin/")
                   1541:        t.FinishSetUp()
                   1542:
                   1543:        G.Check(pkg)
                   1544:
                   1545:        // Don't warn here since BUILD_DIRS, although being a list
                   1546:        // variable, contains only a single value.
                   1547:        t.CheckOutputEmpty()
1.18      rillig   1548: }
                   1549:
1.48      rillig   1550: func (s *Suite) Test_MkLineChecker_checkVarUseQuoting__list_variable_with_single_conditional_value(c *check.C) {
1.25      rillig   1551:        t := s.Init(c)
                   1552:
1.48      rillig   1553:        pkg := t.SetUpPackage("category/package",
                   1554:                "BUILD_DIRS=\tonly-dir",
                   1555:                ".if 0",
                   1556:                "BUILD_DIRS=\tother-dir",
                   1557:                ".endif",
                   1558:                "",
                   1559:                "do-install:",
                   1560:                "\t${INSTALL_PROGRAM} ${WRKSRC}/${BUILD_DIRS}/program ${DESTDIR}${PREFIX}/bin/")
                   1561:        t.FinishSetUp()
1.25      rillig   1562:
1.48      rillig   1563:        G.Check(pkg)
1.25      rillig   1564:
1.48      rillig   1565:        // TODO: Don't warn here since BUILD_DIRS, although being a list
                   1566:        //  variable, contains only a single value.
1.25      rillig   1567:        t.CheckOutputLines(
1.48      rillig   1568:                "WARN: ~/category/package/Makefile:26: " +
                   1569:                        "The list variable BUILD_DIRS should not be embedded in a word.")
1.25      rillig   1570: }
                   1571:
1.48      rillig   1572: func (s *Suite) Test_MkLineChecker_checkVarUseQuoting__list_variable_with_two_constant_words(c *check.C) {
1.27      rillig   1573:        t := s.Init(c)
                   1574:
1.48      rillig   1575:        pkg := t.SetUpPackage("category/package",
                   1576:                "BUILD_DIRS=\tfirst-dir second-dir",
                   1577:                "",
                   1578:                "do-install:",
                   1579:                "\t${INSTALL_PROGRAM} ${WRKSRC}/${BUILD_DIRS}/program ${DESTDIR}${PREFIX}/bin/")
                   1580:        t.FinishSetUp()
1.27      rillig   1581:
1.48      rillig   1582:        G.Check(pkg)
1.27      rillig   1583:
1.48      rillig   1584:        // Since BUILD_DIRS consists of two words, it would destroy the installation command.
1.27      rillig   1585:        t.CheckOutputLines(
1.48      rillig   1586:                "WARN: ~/category/package/Makefile:23: " +
                   1587:                        "The list variable BUILD_DIRS should not be embedded in a word.")
1.27      rillig   1588: }
                   1589:
1.48      rillig   1590: func (s *Suite) Test_MkLineChecker_checkVarassignOpShell(c *check.C) {
1.27      rillig   1591:        t := s.Init(c)
                   1592:
1.48      rillig   1593:        t.SetUpTool("uname", "UNAME", AfterPrefsMk)
                   1594:        t.SetUpTool("echo", "", AtRunTime)
                   1595:        t.SetUpPkgsrc()
                   1596:        t.SetUpPackage("category/package",
                   1597:                ".include \"standalone.mk\"")
                   1598:        t.CreateFileLines("category/package/standalone.mk",
1.38      rillig   1599:                MkCvsID,
1.48      rillig   1600:                "",
                   1601:                ".include \"../../mk/bsd.prefs.mk\"",
                   1602:                "",
                   1603:                "OPSYS_NAME!=\t${UNAME}",
                   1604:                ".if ${OPSYS_NAME} == \"NetBSD\"",
                   1605:                ".endif",
                   1606:                "",
                   1607:                "OS_NAME!=\t${UNAME}",
                   1608:                "",
                   1609:                "MUST_BE_EARLY!=\techo 123 # must be evaluated early",
                   1610:                "",
                   1611:                "show-package-vars: .PHONY",
                   1612:                "\techo OS_NAME=${OS_NAME:Q}",
                   1613:                "\techo MUST_BE_EARLY=${MUST_BE_EARLY:Q}")
                   1614:        t.FinishSetUp()
                   1615:
                   1616:        G.Check(t.File("category/package/standalone.mk"))
                   1617:
                   1618:        // There is no warning about any variable since no package is currently
                   1619:        // being checked, therefore pkglint cannot decide whether the variable
                   1620:        // is used a load time.
                   1621:        t.CheckOutputLines(
                   1622:                "WARN: ~/category/package/standalone.mk:14: Please use \"${ECHO}\" instead of \"echo\".",
                   1623:                "WARN: ~/category/package/standalone.mk:15: Please use \"${ECHO}\" instead of \"echo\".")
1.27      rillig   1624:
1.48      rillig   1625:        t.SetUpCommandLine("-Wall", "--explain")
                   1626:        G.Check(t.File("category/package"))
1.27      rillig   1627:
1.48      rillig   1628:        // There is no warning for OPSYS_NAME since that variable is used at
                   1629:        // load time. In such a case the command has to be executed anyway,
                   1630:        // and executing it exactly once is the best thing to do.
                   1631:        //
                   1632:        // There is no warning for MUST_BE_EARLY since the comment provides the
                   1633:        // reason that this command really has to be executed at load time.
1.27      rillig   1634:        t.CheckOutputLines(
1.48      rillig   1635:                "NOTE: ~/category/package/standalone.mk:9: Consider the :sh modifier instead of != for \"${UNAME}\".",
                   1636:                "",
                   1637:                "\tFor variable assignments using the != operator, the shell command is",
                   1638:                "\trun every time the file is parsed. In some cases this is too early,",
                   1639:                "\tand the command may not yet be installed. In other cases the command",
                   1640:                "\tis executed more often than necessary. Most commands don't need to",
                   1641:                "\tbe executed for \"make clean\", for example.",
                   1642:                "",
                   1643:                "\tThe :sh modifier defers execution until the variable value is",
                   1644:                "\tactually needed. On the other hand, this means the command is",
                   1645:                "\texecuted each time the variable is evaluated.",
                   1646:                "",
                   1647:                "\tExample:",
                   1648:                "",
                   1649:                "\t\tEARLY_YEAR!=    date +%Y",
                   1650:                "",
                   1651:                "\t\tLATE_YEAR_CMD=  date +%Y",
                   1652:                "\t\tLATE_YEAR=      ${LATE_YEAR_CMD:sh}",
                   1653:                "",
                   1654:                "\t\t# or, in a single line:",
                   1655:                "\t\tLATE_YEAR=      ${date +%Y:L:sh}",
                   1656:                "",
                   1657:                "\tTo suppress this note, provide an explanation in a comment at the",
                   1658:                "\tend of the line, or force the variable to be evaluated at load time,",
                   1659:                "\tby using it at the right-hand side of the := operator, or in an .if",
                   1660:                "\tor .for directive.",
                   1661:                "",
                   1662:                "WARN: ~/category/package/standalone.mk:14: Please use \"${ECHO}\" instead of \"echo\".",
                   1663:                "WARN: ~/category/package/standalone.mk:15: Please use \"${ECHO}\" instead of \"echo\".")
1.28      rillig   1664: }
                   1665:
1.48      rillig   1666: func (s *Suite) Test_MkLineChecker_checkText(c *check.C) {
1.28      rillig   1667:        t := s.Init(c)
                   1668:
1.48      rillig   1669:        t.SetUpPkgsrc()
                   1670:
                   1671:        t.SetUpCommandLine("-Wall,no-space")
                   1672:        mklines := t.SetUpFileMkLines("module.mk",
1.38      rillig   1673:                MkCvsID,
1.48      rillig   1674:                "CFLAGS+=                -Wl,--rpath,${PREFIX}/lib",
                   1675:                "PKG_FAIL_REASON+=       \"Group ${GAMEGRP} doesn't exist.\"")
                   1676:        t.FinishSetUp()
1.28      rillig   1677:
                   1678:        mklines.Check()
                   1679:
1.48      rillig   1680:        t.CheckOutputLines(
                   1681:                "WARN: ~/module.mk:2: Please use ${COMPILER_RPATH_FLAG} instead of \"-Wl,--rpath,\".",
                   1682:                "WARN: ~/module.mk:3: Use of \"GAMEGRP\" is deprecated. Use GAMES_GROUP instead.")
1.28      rillig   1683: }
                   1684:
1.48      rillig   1685: func (s *Suite) Test_MkLineChecker_checkText__WRKSRC(c *check.C) {
1.28      rillig   1686:        t := s.Init(c)
                   1687:
1.48      rillig   1688:        t.SetUpCommandLine("-Wall", "--explain")
                   1689:        mklines := t.SetUpFileMkLines("module.mk",
1.38      rillig   1690:                MkCvsID,
1.48      rillig   1691:                "pre-configure:",
                   1692:                "\tcd ${WRKSRC}/..")
1.28      rillig   1693:
                   1694:        mklines.Check()
                   1695:
1.48      rillig   1696:        t.CheckOutputLines(
                   1697:                "WARN: ~/module.mk:3: Building the package should take place entirely inside ${WRKSRC}, not \"${WRKSRC}/..\".",
                   1698:                "",
                   1699:                "\tWRKSRC should be defined so that there is no need to do anything",
                   1700:                "\toutside of this directory.",
                   1701:                "",
                   1702:                "\tExample:",
                   1703:                "",
                   1704:                "\t\tWRKSRC=\t${WRKDIR}",
                   1705:                "\t\tCONFIGURE_DIRS=\t${WRKSRC}/lib ${WRKSRC}/src",
                   1706:                "\t\tBUILD_DIRS=\t${WRKSRC}/lib ${WRKSRC}/src ${WRKSRC}/cmd",
                   1707:                "",
                   1708:                "\tSee the pkgsrc guide, section \"Directories used during the build",
                   1709:                "\tprocess\":",
                   1710:                "\thttps://www.NetBSD.org/docs/pkgsrc/pkgsrc.html#build.builddirs",
                   1711:                "",
                   1712:                "WARN: ~/module.mk:3: WRKSRC is used but not defined.")
1.27      rillig   1713: }
                   1714:
1.48      rillig   1715: func (s *Suite) Test_MkLineChecker_checkVartype__simple_type(c *check.C) {
1.27      rillig   1716:        t := s.Init(c)
                   1717:
1.48      rillig   1718:        t.SetUpVartypes()
                   1719:
                   1720:        // Since COMMENT is defined in vardefs.go its type is certain instead of guessed.
                   1721:        vartype := G.Pkgsrc.VariableType(nil, "COMMENT")
                   1722:
                   1723:        c.Assert(vartype, check.NotNil)
                   1724:        t.CheckEquals(vartype.basicType.name, "Comment")
                   1725:        t.CheckEquals(vartype.IsGuessed(), false)
                   1726:        t.CheckEquals(vartype.IsList(), false)
1.28      rillig   1727:
1.48      rillig   1728:        mklines := t.NewMkLines("Makefile",
1.38      rillig   1729:                MkCvsID,
1.48      rillig   1730:                "COMMENT=\tA nice package")
1.27      rillig   1731:        mklines.Check()
                   1732:
                   1733:        t.CheckOutputLines(
1.48      rillig   1734:                "WARN: Makefile:2: COMMENT should not begin with \"A\".")
1.27      rillig   1735: }
                   1736:
1.48      rillig   1737: func (s *Suite) Test_MkLineChecker_checkVartype(c *check.C) {
1.27      rillig   1738:        t := s.Init(c)
                   1739:
                   1740:        t.SetUpVartypes()
1.48      rillig   1741:        mklines := t.NewMkLines("filename.mk",
1.38      rillig   1742:                MkCvsID,
1.48      rillig   1743:                "DISTNAME=\tgcc-${GCC_VERSION}")
1.27      rillig   1744:
1.48      rillig   1745:        mklines.vars.Define("GCC_VERSION", mklines.mklines[1])
1.28      rillig   1746:        mklines.Check()
1.27      rillig   1747:
1.48      rillig   1748:        t.CheckOutputEmpty()
                   1749: }
                   1750:
                   1751: func (s *Suite) Test_MkLineChecker_checkVartype__append_to_non_list(c *check.C) {
1.37      rillig   1752:        t := s.Init(c)
                   1753:
                   1754:        t.SetUpVartypes()
1.48      rillig   1755:        mklines := t.NewMkLines("filename.mk",
1.38      rillig   1756:                MkCvsID,
1.48      rillig   1757:                "DISTNAME+=\tsuffix",
                   1758:                "COMMENT=\tComment for",
                   1759:                "COMMENT+=\tthe package")
1.37      rillig   1760:
                   1761:        mklines.Check()
                   1762:
                   1763:        t.CheckOutputLines(
1.48      rillig   1764:                "WARN: filename.mk:2: The variable DISTNAME should not be appended to "+
                   1765:                        "(only set, or given a default value) in this file.",
                   1766:                "WARN: filename.mk:2: The \"+=\" operator should only be used with lists, not with DISTNAME.")
1.37      rillig   1767: }
                   1768:
1.48      rillig   1769: func (s *Suite) Test_MkLineChecker_checkVartype__no_tracing(c *check.C) {
1.27      rillig   1770:        t := s.Init(c)
                   1771:
                   1772:        t.SetUpVartypes()
1.48      rillig   1773:        mklines := t.NewMkLines("filename.mk",
1.38      rillig   1774:                MkCvsID,
1.48      rillig   1775:                "UNKNOWN=\tvalue",
                   1776:                "CUR_DIR!=\tpwd")
                   1777:        t.DisableTracing()
1.27      rillig   1778:
                   1779:        mklines.Check()
                   1780:
                   1781:        t.CheckOutputLines(
1.48      rillig   1782:                "WARN: filename.mk:2: UNKNOWN is defined but not used.",
                   1783:                "WARN: filename.mk:3: CUR_DIR is defined but not used.")
1.27      rillig   1784: }
                   1785:
1.48      rillig   1786: func (s *Suite) Test_MkLineChecker_checkVartype__one_per_line(c *check.C) {
1.25      rillig   1787:        t := s.Init(c)
                   1788:
                   1789:        t.SetUpVartypes()
1.48      rillig   1790:        mklines := t.NewMkLines("filename.mk",
1.38      rillig   1791:                MkCvsID,
1.48      rillig   1792:                "PKG_FAIL_REASON+=\tSeveral words are wrong.",
                   1793:                "PKG_FAIL_REASON+=\t\"Properly quoted\"",
                   1794:                "PKG_FAIL_REASON+=\t# none")
                   1795:        t.DisableTracing()
1.25      rillig   1796:
                   1797:        mklines.Check()
                   1798:
                   1799:        t.CheckOutputLines(
1.48      rillig   1800:                "WARN: filename.mk:2: PKG_FAIL_REASON should only get one item per line.")
1.25      rillig   1801: }
                   1802:
1.48      rillig   1803: func (s *Suite) Test_MkLineChecker_checkVartype__CFLAGS_with_backticks(c *check.C) {
1.27      rillig   1804:        t := s.Init(c)
                   1805:
                   1806:        t.SetUpVartypes()
1.48      rillig   1807:        mklines := t.NewMkLines("chat/pidgin-icb/Makefile",
1.38      rillig   1808:                MkCvsID,
1.48      rillig   1809:                "CFLAGS+=\t`pkg-config pidgin --cflags`")
                   1810:        mkline := mklines.mklines[1]
                   1811:
                   1812:        words := mkline.Fields()
                   1813:
                   1814:        // bmake handles backticks in the same way, treating them as ordinary characters
                   1815:        t.CheckDeepEquals(words, []string{"`pkg-config", "pidgin", "--cflags`"})
1.27      rillig   1816:
1.48      rillig   1817:        ck := MkLineChecker{mklines, mklines.mklines[1]}
                   1818:        ck.checkVartype("CFLAGS", opAssignAppend, "`pkg-config pidgin --cflags`", "")
1.27      rillig   1819:
1.48      rillig   1820:        // No warning about "`pkg-config" being an unknown CFlag.
                   1821:        // As of September 2019, there is no such check anymore in pkglint.
                   1822:        t.CheckOutputEmpty()
1.27      rillig   1823: }
                   1824:
1.48      rillig   1825: // See PR 46570, Ctrl+F "4. Shell quoting".
                   1826: // Pkglint is correct, since the shell sees this definition for
                   1827: // CPPFLAGS as three words, not one word.
                   1828: func (s *Suite) Test_MkLineChecker_checkVartype__CFLAGS(c *check.C) {
1.5       rillig   1829:        t := s.Init(c)
                   1830:
1.23      rillig   1831:        t.SetUpVartypes()
1.48      rillig   1832:        mklines := t.NewMkLines("Makefile",
1.38      rillig   1833:                MkCvsID,
1.48      rillig   1834:                "CPPFLAGS.SunOS+=\t-DPIPECOMMAND=\\\"/usr/sbin/sendmail -bs %s\\\"")
1.1       rillig   1835:
1.28      rillig   1836:        mklines.Check()
1.1       rillig   1837:
1.5       rillig   1838:        t.CheckOutputLines(
1.48      rillig   1839:                "WARN: Makefile:2: Compiler flag \"-DPIPECOMMAND=\\\\\\\"/usr/sbin/sendmail\" has unbalanced double quotes.",
                   1840:                "WARN: Makefile:2: Compiler flag \"%s\\\\\\\"\" has unbalanced double quotes.")
1.1       rillig   1841: }
                   1842:
1.48      rillig   1843: func (s *Suite) Test_MkLineChecker_checkVarassignRightCategory__none(c *check.C) {
1.5       rillig   1844:        t := s.Init(c)
                   1845:
1.48      rillig   1846:        t.SetUpPackage("obscure/package",
                   1847:                "CATEGORIES=\t# none")
                   1848:        t.FinishSetUp()
                   1849:
                   1850:        G.Check(t.File("obscure/package"))
                   1851:
                   1852:        t.CheckOutputEmpty()
                   1853: }
1.1       rillig   1854:
1.48      rillig   1855: func (s *Suite) Test_MkLineChecker_checkVarassignRightCategory__indirect(c *check.C) {
                   1856:        t := s.Init(c)
1.30      rillig   1857:
1.48      rillig   1858:        t.SetUpPackage("obscure/package",
                   1859:                "CATEGORIES=\t${PKGPATH:C,/.*,,}")
                   1860:        t.FinishSetUp()
1.30      rillig   1861:
1.48      rillig   1862:        G.Check(t.File("obscure/package"))
1.30      rillig   1863:
1.48      rillig   1864:        // This case does not occur in practice,
                   1865:        // therefore it's ok to have these warnings.
                   1866:        t.CheckOutputLines(
                   1867:                "WARN: ~/obscure/package/Makefile:5: "+
                   1868:                        "The primary category should be \"obscure\", not \"${PKGPATH:C,/.*,,}\".",
                   1869:                "ERROR: ~/obscure/package/Makefile:5: "+
                   1870:                        "Invalid category \"${PKGPATH:C,/.*,,}\".")
                   1871: }
1.30      rillig   1872:
1.48      rillig   1873: func (s *Suite) Test_MkLineChecker_checkVarassignRightCategory__wrong(c *check.C) {
                   1874:        t := s.Init(c)
1.28      rillig   1875:
1.48      rillig   1876:        t.SetUpPackage("obscure/package",
                   1877:                "CATEGORIES=\tperl5")
                   1878:        t.FinishSetUp()
1.20      rillig   1879:
1.48      rillig   1880:        G.Check(t.File("obscure/package"))
1.43      rillig   1881:
1.48      rillig   1882:        t.CheckOutputLines(
                   1883:                "WARN: ~/obscure/package/Makefile:5: The primary category should be \"obscure\", not \"perl5\".")
1.1       rillig   1884: }
                   1885:
1.48      rillig   1886: func (s *Suite) Test_MkLineChecker_checkVarassignRightCategory__wrong_in_package_directory(c *check.C) {
1.5       rillig   1887:        t := s.Init(c)
                   1888:
1.48      rillig   1889:        t.SetUpPackage("obscure/package",
                   1890:                "CATEGORIES=\tperl5")
                   1891:        t.FinishSetUp()
                   1892:        t.Chdir("obscure/package")
1.1       rillig   1893:
1.48      rillig   1894:        G.Check(".")
1.1       rillig   1895:
1.5       rillig   1896:        t.CheckOutputLines(
1.48      rillig   1897:                "WARN: Makefile:5: The primary category should be \"obscure\", not \"perl5\".")
1.1       rillig   1898: }
                   1899:
1.48      rillig   1900: func (s *Suite) Test_MkLineChecker_checkVarassignRightCategory__append(c *check.C) {
1.5       rillig   1901:        t := s.Init(c)
                   1902:
1.48      rillig   1903:        t.SetUpPackage("obscure/package",
                   1904:                "CATEGORIES+=\tperl5")
                   1905:        t.FinishSetUp()
1.1       rillig   1906:
1.48      rillig   1907:        G.Check(t.File("obscure/package"))
1.1       rillig   1908:
1.48      rillig   1909:        // Appending is ok.
                   1910:        // In this particular case, appending has the same effect as assigning,
                   1911:        // but that can be checked somewhere else.
                   1912:        t.CheckOutputEmpty()
1.1       rillig   1913: }
                   1914:
1.48      rillig   1915: func (s *Suite) Test_MkLineChecker_checkVarassignRightCategory__default(c *check.C) {
1.5       rillig   1916:        t := s.Init(c)
                   1917:
1.48      rillig   1918:        t.SetUpPackage("obscure/package",
                   1919:                "CATEGORIES?=\tperl5")
                   1920:        t.FinishSetUp()
1.1       rillig   1921:
1.48      rillig   1922:        G.Check(t.File("obscure/package"))
1.1       rillig   1923:
1.48      rillig   1924:        // Default assignments set the primary category, just like simple assignments.
1.5       rillig   1925:        t.CheckOutputLines(
1.48      rillig   1926:                "WARN: ~/obscure/package/Makefile:5: The primary category should be \"obscure\", not \"perl5\".")
1.1       rillig   1927: }
                   1928:
1.48      rillig   1929: func (s *Suite) Test_MkLineChecker_checkVarassignRightCategory__autofix(c *check.C) {
1.27      rillig   1930:        t := s.Init(c)
                   1931:
1.48      rillig   1932:        t.SetUpCommandLine("-Wall", "--autofix")
                   1933:        t.SetUpPackage("obscure/package",
                   1934:                "CATEGORIES=\tperl5 obscure python")
                   1935:        t.FinishSetUp()
1.27      rillig   1936:
1.48      rillig   1937:        G.Check(t.File("obscure/package"))
1.27      rillig   1938:
                   1939:        t.CheckOutputLines(
1.48      rillig   1940:                "AUTOFIX: ~/obscure/package/Makefile:5: " +
                   1941:                        "Replacing \"perl5 obscure\" with \"obscure perl5\".")
1.27      rillig   1942: }
                   1943:
1.48      rillig   1944: func (s *Suite) Test_MkLineChecker_checkVarassignRightCategory__third(c *check.C) {
1.20      rillig   1945:        t := s.Init(c)
                   1946:
1.48      rillig   1947:        t.SetUpPackage("obscure/package",
                   1948:                "CATEGORIES=\tperl5 python obscure")
                   1949:        t.FinishSetUp()
1.20      rillig   1950:
1.48      rillig   1951:        G.Check(t.File("obscure/package"))
1.31      rillig   1952:
1.48      rillig   1953:        t.CheckOutputLines(
                   1954:                "WARN: ~/obscure/package/Makefile:5: " +
                   1955:                        "The primary category should be \"obscure\", not \"perl5\".")
                   1956:
                   1957:        t.SetUpCommandLine("-Wall", "--show-autofix")
                   1958:
                   1959:        G.Check(t.File("obscure/package"))
                   1960:        t.CheckOutputEmpty()
                   1961: }
1.31      rillig   1962:
1.48      rillig   1963: func (s *Suite) Test_MkLineChecker_checkVarassignRightCategory__other_file(c *check.C) {
                   1964:        t := s.Init(c)
1.31      rillig   1965:
1.48      rillig   1966:        t.SetUpPackage("obscure/package",
                   1967:                "CATEGORIES=\tperl5 obscure python")
                   1968:        mklines := t.SetUpFileMkLines("obscure/package/module.mk",
                   1969:                MkCvsID,
                   1970:                "",
                   1971:                "CATEGORIES=\tperl5")
                   1972:        t.FinishSetUp()
1.31      rillig   1973:
1.48      rillig   1974:        mklines.Check()
1.31      rillig   1975:
1.48      rillig   1976:        // It doesn't matter in which file the CATEGORIES= line appears.
                   1977:        // If it's a plain assignment, it will end up as the primary category.
                   1978:        t.CheckOutputLines(
                   1979:                "WARN: ~/obscure/package/module.mk:3: " +
                   1980:                        "The primary category should be \"obscure\", not \"perl5\".")
                   1981: }
1.31      rillig   1982:
1.48      rillig   1983: func (s *Suite) Test_MkLineChecker_checkVarassignMisc(c *check.C) {
                   1984:        t := s.Init(c)
1.31      rillig   1985:
1.48      rillig   1986:        t.SetUpPkgsrc()
                   1987:        t.SetUpMasterSite("MASTER_SITE_GITHUB", "https://download.github.com/")
                   1988:        t.SetUpCommandLine("-Wall,no-space")
                   1989:        mklines := t.SetUpFileMkLines("module.mk",
                   1990:                MkCvsID,
                   1991:                "EGDIR=                  ${PREFIX}/etc/rc.d",
                   1992:                "RPMIGNOREPATH+=         ${PREFIX}/etc/rc.d",
                   1993:                "_TOOLS_VARNAME.sed=     SED",
                   1994:                "DIST_SUBDIR=            ${PKGNAME}",
                   1995:                "WRKSRC=                 ${PKGNAME}",
                   1996:                "SITES_distfile.tar.gz=  ${MASTER_SITE_GITHUB:=user/}",
                   1997:                "MASTER_SITES=           https://cdn.example.org/${PKGNAME}/",
                   1998:                "MASTER_SITES=           https://cdn.example.org/distname-${PKGVERSION}/")
                   1999:        t.FinishSetUp()
1.35      rillig   2000:
1.48      rillig   2001:        mklines.Check()
1.35      rillig   2002:
1.48      rillig   2003:        // TODO: Split this test into several, one for each topic.
                   2004:        t.CheckOutputLines(
                   2005:                "WARN: ~/module.mk:2: Please use the RCD_SCRIPTS mechanism to install rc.d scripts automatically to ${RCD_SCRIPTS_EXAMPLEDIR}.",
                   2006:                "WARN: ~/module.mk:4: Variable names starting with an underscore (_TOOLS_VARNAME.sed) are reserved for internal pkgsrc use.",
                   2007:                "WARN: ~/module.mk:4: _TOOLS_VARNAME.sed is defined but not used.",
                   2008:                "WARN: ~/module.mk:5: PKGNAME should not be used in DIST_SUBDIR as it includes the PKGREVISION. Please use PKGNAME_NOREV instead.",
                   2009:                "WARN: ~/module.mk:6: PKGNAME should not be used in WRKSRC as it includes the PKGREVISION. Please use PKGNAME_NOREV instead.",
                   2010:                "WARN: ~/module.mk:7: SITES_distfile.tar.gz is defined but not used.",
                   2011:                "WARN: ~/module.mk:7: SITES_* is deprecated. Please use SITES.* instead.",
                   2012:                "WARN: ~/module.mk:8: PKGNAME should not be used in MASTER_SITES as it includes the PKGREVISION. Please use PKGNAME_NOREV instead.",
                   2013:                "WARN: ~/module.mk:9: PKGVERSION should not be used in MASTER_SITES as it includes the PKGREVISION. Please use PKGVERSION_NOREV instead.")
                   2014: }
1.20      rillig   2015:
1.48      rillig   2016: func (s *Suite) Test_MkLineChecker_checkVarassignMisc__multiple_inclusion_guards(c *check.C) {
                   2017:        t := s.Init(c)
1.31      rillig   2018:
1.48      rillig   2019:        t.SetUpPkgsrc()
                   2020:        t.CreateFileLines("filename.mk",
                   2021:                MkCvsID,
                   2022:                ".if !defined(FILENAME_MK)",
                   2023:                "FILENAME_MK=\t# defined",
                   2024:                ".endif")
                   2025:        t.CreateFileLines("Makefile.common",
                   2026:                MkCvsID,
                   2027:                ".if !defined(MAKEFILE_COMMON)",
                   2028:                "MAKEFILE_COMMON=\t# defined",
                   2029:                "",
                   2030:                ".endif")
                   2031:        t.CreateFileLines("other.mk",
                   2032:                MkCvsID,
                   2033:                "COMMENT=\t# defined")
                   2034:        t.FinishSetUp()
1.35      rillig   2035:
1.48      rillig   2036:        G.Check(t.File("filename.mk"))
                   2037:        G.Check(t.File("Makefile.common"))
                   2038:        G.Check(t.File("other.mk"))
1.35      rillig   2039:
1.48      rillig   2040:        // For multiple-inclusion guards, the meaning of the variable value
                   2041:        // is clear, therefore they are exempted from the warnings.
                   2042:        t.CheckOutputLines(
                   2043:                "NOTE: ~/other.mk:2: Please use \"# empty\", \"# none\" or \"# yes\" " +
                   2044:                        "instead of \"# defined\".")
                   2045: }
1.20      rillig   2046:
1.48      rillig   2047: func (s *Suite) Test_MkLineChecker_checkVarassignDecreasingVersions(c *check.C) {
                   2048:        t := s.Init(c)
1.35      rillig   2049:
1.48      rillig   2050:        t.SetUpVartypes()
                   2051:        mklines := t.NewMkLines("Makefile",
                   2052:                MkCvsID,
                   2053:                "PYTHON_VERSIONS_ACCEPTED=\t36 __future__ # rationale",
                   2054:                "PYTHON_VERSIONS_ACCEPTED=\t36 -13 # rationale",
                   2055:                "PYTHON_VERSIONS_ACCEPTED=\t36 ${PKGVERSION_NOREV} # rationale",
                   2056:                "PYTHON_VERSIONS_ACCEPTED=\t36 37 # rationale",
                   2057:                "PYTHON_VERSIONS_ACCEPTED=\t37 36 27 25 # rationale")
1.35      rillig   2058:
1.48      rillig   2059:        // TODO: All but the last of the above assignments should be flagged as
                   2060:        //  redundant by RedundantScope; as of March 2019, that check is only
                   2061:        //  implemented for package Makefiles, not for individual files.
1.31      rillig   2062:
1.48      rillig   2063:        mklines.Check()
1.31      rillig   2064:
1.48      rillig   2065:        // Half of these warnings are from VartypeCheck.Version, the
                   2066:        // other half are from checkVarassignDecreasingVersions.
                   2067:        // Strictly speaking some of them are redundant, but that would
                   2068:        // mean to reject only variable references in checkVarassignDecreasingVersions.
                   2069:        // This is probably ok.
                   2070:        // TODO: Fix this.
                   2071:        t.CheckOutputLines(
                   2072:                "WARN: Makefile:2: Invalid version number \"__future__\".",
                   2073:                "ERROR: Makefile:2: Value \"__future__\" for "+
                   2074:                        "PYTHON_VERSIONS_ACCEPTED must be a positive integer.",
                   2075:                "WARN: Makefile:3: Invalid version number \"-13\".",
                   2076:                "ERROR: Makefile:3: Value \"-13\" for "+
                   2077:                        "PYTHON_VERSIONS_ACCEPTED must be a positive integer.",
                   2078:                "ERROR: Makefile:4: Value \"${PKGVERSION_NOREV}\" for "+
                   2079:                        "PYTHON_VERSIONS_ACCEPTED must be a positive integer.",
                   2080:                "WARN: Makefile:5: The values for PYTHON_VERSIONS_ACCEPTED "+
                   2081:                        "should be in decreasing order (37 before 36).")
                   2082: }
1.35      rillig   2083:
1.48      rillig   2084: func (s *Suite) Test_MkLineChecker_checkVarassignRightVaruse(c *check.C) {
                   2085:        t := s.Init(c)
1.35      rillig   2086:
1.48      rillig   2087:        t.SetUpVartypes()
1.31      rillig   2088:
1.48      rillig   2089:        mklines := t.NewMkLines("module.mk",
                   2090:                MkCvsID,
                   2091:                "PLIST_SUBST+=\tLOCALBASE=${LOCALBASE:Q}")
1.35      rillig   2092:
1.48      rillig   2093:        mklines.Check()
1.35      rillig   2094:
1.48      rillig   2095:        t.CheckOutputLines(
                   2096:                "WARN: module.mk:2: Please use PREFIX instead of LOCALBASE.",
                   2097:                "NOTE: module.mk:2: The :Q modifier isn't necessary for ${LOCALBASE} here.")
                   2098: }
1.31      rillig   2099:
1.48      rillig   2100: func (s *Suite) Test_MkLineChecker_checkShellCommand__indentation(c *check.C) {
                   2101:        t := s.Init(c)
1.35      rillig   2102:
1.48      rillig   2103:        mklines := t.SetUpFileMkLines("filename.mk",
                   2104:                MkCvsID,
                   2105:                "",
                   2106:                "do-install:",
                   2107:                "\t\techo 'unnecessarily indented'",
                   2108:                "\t\tfor var in 1 2 3; do \\",
                   2109:                "\t\t\techo \"$$var\"; \\",
                   2110:                "\t                echo \"spaces\"; \\",
                   2111:                "\t\tdone",
                   2112:                "",
                   2113:                "\t\t\t\t\t# comment, not a shell command")
1.35      rillig   2114:
1.48      rillig   2115:        mklines.Check()
                   2116:        t.SetUpCommandLine("-Wall", "--autofix")
                   2117:        mklines.Check()
1.31      rillig   2118:
1.48      rillig   2119:        t.CheckOutputLines(
                   2120:                "NOTE: ~/filename.mk:4: Shell programs should be indented with a single tab.",
                   2121:                "WARN: ~/filename.mk:4: Unknown shell command \"echo\".",
                   2122:                "NOTE: ~/filename.mk:5--8: Shell programs should be indented with a single tab.",
                   2123:                "WARN: ~/filename.mk:5--8: Unknown shell command \"echo\".",
                   2124:                "WARN: ~/filename.mk:5--8: Please switch to \"set -e\" mode before using a semicolon "+
                   2125:                        "(after \"echo \\\"$$var\\\"\") to separate commands.",
                   2126:                "WARN: ~/filename.mk:5--8: Unknown shell command \"echo\".",
1.35      rillig   2127:
1.48      rillig   2128:                "AUTOFIX: ~/filename.mk:4: Replacing \"\\t\\t\" with \"\\t\".",
                   2129:                "AUTOFIX: ~/filename.mk:5: Replacing \"\\t\\t\" with \"\\t\".",
                   2130:                "AUTOFIX: ~/filename.mk:6: Replacing \"\\t\\t\" with \"\\t\".",
                   2131:                "AUTOFIX: ~/filename.mk:8: Replacing \"\\t\\t\" with \"\\t\".")
                   2132:        t.CheckFileLinesDetab("filename.mk",
                   2133:                MkCvsID,
                   2134:                "",
                   2135:                "do-install:",
                   2136:                "        echo 'unnecessarily indented'",
                   2137:                "        for var in 1 2 3; do \\",
                   2138:                "                echo \"$$var\"; \\",
                   2139:                "                        echo \"spaces\"; \\", // not changed
                   2140:                "        done",
                   2141:                "",
                   2142:                "                                        # comment, not a shell command")
                   2143: }
1.35      rillig   2144:
1.48      rillig   2145: func (s *Suite) Test_MkLineChecker_checkInclude(c *check.C) {
                   2146:        t := s.Init(c)
1.31      rillig   2147:
1.48      rillig   2148:        t.SetUpVartypes()
1.37      rillig   2149:
1.48      rillig   2150:        t.CreateFileLines("pkgtools/x11-links/buildlink3.mk")
                   2151:        t.CreateFileLines("graphics/jpeg/buildlink3.mk")
                   2152:        t.CreateFileLines("devel/intltool/buildlink3.mk")
                   2153:        t.CreateFileLines("devel/intltool/builtin.mk")
                   2154:        mklines := t.SetUpFileMkLines("category/package/filename.mk",
                   2155:                MkCvsID,
                   2156:                "",
                   2157:                ".include \"../../pkgtools/x11-links/buildlink3.mk\"",
                   2158:                ".include \"../../graphics/jpeg/buildlink3.mk\"",
                   2159:                ".include \"../../devel/intltool/buildlink3.mk\"",
                   2160:                ".include \"../../devel/intltool/builtin.mk\"")
1.37      rillig   2161:
1.48      rillig   2162:        mklines.Check()
1.37      rillig   2163:
1.48      rillig   2164:        t.CheckOutputLines(
                   2165:                "ERROR: ~/category/package/filename.mk:3: "+
                   2166:                        "../../pkgtools/x11-links/buildlink3.mk must not be included directly. "+
                   2167:                        "Include \"../../mk/x11.buildlink3.mk\" instead.",
                   2168:                "ERROR: ~/category/package/filename.mk:4: "+
                   2169:                        "../../graphics/jpeg/buildlink3.mk must not be included directly. "+
                   2170:                        "Include \"../../mk/jpeg.buildlink3.mk\" instead.",
                   2171:                "WARN: ~/category/package/filename.mk:5: "+
                   2172:                        "Please write \"USE_TOOLS+= intltool\" instead of this line.",
                   2173:                "ERROR: ~/category/package/filename.mk:6: "+
                   2174:                        "../../devel/intltool/builtin.mk must not be included directly. "+
                   2175:                        "Include \"../../devel/intltool/buildlink3.mk\" instead.")
                   2176: }
1.37      rillig   2177:
1.48      rillig   2178: func (s *Suite) Test_MkLineChecker_checkInclude__Makefile(c *check.C) {
                   2179:        t := s.Init(c)
1.37      rillig   2180:
1.48      rillig   2181:        mklines := t.NewMkLines(t.File("Makefile"),
                   2182:                MkCvsID,
                   2183:                ".include \"../../other/package/Makefile\"")
1.31      rillig   2184:
1.48      rillig   2185:        mklines.Check()
1.35      rillig   2186:
1.48      rillig   2187:        t.CheckOutputLines(
                   2188:                "ERROR: ~/Makefile:2: Relative path \"../../other/package/Makefile\" does not exist.",
                   2189:                "ERROR: ~/Makefile:2: Other Makefiles must not be included directly.")
                   2190: }
1.35      rillig   2191:
1.48      rillig   2192: func (s *Suite) Test_MkLineChecker_checkInclude__Makefile_exists(c *check.C) {
                   2193:        t := s.Init(c)
1.31      rillig   2194:
1.48      rillig   2195:        t.CreateFileLines("other/existing/Makefile")
                   2196:        t.SetUpPackage("category/package",
                   2197:                ".include \"../../other/existing/Makefile\"",
                   2198:                ".include \"../../other/not-found/Makefile\"")
                   2199:        t.FinishSetUp()
1.35      rillig   2200:
1.48      rillig   2201:        G.checkdirPackage(t.File("category/package"))
1.35      rillig   2202:
1.48      rillig   2203:        t.CheckOutputLines(
                   2204:                "ERROR: ~/category/package/Makefile:21: Cannot read \"../../other/not-found/Makefile\".")
                   2205: }
1.31      rillig   2206:
1.48      rillig   2207: func (s *Suite) Test_MkLineChecker_checkInclude__hacks(c *check.C) {
                   2208:        t := s.Init(c)
1.37      rillig   2209:
1.48      rillig   2210:        t.SetUpPackage("category/package")
                   2211:        t.CreateFileLines("category/package/hacks.mk",
                   2212:                MkCvsID,
                   2213:                ".include \"../../category/package/nonexistent.mk\"",
                   2214:                ".include \"../../category/package/builtin.mk\"")
                   2215:        t.CreateFileLines("category/package/builtin.mk",
                   2216:                MkCvsID)
                   2217:        t.FinishSetUp()
1.37      rillig   2218:
1.48      rillig   2219:        G.checkdirPackage(t.File("category/package"))
1.37      rillig   2220:
1.48      rillig   2221:        // The purpose of this "nonexistent" diagnostic is only to show that
                   2222:        // hacks.mk is indeed parsed and checked.
                   2223:        t.CheckOutputLines(
                   2224:                "ERROR: ~/category/package/hacks.mk:2: " +
                   2225:                        "Relative path \"../../category/package/nonexistent.mk\" does not exist.")
                   2226: }
1.35      rillig   2227:
1.48      rillig   2228: func (s *Suite) Test_MkLineChecker_checkInclude__builtin_mk(c *check.C) {
                   2229:        t := s.Init(c)
1.35      rillig   2230:
1.48      rillig   2231:        t.SetUpPackage("category/package",
                   2232:                ".include \"../../category/package/builtin.mk\"",
                   2233:                ".include \"../../category/package/builtin.mk\" # ok")
                   2234:        t.CreateFileLines("category/package/builtin.mk",
                   2235:                MkCvsID)
                   2236:        t.FinishSetUp()
1.31      rillig   2237:
1.48      rillig   2238:        G.checkdirPackage(t.File("category/package"))
1.35      rillig   2239:
1.48      rillig   2240:        t.CheckOutputLines(
                   2241:                "ERROR: ~/category/package/Makefile:20: " +
                   2242:                        "../../category/package/builtin.mk must not be included directly. " +
                   2243:                        "Include \"../../category/package/buildlink3.mk\" instead.")
                   2244: }
1.35      rillig   2245:
1.50    ! rillig   2246: func (s *Suite) Test_MkLineChecker_checkInclude__buildlink3_mk_includes_builtin_mk(c *check.C) {
        !          2247:        t := s.Init(c)
        !          2248:
        !          2249:        t.SetUpPkgsrc()
        !          2250:        mklines := t.SetUpFileMkLines("category/package/buildlink3.mk",
        !          2251:                MkCvsID,
        !          2252:                ".include \"builtin.mk\"")
        !          2253:        t.CreateFileLines("category/package/builtin.mk",
        !          2254:                MkCvsID)
        !          2255:        t.FinishSetUp()
        !          2256:
        !          2257:        mklines.Check()
        !          2258:
        !          2259:        t.CheckOutputEmpty()
        !          2260: }
        !          2261:
1.48      rillig   2262: func (s *Suite) Test_MkLineChecker_checkInclude__builtin_mk_rationale(c *check.C) {
                   2263:        t := s.Init(c)
1.20      rillig   2264:
1.48      rillig   2265:        t.SetUpPackage("category/package",
                   2266:                "# I have good reasons for including this file directly.",
                   2267:                ".include \"../../category/package/builtin.mk\"",
                   2268:                "",
                   2269:                ".include \"../../category/package/builtin.mk\"")
                   2270:        t.CreateFileLines("category/package/builtin.mk",
                   2271:                MkCvsID)
                   2272:        t.FinishSetUp()
1.35      rillig   2273:
1.48      rillig   2274:        G.checkdirPackage(t.File("category/package"))
1.35      rillig   2275:
1.48      rillig   2276:        t.CheckOutputLines(
                   2277:                "ERROR: ~/category/package/Makefile:23: " +
                   2278:                        "../../category/package/builtin.mk must not be included directly. " +
                   2279:                        "Include \"../../category/package/buildlink3.mk\" instead.")
                   2280: }
1.20      rillig   2281:
1.48      rillig   2282: func (s *Suite) Test_MkLineChecker_checkDirectiveIndentation__autofix(c *check.C) {
                   2283:        t := s.Init(c)
1.35      rillig   2284:
1.48      rillig   2285:        t.SetUpCommandLine("--autofix", "-Wspace")
                   2286:        lines := t.SetUpFileLines("filename.mk",
                   2287:                MkCvsID,
                   2288:                ".if defined(A)",
                   2289:                ".for a in ${A}",
                   2290:                ".if defined(C)",
                   2291:                ".endif",
                   2292:                ".endfor",
                   2293:                ".endif")
                   2294:        mklines := NewMkLines(lines)
1.35      rillig   2295:
1.48      rillig   2296:        mklines.Check()
1.31      rillig   2297:
1.48      rillig   2298:        t.CheckOutputLines(
                   2299:                "AUTOFIX: ~/filename.mk:3: Replacing \".\" with \".  \".",
                   2300:                "AUTOFIX: ~/filename.mk:4: Replacing \".\" with \".    \".",
                   2301:                "AUTOFIX: ~/filename.mk:5: Replacing \".\" with \".    \".",
                   2302:                "AUTOFIX: ~/filename.mk:6: Replacing \".\" with \".  \".")
                   2303:        t.CheckFileLines("filename.mk",
                   2304:                "# $"+"NetBSD$",
                   2305:                ".if defined(A)",
                   2306:                ".  for a in ${A}",
                   2307:                ".    if defined(C)",
                   2308:                ".    endif",
                   2309:                ".  endfor",
                   2310:                ".endif")
                   2311: }
1.37      rillig   2312:
1.48      rillig   2313: // Up to 2018-01-28, pkglint applied the autofix also to the continuation
                   2314: // lines, which is incorrect. It replaced the dot in "4.*" with spaces.
                   2315: func (s *Suite) Test_MkLineChecker_checkDirectiveIndentation__autofix_multiline(c *check.C) {
                   2316:        t := s.Init(c)
1.37      rillig   2317:
1.48      rillig   2318:        t.SetUpCommandLine("-Wall", "--autofix")
                   2319:        t.SetUpVartypes()
                   2320:        mklines := t.SetUpFileMkLines("options.mk",
                   2321:                MkCvsID,
                   2322:                ".if ${PKGNAME} == pkgname",
                   2323:                ".if \\",
                   2324:                "   ${PLATFORM:MNetBSD-4.*}",
                   2325:                ".endif",
                   2326:                ".endif")
1.20      rillig   2327:
1.48      rillig   2328:        mklines.Check()
1.35      rillig   2329:
1.48      rillig   2330:        t.CheckOutputLines(
                   2331:                "AUTOFIX: ~/options.mk:3: Replacing \".\" with \".  \".",
                   2332:                "AUTOFIX: ~/options.mk:5: Replacing \".\" with \".  \".")
1.35      rillig   2333:
1.48      rillig   2334:        t.CheckFileLines("options.mk",
                   2335:                MkCvsID,
                   2336:                ".if ${PKGNAME} == pkgname",
                   2337:                ".  if \\",
                   2338:                "   ${PLATFORM:MNetBSD-4.*}",
                   2339:                ".  endif",
                   2340:                ".endif")
                   2341: }
1.20      rillig   2342:
1.48      rillig   2343: func (s *Suite) Test_MkLineChecker_CheckRelativePath(c *check.C) {
                   2344:        t := s.Init(c)
1.35      rillig   2345:
1.48      rillig   2346:        t.SetUpPkgsrc()
                   2347:        t.CreateFileLines("wip/package/Makefile")
                   2348:        t.CreateFileLines("wip/package/module.mk")
                   2349:        mklines := t.SetUpFileMkLines("category/package/module.mk",
                   2350:                MkCvsID,
                   2351:                "DEPENDS+=       wip-package-[0-9]*:../../wip/package",
                   2352:                ".include \"../../wip/package/module.mk\"",
                   2353:                "",
                   2354:                "DEPENDS+=       unresolvable-[0-9]*:../../lang/${LATEST_PYTHON}",
                   2355:                ".include \"../../lang/${LATEST_PYTHON}/module.mk\"",
                   2356:                "",
                   2357:                ".include \"module.mk\"",
                   2358:                ".include \"../../category/../category/package/module.mk\"", // Oops
                   2359:                ".include \"../../mk/bsd.prefs.mk\"",
                   2360:                ".include \"../package/module.mk\"",
                   2361:                // TODO: warn about this as well, since ${.CURDIR} is essentially
                   2362:                //  equivalent to ".".
                   2363:                ".include \"${.CURDIR}/../package/module.mk\"")
                   2364:        t.FinishSetUp()
1.35      rillig   2365:
1.48      rillig   2366:        mklines.Check()
1.43      rillig   2367:
1.48      rillig   2368:        t.CheckOutputLines(
                   2369:                "ERROR: ~/category/package/module.mk:2: A main pkgsrc package must not depend on a pkgsrc-wip package.",
                   2370:                "ERROR: ~/category/package/module.mk:3: A main pkgsrc package must not depend on a pkgsrc-wip package.",
                   2371:                "WARN: ~/category/package/module.mk:5: LATEST_PYTHON is used but not defined.",
                   2372:                "WARN: ~/category/package/module.mk:11: References to other packages should "+
                   2373:                        "look like \"../../category/package\", not \"../package\".",
                   2374:                "WARN: ~/category/package/module.mk:12: References to other packages should "+
                   2375:                        "look like \"../../category/package\", not \"../package\".")
                   2376: }
1.43      rillig   2377:
1.48      rillig   2378: func (s *Suite) Test_MkLineChecker_CheckRelativePath__absolute_path(c *check.C) {
                   2379:        t := s.Init(c)
1.43      rillig   2380:
1.48      rillig   2381:        absDir := condStr(runtime.GOOS == "windows", "C:/", "/")
                   2382:        // Just a random UUID, to really guarantee that the file does not exist.
                   2383:        absPath := absDir + "0f5c2d56-8a7a-4c9d-9caa-859b52bbc8c7"
1.46      rillig   2384:
1.48      rillig   2385:        t.SetUpPkgsrc()
                   2386:        mklines := t.SetUpFileMkLines("category/package/module.mk",
                   2387:                MkCvsID,
                   2388:                "DISTINFO_FILE=\t"+absPath)
                   2389:        t.FinishSetUp()
1.46      rillig   2390:
1.48      rillig   2391:        mklines.Check()
1.46      rillig   2392:
1.48      rillig   2393:        t.CheckOutputLines(
                   2394:                "ERROR: ~/category/package/module.mk:2: The path \"" + absPath + "\" must be relative.")
1.20      rillig   2395: }
                   2396:
1.48      rillig   2397: func (s *Suite) Test_MkLineChecker_CheckRelativePath__include_if_exists(c *check.C) {
1.5       rillig   2398:        t := s.Init(c)
                   2399:
1.48      rillig   2400:        mklines := t.SetUpFileMkLines("filename.mk",
1.38      rillig   2401:                MkCvsID,
1.48      rillig   2402:                ".include \"included.mk\"",
                   2403:                ".sinclude \"included.mk\"")
1.1       rillig   2404:
1.28      rillig   2405:        mklines.Check()
1.1       rillig   2406:
1.48      rillig   2407:        // There is no warning for line 3 because of the "s" in "sinclude".
1.5       rillig   2408:        t.CheckOutputLines(
1.48      rillig   2409:                "ERROR: ~/filename.mk:2: Relative path \"included.mk\" does not exist.")
1.1       rillig   2410: }
                   2411:
1.48      rillig   2412: func (s *Suite) Test_MkLineChecker_CheckRelativePath__wip_mk(c *check.C) {
1.37      rillig   2413:        t := s.Init(c)
                   2414:
1.48      rillig   2415:        t.CreateFileLines("wip/mk/git-package.mk",
                   2416:                MkCvsID)
                   2417:        t.CreateFileLines("wip/other/version.mk",
                   2418:                MkCvsID)
                   2419:        t.SetUpPackage("wip/package",
                   2420:                ".include \"../mk/git-package.mk\"",
                   2421:                ".include \"../other/version.mk\"")
                   2422:        t.FinishSetUp()
1.37      rillig   2423:
1.48      rillig   2424:        G.Check(t.File("wip/package"))
1.37      rillig   2425:
1.48      rillig   2426:        t.CheckOutputLines(
                   2427:                "WARN: ~/wip/package/Makefile:20: References to the pkgsrc-wip "+
                   2428:                        "infrastructure should look like \"../../wip/mk\", not \"../mk\".",
                   2429:                "WARN: ~/wip/package/Makefile:21: References to other packages "+
                   2430:                        "should look like \"../../category/package\", not \"../package\".")
1.37      rillig   2431: }
                   2432:
1.48      rillig   2433: func (s *Suite) Test_MkLineChecker_CheckRelativePkgdir(c *check.C) {
1.5       rillig   2434:        t := s.Init(c)
                   2435:
1.48      rillig   2436:        t.CreateFileLines("other/package/Makefile")
1.1       rillig   2437:
1.49      rillig   2438:        test := func(relativePkgdir Path, diagnostics ...string) {
1.48      rillig   2439:                // Must be in the filesystem because of directory references.
                   2440:                mklines := t.SetUpFileMkLines("category/package/Makefile",
                   2441:                        "# dummy")
1.1       rillig   2442:
1.48      rillig   2443:                checkRelativePkgdir := func(mkline *MkLine) {
                   2444:                        MkLineChecker{mklines, mkline}.CheckRelativePkgdir(relativePkgdir)
                   2445:                }
1.1       rillig   2446:
1.48      rillig   2447:                mklines.ForEach(checkRelativePkgdir)
1.1       rillig   2448:
1.48      rillig   2449:                t.CheckOutput(diagnostics)
                   2450:        }
1.1       rillig   2451:
1.48      rillig   2452:        test("../pkgbase",
                   2453:                "ERROR: ~/category/package/Makefile:1: Relative path \"../pkgbase/Makefile\" does not exist.",
                   2454:                "WARN: ~/category/package/Makefile:1: \"../pkgbase\" is not a valid relative package directory.")
1.5       rillig   2455:
1.48      rillig   2456:        test("../../other/package",
                   2457:                nil...)
1.1       rillig   2458:
1.48      rillig   2459:        test("../../other/does-not-exist",
                   2460:                "ERROR: ~/category/package/Makefile:1: Relative path \"../../other/does-not-exist/Makefile\" does not exist.")
1.1       rillig   2461:
1.48      rillig   2462:        test("${OTHER_PACKAGE}",
                   2463:                nil...)
1.1       rillig   2464: }
1.6       rillig   2465:
1.48      rillig   2466: func (s *Suite) Test_MkLineChecker_checkDirective(c *check.C) {
                   2467:        t := s.Init(c)
                   2468:
                   2469:        t.SetUpVartypes()
1.21      rillig   2470:
1.48      rillig   2471:        mklines := t.NewMkLines("category/package/filename.mk",
1.38      rillig   2472:                MkCvsID,
1.48      rillig   2473:                "",
                   2474:                ".for",
                   2475:                ".endfor",
                   2476:                "",
                   2477:                ".if",
                   2478:                ".else don't",
                   2479:                ".endif invalid-arg",
                   2480:                "",
                   2481:                ".ifdef FNAME_MK",
                   2482:                ".endif",
                   2483:                ".ifndef FNAME_MK",
1.21      rillig   2484:                ".endif",
1.48      rillig   2485:                "",
                   2486:                ".for var in a b c",
1.21      rillig   2487:                ".endfor",
1.48      rillig   2488:                ".undef var unrelated")
1.21      rillig   2489:
                   2490:        mklines.Check()
                   2491:
                   2492:        t.CheckOutputLines(
1.48      rillig   2493:                "ERROR: category/package/filename.mk:3: \".for\" requires arguments.",
                   2494:                "ERROR: category/package/filename.mk:6: \".if\" requires arguments.",
                   2495:                "ERROR: category/package/filename.mk:7: \".else\" does not take arguments. "+
                   2496:                        "If you meant \"else if\", use \".elif\".",
                   2497:                "ERROR: category/package/filename.mk:8: \".endif\" does not take arguments.",
                   2498:                "WARN: category/package/filename.mk:10: The \".ifdef\" directive is deprecated. "+
                   2499:                        "Please use \".if defined(FNAME_MK)\" instead.",
                   2500:                "WARN: category/package/filename.mk:12: The \".ifndef\" directive is deprecated. "+
                   2501:                        "Please use \".if !defined(FNAME_MK)\" instead.",
                   2502:                "NOTE: category/package/filename.mk:17: Using \".undef\" after a \".for\" loop is unnecessary.")
1.21      rillig   2503: }
                   2504:
1.48      rillig   2505: func (s *Suite) Test_MkLineChecker_checkDirective__for_loop_varname(c *check.C) {
1.6       rillig   2506:        t := s.Init(c)
                   2507:
1.23      rillig   2508:        t.SetUpVartypes()
1.48      rillig   2509:
                   2510:        mklines := t.NewMkLines("filename.mk",
1.38      rillig   2511:                MkCvsID,
1.48      rillig   2512:                "",
                   2513:                ".for VAR in a b c", // Should be lowercase.
                   2514:                ".endfor",
                   2515:                "",
                   2516:                ".for _var_ in a b c", // Should be written without underscores.
                   2517:                ".endfor",
                   2518:                "",
                   2519:                ".for .var. in a b c", // Should be written without dots.
                   2520:                ".endfor",
                   2521:                "",
                   2522:                ".for ${VAR} in a b c", // The variable name really must be an identifier.
                   2523:                ".endfor")
1.6       rillig   2524:
                   2525:        mklines.Check()
                   2526:
                   2527:        t.CheckOutputLines(
1.48      rillig   2528:                "WARN: filename.mk:3: The variable name \"VAR\" in the .for loop should not contain uppercase letters.",
                   2529:                "WARN: filename.mk:6: Variable names starting with an underscore (_var_) are reserved for internal pkgsrc use.",
                   2530:                "ERROR: filename.mk:9: Invalid variable name \".var.\".",
                   2531:                "ERROR: filename.mk:12: Invalid variable name \"${VAR}\".")
1.6       rillig   2532: }
1.7       rillig   2533:
1.48      rillig   2534: func (s *Suite) Test_MkLineChecker_checkDirectiveEnd__ending_comments(c *check.C) {
1.7       rillig   2535:        t := s.Init(c)
                   2536:
1.23      rillig   2537:        t.SetUpVartypes()
1.48      rillig   2538:        mklines := t.NewMkLines("opsys.mk",
1.38      rillig   2539:                MkCvsID,
1.40      rillig   2540:                "",
1.48      rillig   2541:                ".for i in 1 2 3 4 5",
                   2542:                ".  if ${OPSYS} == NetBSD",
                   2543:                ".    if ${MACHINE_ARCH} == x86_64",
                   2544:                ".      if ${OS_VERSION:M8.*}",
                   2545:                ".      endif # MACHINE_ARCH", // Wrong, should be OS_VERSION.
                   2546:                ".    endif # OS_VERSION",     // Wrong, should be MACHINE_ARCH.
                   2547:                ".  endif # OPSYS",            // Correct.
                   2548:                ".endfor # j",                 // Wrong, should be i.
                   2549:                "",
                   2550:                ".if ${PKG_OPTIONS:Moption}",
                   2551:                ".endif # option", // Correct.
                   2552:                "",
                   2553:                ".if ${PKG_OPTIONS:Moption}",
                   2554:                ".endif # opti", // This typo goes unnoticed since "opti" is a substring of the condition.
                   2555:                "",
                   2556:                ".if ${OPSYS} == NetBSD",
                   2557:                ".elif ${OPSYS} == FreeBSD",
                   2558:                ".endif # NetBSD", // Wrong, should be FreeBSD from the .elif.
1.40      rillig   2559:                "",
1.48      rillig   2560:                ".for ii in 1 2",
                   2561:                ".  for jj in 1 2",
                   2562:                ".  endfor # ii", // Note: a simple "i" would not generate a warning because it is found in the word "in".
                   2563:                ".endfor # ii")
1.7       rillig   2564:
1.48      rillig   2565:        // See MkLineChecker.checkDirective
1.7       rillig   2566:        mklines.Check()
                   2567:
                   2568:        t.CheckOutputLines(
1.48      rillig   2569:                "WARN: opsys.mk:7: Comment \"MACHINE_ARCH\" does not match condition \"${OS_VERSION:M8.*}\".",
                   2570:                "WARN: opsys.mk:8: Comment \"OS_VERSION\" does not match condition \"${MACHINE_ARCH} == x86_64\".",
                   2571:                "WARN: opsys.mk:10: Comment \"j\" does not match loop \"i in 1 2 3 4 5\".",
                   2572:                "WARN: opsys.mk:12: Unknown option \"option\".",
                   2573:                "WARN: opsys.mk:20: Comment \"NetBSD\" does not match condition \"${OPSYS} == FreeBSD\".",
                   2574:                "WARN: opsys.mk:24: Comment \"ii\" does not match loop \"jj in 1 2\".")
1.7       rillig   2575: }
1.9       rillig   2576:
1.48      rillig   2577: // After removing the dummy indentation in commit d5a926af,
                   2578: // there was a panic: runtime error: index out of range,
                   2579: // in wip/jacorb-lib/buildlink3.mk.
                   2580: func (s *Suite) Test_MkLineChecker_checkDirectiveEnd__unbalanced(c *check.C) {
1.15      rillig   2581:        t := s.Init(c)
                   2582:
1.23      rillig   2583:        t.SetUpVartypes()
1.48      rillig   2584:        mklines := t.NewMkLines("filename.mk",
1.38      rillig   2585:                MkCvsID,
1.48      rillig   2586:                "",
                   2587:                ".endfor # comment",
                   2588:                ".endif # comment")
1.15      rillig   2589:
                   2590:        mklines.Check()
                   2591:
1.16      rillig   2592:        t.CheckOutputLines(
1.48      rillig   2593:                "ERROR: filename.mk:3: Unmatched .endfor.",
                   2594:                "ERROR: filename.mk:4: Unmatched .endif.")
1.15      rillig   2595: }
                   2596:
1.48      rillig   2597: func (s *Suite) Test_MkLineChecker_checkDirectiveCond(c *check.C) {
1.15      rillig   2598:        t := s.Init(c)
                   2599:
1.48      rillig   2600:        t.SetUpVartypes()
                   2601:
                   2602:        test := func(cond string, output ...string) {
                   2603:                mklines := t.NewMkLines("filename.mk",
                   2604:                        cond)
                   2605:                mklines.ForEach(func(mkline *MkLine) {
                   2606:                        MkLineChecker{mklines, mkline}.checkDirectiveCond()
                   2607:                })
                   2608:                t.CheckOutput(output)
                   2609:        }
                   2610:
                   2611:        test(
                   2612:                ".if !empty(PKGSRC_COMPILER:Mmycc)",
                   2613:                "WARN: filename.mk:1: The pattern \"mycc\" cannot match any of "+
                   2614:                        "{ ccache ccc clang distcc f2c gcc hp icc ido "+
                   2615:                        "mipspro mipspro-ucode pcc sunpro xlc } for PKGSRC_COMPILER.")
                   2616:
                   2617:        test(
                   2618:                ".elif ${A} != ${B}",
                   2619:                "WARN: filename.mk:1: A is used but not defined.",
                   2620:                "WARN: filename.mk:1: B is used but not defined.")
                   2621:
                   2622:        test(".if ${HOMEPAGE} == \"mailto:someone@example.org\"",
                   2623:                "WARN: filename.mk:1: \"mailto:someone@example.org\" is not a valid URL.",
                   2624:                "WARN: filename.mk:1: HOMEPAGE should not be used at load time in any file.")
                   2625:
                   2626:        test(".if !empty(PKGSRC_RUN_TEST:M[Y][eE][sS])",
                   2627:                "WARN: filename.mk:1: PKGSRC_RUN_TEST should be matched "+
                   2628:                        "against \"[yY][eE][sS]\" or \"[nN][oO]\", not \"[Y][eE][sS]\".")
                   2629:
                   2630:        test(".if !empty(IS_BUILTIN.Xfixes:M[yY][eE][sS])")
                   2631:
                   2632:        test(".if !empty(${IS_BUILTIN.Xfixes:M[yY][eE][sS]})",
                   2633:                "WARN: filename.mk:1: The empty() function takes a variable name as parameter, "+
                   2634:                        "not a variable expression.")
                   2635:
                   2636:        test(".if ${PKGSRC_COMPILER} == \"msvc\"",
                   2637:                "WARN: filename.mk:1: \"msvc\" is not valid for PKGSRC_COMPILER. "+
                   2638:                        "Use one of { ccache ccc clang distcc f2c gcc hp icc ido mipspro mipspro-ucode pcc sunpro xlc } instead.",
1.49      rillig   2639:                "ERROR: filename.mk:1: Use ${PKGSRC_COMPILER:Mmsvc} instead of the == operator.")
1.48      rillig   2640:
                   2641:        test(".if ${PKG_LIBTOOL:Mlibtool}",
                   2642:                "NOTE: filename.mk:1: PKG_LIBTOOL should be compared using == instead of matching against \":Mlibtool\".",
                   2643:                "WARN: filename.mk:1: PKG_LIBTOOL should not be used at load time in any file.")
1.15      rillig   2644:
1.48      rillig   2645:        test(".if ${MACHINE_PLATFORM:MUnknownOS-*-*} || ${MACHINE_ARCH:Mx86}",
                   2646:                "WARN: filename.mk:1: "+
                   2647:                        "The pattern \"UnknownOS\" cannot match any of "+
                   2648:                        "{ AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD HPUX Haiku "+
                   2649:                        "IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare "+
                   2650:                        "} for the operating system part of MACHINE_PLATFORM.",
                   2651:                "WARN: filename.mk:1: "+
                   2652:                        "The pattern \"x86\" cannot match any of "+
                   2653:                        "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 cobalt coldfire convex dreamcast earm "+
                   2654:                        "earmeb earmhf earmhfeb earmv4 earmv4eb earmv5 earmv5eb earmv6 earmv6eb earmv6hf earmv6hfeb "+
                   2655:                        "earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 i386 i586 i686 ia64 "+
                   2656:                        "m68000 m68k m88k mips mips64 mips64eb mips64el mipseb mipsel mipsn32 mlrisc ns32k pc532 pmax "+
                   2657:                        "powerpc powerpc64 rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+
                   2658:                        "} for MACHINE_ARCH.",
                   2659:                "NOTE: filename.mk:1: MACHINE_ARCH should be compared using == instead of matching against \":Mx86\".")
1.15      rillig   2660:
1.48      rillig   2661:        // Doesn't occur in practice since it is surprising that the ! applies
                   2662:        // to the comparison operator, and not to one of its arguments.
                   2663:        test(".if !${VAR} == value",
                   2664:                "WARN: filename.mk:1: VAR is used but not defined.")
1.15      rillig   2665:
1.48      rillig   2666:        // Doesn't occur in practice since this string can never be empty.
                   2667:        test(".if !\"${VAR}str\"",
                   2668:                "WARN: filename.mk:1: VAR is used but not defined.")
1.15      rillig   2669:
1.48      rillig   2670:        // Doesn't occur in practice since !${VAR} && !${VAR2} is more idiomatic.
                   2671:        test(".if !\"${VAR}${VAR2}\"",
                   2672:                "WARN: filename.mk:1: VAR is used but not defined.",
                   2673:                "WARN: filename.mk:1: VAR2 is used but not defined.")
1.15      rillig   2674:
1.48      rillig   2675:        // Just for code coverage; always evaluates to true.
                   2676:        test(".if \"string\"",
                   2677:                nil...)
1.15      rillig   2678:
1.48      rillig   2679:        // Code coverage for checkVar.
                   2680:        test(".if ${OPSYS} || ${MACHINE_ARCH}",
                   2681:                nil...)
1.15      rillig   2682:
1.48      rillig   2683:        test(".if ${VAR}",
                   2684:                "WARN: filename.mk:1: VAR is used but not defined.")
1.38      rillig   2685:
1.48      rillig   2686:        test(".if ${VAR} == 3",
                   2687:                "WARN: filename.mk:1: VAR is used but not defined.")
1.38      rillig   2688:
1.48      rillig   2689:        test(".if \"value\" == ${VAR}",
                   2690:                "WARN: filename.mk:1: VAR is used but not defined.")
1.38      rillig   2691:
1.48      rillig   2692:        test(".if ${MASTER_SITES:Mftp://*} == \"ftp://netbsd.org/\"",
1.49      rillig   2693:                // FIXME: duplicate diagnostic, see MkParser.MkCond.
                   2694:                "WARN: filename.mk:1: Invalid variable modifier \"//*\" for \"MASTER_SITES\".",
1.48      rillig   2695:                "WARN: filename.mk:1: Invalid variable modifier \"//*\" for \"MASTER_SITES\".",
                   2696:                "WARN: filename.mk:1: \"ftp\" is not a valid URL.",
1.49      rillig   2697:                "WARN: filename.mk:1: MASTER_SITES should not be used at load time in any file.")
1.38      rillig   2698: }
                   2699:
1.48      rillig   2700: func (s *Suite) Test_MkLineChecker_checkDirectiveCond__tracing(c *check.C) {
1.38      rillig   2701:        t := s.Init(c)
                   2702:
1.48      rillig   2703:        t.EnableTracingToLog()
                   2704:        mklines := t.NewMkLines("filename.mk",
                   2705:                ".if ${VAR:Mpattern1:Mpattern2} == comparison")
1.38      rillig   2706:
1.48      rillig   2707:        mklines.ForEach(func(mkline *MkLine) {
                   2708:                MkLineChecker{mklines, mkline}.checkDirectiveCond()
                   2709:        })
1.38      rillig   2710:
1.48      rillig   2711:        t.CheckOutputLinesMatching(`^WARN|checkCompare`,
                   2712:                "TRACE: 1   checkCompareVarStr ${VAR:Mpattern1:Mpattern2} == comparison",
                   2713:                "WARN: filename.mk:1: VAR is used but not defined.")
1.38      rillig   2714: }
                   2715:
1.48      rillig   2716: func (s *Suite) Test_MkLineChecker_checkDirectiveCond__comparison_with_shell_command(c *check.C) {
1.38      rillig   2717:        t := s.Init(c)
                   2718:
1.48      rillig   2719:        t.SetUpVartypes()
                   2720:        mklines := t.NewMkLines("security/openssl/Makefile",
                   2721:                MkCvsID,
                   2722:                ".if ${PKGSRC_COMPILER} == \"gcc\" && ${CC} == \"cc\"",
                   2723:                ".endif")
1.38      rillig   2724:
1.48      rillig   2725:        mklines.Check()
1.38      rillig   2726:
1.48      rillig   2727:        // Don't warn about unknown shell command "cc".
1.38      rillig   2728:        t.CheckOutputLines(
1.49      rillig   2729:                "ERROR: security/openssl/Makefile:2: Use ${PKGSRC_COMPILER:Mgcc} instead of the == operator.")
1.38      rillig   2730: }
                   2731:
1.48      rillig   2732: // The :N modifier filters unwanted values. After this filter, any variable value
                   2733: // may be compared with the empty string, regardless of the variable type.
                   2734: // Effectively, the :N modifier changes the type from T to Option(T).
                   2735: func (s *Suite) Test_MkLineChecker_checkDirectiveCond__compare_pattern_with_empty(c *check.C) {
1.38      rillig   2736:        t := s.Init(c)
                   2737:
1.48      rillig   2738:        t.SetUpVartypes()
                   2739:        mklines := t.NewMkLines("filename.mk",
                   2740:                MkCvsID,
                   2741:                ".if ${X11BASE:Npattern} == \"\"",
                   2742:                ".endif",
                   2743:                "",
                   2744:                ".if ${X11BASE:N<>} == \"*\"",
                   2745:                ".endif",
1.38      rillig   2746:                "",
1.48      rillig   2747:                ".if !(${OPSYS:M*BSD} != \"\")",
                   2748:                ".endif")
1.38      rillig   2749:
1.48      rillig   2750:        mklines.Check()
1.38      rillig   2751:
1.48      rillig   2752:        // TODO: There should be a warning about "<>" containing invalid
                   2753:        //  characters for a path. See VartypeCheck.Pathname
1.38      rillig   2754:        t.CheckOutputLines(
1.48      rillig   2755:                "WARN: filename.mk:5: The pathname pattern \"<>\" contains the invalid characters \"<>\".",
                   2756:                "WARN: filename.mk:5: The pathname \"*\" contains the invalid character \"*\".")
1.38      rillig   2757: }
                   2758:
1.48      rillig   2759: func (s *Suite) Test_MkLineChecker_checkDirectiveCond__comparing_PKGSRC_COMPILER_with_eqeq(c *check.C) {
1.9       rillig   2760:        t := s.Init(c)
                   2761:
1.23      rillig   2762:        t.SetUpVartypes()
1.48      rillig   2763:        mklines := t.NewMkLines("Makefile",
1.38      rillig   2764:                MkCvsID,
1.48      rillig   2765:                ".if ${PKGSRC_COMPILER} == \"clang\"",
                   2766:                ".elif ${PKGSRC_COMPILER} != \"gcc\"",
                   2767:                ".endif")
1.9       rillig   2768:
                   2769:        mklines.Check()
                   2770:
                   2771:        t.CheckOutputLines(
1.49      rillig   2772:                "ERROR: Makefile:2: Use ${PKGSRC_COMPILER:Mclang} instead of the == operator.",
                   2773:                "ERROR: Makefile:3: Use ${PKGSRC_COMPILER:Ngcc} instead of the != operator.")
1.9       rillig   2774: }
1.14      rillig   2775:
1.48      rillig   2776: func (s *Suite) Test_MkLineChecker_checkDirectiveCondEmpty(c *check.C) {
1.14      rillig   2777:        t := s.Init(c)
                   2778:
1.23      rillig   2779:        t.SetUpVartypes()
1.48      rillig   2780:        t.Chdir(".")
                   2781:
                   2782:        test := func(before string, diagnosticsAndAfter ...string) {
                   2783:
                   2784:                mklines := t.SetUpFileMkLines("module.mk",
                   2785:                        MkCvsID,
                   2786:                        before,
                   2787:                        ".endif")
                   2788:                ck := MkLineChecker{mklines, mklines.mklines[1]}
                   2789:
                   2790:                t.SetUpCommandLine("-Wall")
                   2791:                mklines.ForEach(func(mkline *MkLine) {
                   2792:                        if mkline == mklines.mklines[1] {
                   2793:                                ck.checkDirectiveCond()
                   2794:                        }
                   2795:                })
                   2796:
                   2797:                t.SetUpCommandLine("-Wall", "--autofix")
                   2798:                mklines.ForEach(func(mkline *MkLine) {
                   2799:                        if mkline == mklines.mklines[1] {
                   2800:                                ck.checkDirectiveCond()
                   2801:                        }
                   2802:                })
                   2803:
                   2804:                mklines.SaveAutofixChanges()
                   2805:                afterMklines := t.LoadMkInclude("module.mk")
                   2806:
                   2807:                if len(diagnosticsAndAfter) > 0 {
                   2808:                        diagLen := len(diagnosticsAndAfter)
                   2809:                        diagnostics := diagnosticsAndAfter[:diagLen-1]
                   2810:                        after := diagnosticsAndAfter[diagLen-1]
                   2811:
                   2812:                        t.CheckOutput(diagnostics)
                   2813:                        t.CheckEquals(afterMklines.mklines[1].Text, after)
                   2814:                } else {
                   2815:                        t.CheckOutputEmpty()
                   2816:                }
                   2817:        }
                   2818:
                   2819:        test(
                   2820:                ".if ${PKGPATH:Mpattern}",
                   2821:
                   2822:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mpattern\".",
                   2823:                "AUTOFIX: module.mk:2: Replacing \"${PKGPATH:Mpattern}\" with \"${PKGPATH} == pattern\".",
                   2824:
                   2825:                ".if ${PKGPATH} == pattern")
                   2826:
                   2827:        // When the pattern contains placeholders, it cannot be converted to == or !=.
                   2828:        test(
                   2829:                ".if ${PKGPATH:Mpa*n}",
                   2830:                nil...)
                   2831:
                   2832:        // The :tl modifier prevents the autofix.
                   2833:        test(
                   2834:                ".if ${PKGPATH:tl:Mpattern}",
                   2835:
                   2836:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mpattern\".",
                   2837:
                   2838:                ".if ${PKGPATH:tl:Mpattern}")
1.14      rillig   2839:
1.48      rillig   2840:        test(
                   2841:                ".if ${PKGPATH:Ncategory/package}",
1.14      rillig   2842:
1.48      rillig   2843:                "NOTE: module.mk:2: PKGPATH should be compared using != instead of matching against \":Ncategory/package\".",
                   2844:                "AUTOFIX: module.mk:2: Replacing \"${PKGPATH:Ncategory/package}\" with \"${PKGPATH} != category/package\".",
1.14      rillig   2845:
1.48      rillig   2846:                ".if ${PKGPATH} != category/package")
1.19      rillig   2847:
1.48      rillig   2848:        // ${PKGPATH:None:Ntwo} is a short variant of ${PKGPATH} != "one" &&
                   2849:        // ${PKGPATH} != "two". Applying the transformation would make the
                   2850:        // condition longer than before, therefore nothing is done here.
                   2851:        test(
                   2852:                ".if ${PKGPATH:None:Ntwo}",
                   2853:                nil...)
1.19      rillig   2854:
1.48      rillig   2855:        // Note: this combination doesn't make sense since the patterns "one" and "two" don't overlap.
                   2856:        test(".if ${PKGPATH:Mone:Mtwo}",
1.37      rillig   2857:
1.48      rillig   2858:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mone\".",
                   2859:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mtwo\".",
1.19      rillig   2860:
1.48      rillig   2861:                ".if ${PKGPATH:Mone:Mtwo}")
1.19      rillig   2862:
1.48      rillig   2863:        test(".if !empty(PKGPATH:Mpattern)",
1.19      rillig   2864:
1.48      rillig   2865:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mpattern\".",
                   2866:                "AUTOFIX: module.mk:2: Replacing \"!empty(PKGPATH:Mpattern)\" with \"${PKGPATH} == pattern\".",
1.16      rillig   2867:
1.48      rillig   2868:                ".if ${PKGPATH} == pattern")
1.16      rillig   2869:
1.48      rillig   2870:        test(".if empty(PKGPATH:Mpattern)",
1.16      rillig   2871:
1.48      rillig   2872:                "NOTE: module.mk:2: PKGPATH should be compared using != instead of matching against \":Mpattern\".",
                   2873:                "AUTOFIX: module.mk:2: Replacing \"empty(PKGPATH:Mpattern)\" with \"${PKGPATH} != pattern\".",
1.16      rillig   2874:
1.48      rillig   2875:                ".if ${PKGPATH} != pattern")
1.14      rillig   2876:
1.48      rillig   2877:        test(".if !!empty(PKGPATH:Mpattern)",
1.14      rillig   2878:
1.48      rillig   2879:                // TODO: When taking all the ! into account, this is actually a
                   2880:                //  test for emptiness, therefore the diagnostics should suggest
                   2881:                //  the != operator instead of ==.
                   2882:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mpattern\".",
                   2883:                "AUTOFIX: module.mk:2: Replacing \"!empty(PKGPATH:Mpattern)\" with \"${PKGPATH} == pattern\".",
1.14      rillig   2884:
1.48      rillig   2885:                // TODO: The ! and == could be combined into a !=.
                   2886:                //  Luckily the !! pattern doesn't occur in practice.
                   2887:                ".if !${PKGPATH} == pattern")
1.14      rillig   2888:
1.48      rillig   2889:        test(".if empty(PKGPATH:Mpattern) || 0",
1.14      rillig   2890:
1.48      rillig   2891:                "NOTE: module.mk:2: PKGPATH should be compared using != instead of matching against \":Mpattern\".",
                   2892:                "AUTOFIX: module.mk:2: Replacing \"empty(PKGPATH:Mpattern)\" with \"${PKGPATH} != pattern\".",
1.25      rillig   2893:
1.48      rillig   2894:                ".if ${PKGPATH} != pattern || 0")
1.25      rillig   2895:
1.48      rillig   2896:        // No note in this case since there is no implicit !empty around the varUse.
                   2897:        test(".if ${PKGPATH:Mpattern} != ${OTHER}",
1.25      rillig   2898:
1.48      rillig   2899:                "WARN: module.mk:2: OTHER is used but not defined.",
1.28      rillig   2900:
1.48      rillig   2901:                ".if ${PKGPATH:Mpattern} != ${OTHER}")
1.25      rillig   2902:
1.48      rillig   2903:        test(
                   2904:                ".if ${PKGPATH:Mpattern}",
1.25      rillig   2905:
1.48      rillig   2906:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mpattern\".",
                   2907:                "AUTOFIX: module.mk:2: Replacing \"${PKGPATH:Mpattern}\" with \"${PKGPATH} == pattern\".",
1.25      rillig   2908:
1.48      rillig   2909:                ".if ${PKGPATH} == pattern")
1.25      rillig   2910:
1.48      rillig   2911:        test(
                   2912:                ".if !${PKGPATH:Mpattern}",
1.25      rillig   2913:
1.48      rillig   2914:                "NOTE: module.mk:2: PKGPATH should be compared using != instead of matching against \":Mpattern\".",
                   2915:                "AUTOFIX: module.mk:2: Replacing \"!${PKGPATH:Mpattern}\" with \"${PKGPATH} != pattern\".",
1.25      rillig   2916:
1.48      rillig   2917:                ".if ${PKGPATH} != pattern")
1.25      rillig   2918:
1.48      rillig   2919:        test(
                   2920:                ".if !!${PKGPATH:Mpattern}",
1.25      rillig   2921:
1.48      rillig   2922:                "NOTE: module.mk:2: PKGPATH should be compared using != instead of matching against \":Mpattern\".",
                   2923:                "AUTOFIX: module.mk:2: Replacing \"!${PKGPATH:Mpattern}\" with \"${PKGPATH} != pattern\".",
1.25      rillig   2924:
1.48      rillig   2925:                ".if !${PKGPATH} != pattern")
1.17      rillig   2926:
1.48      rillig   2927:        // This pattern with spaces doesn't make sense at all in the :M
                   2928:        // modifier since it can never match.
                   2929:        // Or can it, if the PKGPATH contains quotes?
                   2930:        // How exactly does bmake apply the matching here, are both values unquoted?
                   2931:        test(
                   2932:                ".if ${PKGPATH:Mpattern with spaces}",
1.17      rillig   2933:
1.48      rillig   2934:                "WARN: module.mk:2: The pathname pattern \"pattern with spaces\" "+
                   2935:                        "contains the invalid characters \"  \".",
1.17      rillig   2936:
1.48      rillig   2937:                ".if ${PKGPATH:Mpattern with spaces}")
                   2938:        // TODO: ".if ${PKGPATH} == \"pattern with spaces\"")
1.25      rillig   2939:
1.48      rillig   2940:        test(
                   2941:                ".if ${PKGPATH:M'pattern with spaces'}",
1.25      rillig   2942:
1.48      rillig   2943:                "WARN: module.mk:2: The pathname pattern \"'pattern with spaces'\" "+
                   2944:                        "contains the invalid characters \"'  '\".",
1.17      rillig   2945:
1.48      rillig   2946:                ".if ${PKGPATH:M'pattern with spaces'}")
                   2947:        // TODO: ".if ${PKGPATH} == 'pattern with spaces'")
1.19      rillig   2948:
1.48      rillig   2949:        test(
                   2950:                ".if ${PKGPATH:M&&}",
1.19      rillig   2951:
1.48      rillig   2952:                "WARN: module.mk:2: The pathname pattern \"&&\" "+
                   2953:                        "contains the invalid characters \"&&\".",
1.19      rillig   2954:
1.48      rillig   2955:                ".if ${PKGPATH:M&&}")
                   2956:        // TODO: ".if ${PKGPATH} == '&&'")
1.19      rillig   2957:
1.48      rillig   2958:        // If PKGPATH is "", the condition is false.
                   2959:        // If PKGPATH is "negative-pattern", the condition is false.
                   2960:        // In all other cases, the condition is true.
                   2961:        //
                   2962:        // Therefore this condition cannot simply be transformed into
                   2963:        // ${PKGPATH} != negative-pattern, since that would produce a
                   2964:        // different result in the case where PKGPATH is empty.
                   2965:        //
                   2966:        // For system-provided variables that are guaranteed to be non-empty,
                   2967:        // such as OPSYS or PKGPATH, this replacement is valid.
                   2968:        // These variables are only guaranteed to be defined after bsd.prefs.mk
                   2969:        // has been included, like everywhere else.
                   2970:        test(
                   2971:                ".if ${PKGPATH:Nnegative-pattern}",
1.19      rillig   2972:
1.48      rillig   2973:                "NOTE: module.mk:2: PKGPATH should be compared using != instead of matching against \":Nnegative-pattern\".",
                   2974:                "AUTOFIX: module.mk:2: Replacing \"${PKGPATH:Nnegative-pattern}\" with \"${PKGPATH} != negative-pattern\".",
1.32      rillig   2975:
1.48      rillig   2976:                ".if ${PKGPATH} != negative-pattern")
1.32      rillig   2977:
1.48      rillig   2978:        // Since UNKNOWN is not a well-known system-provided variable that is
                   2979:        // guaranteed to be non-empty (see the previous example), it is not
                   2980:        // transformed at all.
                   2981:        test(
                   2982:                ".if ${UNKNOWN:Nnegative-pattern}",
1.32      rillig   2983:
1.48      rillig   2984:                "WARN: module.mk:2: UNKNOWN is used but not defined.",
1.32      rillig   2985:
1.48      rillig   2986:                ".if ${UNKNOWN:Nnegative-pattern}")
1.32      rillig   2987:
1.48      rillig   2988:        test(
                   2989:                ".if ${PKGPATH:Mpath1} || ${PKGPATH:Mpath2}",
1.21      rillig   2990:
1.48      rillig   2991:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mpath1\".",
                   2992:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mpath2\".",
                   2993:                "AUTOFIX: module.mk:2: Replacing \"${PKGPATH:Mpath1}\" with \"${PKGPATH} == path1\".",
                   2994:                "AUTOFIX: module.mk:2: Replacing \"${PKGPATH:Mpath2}\" with \"${PKGPATH} == path2\".",
1.21      rillig   2995:
1.48      rillig   2996:                ".if ${PKGPATH} == path1 || ${PKGPATH} == path2")
1.21      rillig   2997:
1.48      rillig   2998:        test(
                   2999:                ".if (((((${PKGPATH:Mpath})))))",
1.21      rillig   3000:
1.48      rillig   3001:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mpath\".",
                   3002:                "AUTOFIX: module.mk:2: Replacing \"${PKGPATH:Mpath}\" with \"${PKGPATH} == path\".",
1.32      rillig   3003:
1.48      rillig   3004:                ".if (((((${PKGPATH} == path)))))")
1.32      rillig   3005:
1.48      rillig   3006:        // Note: this combination doesn't make sense since the patterns "one" and "two" don't overlap.
                   3007:        test(
                   3008:                ".if ${PKGPATH:Mone:Mtwo}",
1.32      rillig   3009:
1.48      rillig   3010:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mone\".",
                   3011:                "NOTE: module.mk:2: PKGPATH should be compared using == instead of matching against \":Mtwo\".",
1.32      rillig   3012:
1.48      rillig   3013:                ".if ${PKGPATH:Mone:Mtwo}")
1.32      rillig   3014:
1.48      rillig   3015:        test(
                   3016:                ".if ${MACHINE_ARCH:Mx86_64}",
1.14      rillig   3017:
1.48      rillig   3018:                "NOTE: module.mk:2: MACHINE_ARCH should be compared using == instead of matching against \":Mx86_64\".",
                   3019:                "AUTOFIX: module.mk:2: Replacing \"${MACHINE_ARCH:Mx86_64}\" with \"${MACHINE_ARCH} == x86_64\".",
1.14      rillig   3020:
1.48      rillig   3021:                ".if ${MACHINE_ARCH} == x86_64")
1.27      rillig   3022: }
                   3023:
1.48      rillig   3024: func (s *Suite) Test_MkLineChecker_checkDirectiveCondCompare(c *check.C) {
1.27      rillig   3025:        t := s.Init(c)
                   3026:
1.48      rillig   3027:        t.SetUpVartypes()
1.27      rillig   3028:
1.48      rillig   3029:        test := func(cond string, output ...string) {
                   3030:                mklines := t.NewMkLines("filename.mk",
                   3031:                        cond)
                   3032:                mklines.ForEach(func(mkline *MkLine) {
                   3033:                        MkLineChecker{mklines, mkline}.checkDirectiveCond()
                   3034:                })
                   3035:                t.CheckOutput(output)
                   3036:        }
1.27      rillig   3037:
1.48      rillig   3038:        // As of July 2019, pkglint doesn't have specific checks for comparing
                   3039:        // variables to numbers.
                   3040:        test(".if ${VAR} > 0",
                   3041:                "WARN: filename.mk:1: VAR is used but not defined.")
1.14      rillig   3042:
1.48      rillig   3043:        // For string comparisons, the checks from vartypecheck.go are
                   3044:        // performed.
                   3045:        test(".if ${DISTNAME} == \"<>\"",
                   3046:                "WARN: filename.mk:1: The filename \"<>\" contains the invalid characters \"<>\".",
                   3047:                "WARN: filename.mk:1: DISTNAME should not be used at load time in any file.")
1.14      rillig   3048:
1.48      rillig   3049:        // This type of comparison doesn't occur in practice since it is
                   3050:        // overly verbose.
                   3051:        test(".if \"${BUILD_DIRS}str\" == \"str\"",
                   3052:                // TODO: why should it not be used? In a .for loop it sounds pretty normal.
                   3053:                "WARN: filename.mk:1: BUILD_DIRS should not be used at load time in any file.")
1.14      rillig   3054:
1.48      rillig   3055:        // This is a shorthand for defined(VAR), but it is not used in practice.
                   3056:        test(".if VAR",
                   3057:                "WARN: filename.mk:1: Invalid condition, unrecognized part: \"VAR\".")
1.14      rillig   3058:
1.48      rillig   3059:        // Calling a function with braces instead of parentheses is syntactically
                   3060:        // invalid. Pkglint is stricter than bmake in this situation.
                   3061:        //
                   3062:        // Bmake reads the "empty{VAR}" as a variable name. It then checks whether
                   3063:        // this variable is defined. It is not, of course, therefore the expression
                   3064:        // is false. The ! in front of it negates this false, which makes the whole
                   3065:        // condition true.
                   3066:        //
                   3067:        // See https://mail-index.netbsd.org/tech-pkg/2019/07/07/msg021539.html
                   3068:        test(".if !empty{VAR}",
                   3069:                "WARN: filename.mk:1: Invalid condition, unrecognized part: \"empty{VAR}\".")
1.14      rillig   3070: }
                   3071:
1.48      rillig   3072: func (s *Suite) Test_MkLineChecker_checkDirectiveCondCompareVarStr__no_tracing(c *check.C) {
1.19      rillig   3073:        t := s.Init(c)
1.48      rillig   3074:        b := NewMkTokenBuilder()
1.19      rillig   3075:
1.48      rillig   3076:        t.SetUpVartypes()
                   3077:        mklines := t.NewMkLines("filename.mk",
                   3078:                ".if ${DISTFILES:Mpattern:O:u} == NetBSD")
                   3079:        t.DisableTracing()
1.19      rillig   3080:
1.48      rillig   3081:        ck := MkLineChecker{mklines, mklines.mklines[0]}
                   3082:        varUse := b.VarUse("DISTFILES", "Mpattern", "O", "u")
                   3083:        ck.checkDirectiveCondCompareVarStr(varUse, "==", "distfile-1.0.tar.gz")
1.19      rillig   3084:
1.48      rillig   3085:        t.CheckOutputEmpty()
1.19      rillig   3086: }
                   3087:
1.49      rillig   3088: func (s *Suite) Test_MkLineChecker_checkCompareVarStrCompiler(c *check.C) {
                   3089:        t := s.Init(c)
                   3090:
                   3091:        t.SetUpVartypes()
                   3092:        t.Chdir(".")
                   3093:
                   3094:        test := func(cond string, diagnostics ...string) {
                   3095:                mklines := t.SetUpFileMkLines("filename.mk",
                   3096:                        MkCvsID,
                   3097:                        "",
                   3098:                        ".if "+cond,
                   3099:                        ".endif")
                   3100:
                   3101:                t.SetUpCommandLine("-Wall")
                   3102:                mklines.Check()
                   3103:                t.SetUpCommandLine("-Wall", "--autofix")
                   3104:                mklines.Check()
                   3105:
                   3106:                t.CheckOutput(diagnostics)
                   3107:        }
                   3108:
                   3109:        test(
                   3110:                "${PKGSRC_COMPILER} == gcc",
                   3111:
                   3112:                "ERROR: filename.mk:3: "+
                   3113:                        "Use ${PKGSRC_COMPILER:Mgcc} instead of the == operator.",
                   3114:                "AUTOFIX: filename.mk:3: "+
                   3115:                        "Replacing \"${PKGSRC_COMPILER} == gcc\" "+
                   3116:                        "with \"${PKGSRC_COMPILER:Mgcc}\".")
                   3117:
                   3118:        // No autofix because of missing whitespace.
                   3119:        // TODO: Provide the autofix regardless of the whitespace.
                   3120:        test(
                   3121:                "${PKGSRC_COMPILER}==gcc",
                   3122:
                   3123:                "ERROR: filename.mk:3: "+
                   3124:                        "Use ${PKGSRC_COMPILER:Mgcc} instead of the == operator.")
                   3125:
                   3126:        // The comparison value can be with or without quotes.
                   3127:        test(
                   3128:                "${PKGSRC_COMPILER} == \"gcc\"",
                   3129:
                   3130:                "ERROR: filename.mk:3: "+
                   3131:                        "Use ${PKGSRC_COMPILER:Mgcc} instead of the == operator.",
                   3132:                "AUTOFIX: filename.mk:3: "+
                   3133:                        "Replacing \"${PKGSRC_COMPILER} == \\\"gcc\\\"\" "+
                   3134:                        "with \"${PKGSRC_COMPILER:Mgcc}\".")
                   3135:
                   3136:        // No warning because it is not obvious what is meant here.
                   3137:        // This case probably doesn't occur in practice.
                   3138:        test(
                   3139:                "${PKGSRC_COMPILER} == \"distcc gcc\"",
                   3140:
                   3141:                nil...)
                   3142: }
                   3143:
1.48      rillig   3144: func (s *Suite) Test_MkLineChecker_checkDirectiveFor(c *check.C) {
1.14      rillig   3145:        t := s.Init(c)
                   3146:
1.48      rillig   3147:        t.SetUpVartypes()
                   3148:        mklines := t.NewMkLines("for.mk",
1.38      rillig   3149:                MkCvsID,
1.48      rillig   3150:                ".for dir in ${PATH:C,:, ,g}",
                   3151:                ".endfor",
1.14      rillig   3152:                "",
1.48      rillig   3153:                ".for dir in ${PATH}",
                   3154:                ".endfor",
1.14      rillig   3155:                "",
1.48      rillig   3156:                ".for dir in ${PATH:M*/bin}",
                   3157:                ".endfor")
1.14      rillig   3158:
                   3159:        mklines.Check()
                   3160:
                   3161:        t.CheckOutputLines(
1.48      rillig   3162:                // No warning about a missing :Q in line 2 since the :C modifier
                   3163:                // converts the colon-separated list into a space-separated list,
                   3164:                // as required by the .for loop.
                   3165:
                   3166:                // This warning is correct since PATH is separated by colons, not by spaces.
                   3167:                "WARN: for.mk:5: Please use ${PATH:Q} instead of ${PATH}.",
                   3168:
                   3169:                // This warning is also correct since the :M modifier doesn't change the
                   3170:                // word boundaries.
                   3171:                "WARN: for.mk:8: Please use ${PATH:M*/bin:Q} instead of ${PATH:M*/bin}.")
1.14      rillig   3172: }
1.27      rillig   3173:
1.48      rillig   3174: func (s *Suite) Test_MkLineChecker_checkDirectiveFor__infrastructure(c *check.C) {
1.27      rillig   3175:        t := s.Init(c)
                   3176:
                   3177:        t.SetUpPkgsrc()
1.48      rillig   3178:        t.CreateFileLines("mk/file.mk",
1.38      rillig   3179:                MkCvsID,
1.48      rillig   3180:                ".for i = 1 2 3", // The "=" should rather be "in".
                   3181:                ".endfor",
                   3182:                "",
                   3183:                ".for _i_ in 1 2 3", // Underscores are only allowed in infrastructure files.
                   3184:                ".endfor")
1.30      rillig   3185:        t.FinishSetUp()
1.27      rillig   3186:
1.48      rillig   3187:        G.Check(t.File("mk/file.mk"))
1.27      rillig   3188:
1.48      rillig   3189:        // Pkglint doesn't care about trivial syntax errors like the "=" instead
                   3190:        // of "in" above; bmake will already catch these.
                   3191:        t.CheckOutputEmpty()
1.27      rillig   3192: }
                   3193:
1.48      rillig   3194: func (s *Suite) Test_MkLineChecker_checkDependencyRule(c *check.C) {
1.27      rillig   3195:        t := s.Init(c)
                   3196:
1.48      rillig   3197:        t.SetUpVartypes()
                   3198:
                   3199:        mklines := t.NewMkLines("category/package/filename.mk",
1.38      rillig   3200:                MkCvsID,
1.48      rillig   3201:                "",
                   3202:                ".PHONY: target-1",
                   3203:                "target-2: .PHONY",
                   3204:                ".ORDER: target-1 target-2",
                   3205:                "target-1:",
                   3206:                "target-2:",
                   3207:                "target-3:",
                   3208:                "${_COOKIE.test}:")
1.27      rillig   3209:
                   3210:        mklines.Check()
                   3211:
                   3212:        t.CheckOutputLines(
1.48      rillig   3213:                "WARN: category/package/filename.mk:8: Undeclared target \"target-3\".")
1.27      rillig   3214: }

CVSweb <webmaster@jp.NetBSD.org>