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

Annotation of pkgsrc/pkgtools/pkglint/files/mkvarusechecker.go, Revision 1.2

1.1       rillig      1: package pkglint
                      2:
                      3: import "strings"
                      4:
                      5: type MkVarUseChecker struct {
                      6:        use     *MkVarUse
                      7:        vartype *Vartype
                      8:
                      9:        MkLines *MkLines
                     10:        MkLine  *MkLine
                     11: }
                     12:
                     13: func NewMkVarUseChecker(use *MkVarUse, mklines *MkLines, mkline *MkLine) *MkVarUseChecker {
                     14:        vartype := G.Pkgsrc.VariableType(mklines, use.varname)
                     15:
                     16:        return &MkVarUseChecker{use, vartype, mklines, mkline}
                     17: }
                     18:
                     19: // CheckVaruse checks a single use of a variable in a specific context.
                     20: func (ck *MkVarUseChecker) Check(vuc *VarUseContext) {
                     21:        if ck.use.IsExpression() {
                     22:                return
                     23:        }
                     24:
                     25:        ck.checkUndefined()
                     26:        ck.checkPermissions(vuc)
                     27:
                     28:        ck.checkVarname()
                     29:        ck.checkModifiers()
                     30:        ck.checkQuoting(vuc)
                     31:
                     32:        ck.checkBuildDefs()
                     33:        ck.checkDeprecated()
                     34:
                     35:        NewMkLineChecker(ck.MkLines, ck.MkLine).
                     36:                checkTextVarUse(ck.use.varname, ck.vartype, vuc.time)
                     37: }
                     38:
                     39: func (ck *MkVarUseChecker) checkUndefined() {
                     40:        varuse := ck.use
                     41:        vartype := ck.vartype
                     42:        varname := varuse.varname
                     43:
                     44:        switch {
                     45:        case !G.Opts.WarnExtra,
                     46:                // Well-known variables are probably defined by the infrastructure.
                     47:                vartype != nil && !vartype.IsGuessed(),
1.2     ! rillig     48:                // TODO: At load time, check ck.MkLines.loadVars instead of allVars.
        !            49:                ck.MkLines.allVars.IsDefinedSimilar(varname),
1.1       rillig     50:                ck.MkLines.forVars[varname],
1.2     ! rillig     51:                ck.MkLines.allVars.Mentioned(varname) != nil,
1.1       rillig     52:                G.Pkg != nil && G.Pkg.vars.IsDefinedSimilar(varname),
                     53:                containsVarRef(varname),
                     54:                G.Pkgsrc.vartypes.IsDefinedCanon(varname),
                     55:                varname == "":
                     56:                return
                     57:        }
                     58:
                     59:        if ck.MkLines.once.FirstTimeSlice("used but not defined", varname) {
                     60:                ck.MkLine.Warnf("%s is used but not defined.", varname)
                     61:        }
                     62: }
                     63:
                     64: func (ck *MkVarUseChecker) checkModifiers() {
1.2     ! rillig     65:        if len(ck.use.modifiers) == 0 {
1.1       rillig     66:                return
                     67:        }
                     68:
                     69:        ck.checkModifiersSuffix()
                     70:        ck.checkModifiersRange()
                     71:
                     72:        // TODO: Add checks for a single modifier, among them:
                     73:        // TODO: Suggest to replace ${VAR:@l@-l${l}@} with the simpler ${VAR:S,^,-l,}.
                     74:        // TODO: Suggest to replace ${VAR:@l@${l}suffix@} with the simpler ${VAR:=suffix}.
                     75:        // TODO: Investigate why :Q is not checked at this exact place.
                     76: }
                     77:
                     78: func (ck *MkVarUseChecker) checkModifiersSuffix() {
                     79:        varuse := ck.use
                     80:        vartype := ck.vartype
                     81:
                     82:        if !varuse.modifiers[0].IsSuffixSubst() || vartype == nil || vartype.IsList() {
                     83:                return
                     84:        }
                     85:
                     86:        ck.MkLine.Warnf("The :from=to modifier should only be used with lists, not with %s.", varuse.varname)
                     87:        ck.MkLine.Explain(
                     88:                "Instead of (for example):",
                     89:                "\tMASTER_SITES=\t${HOMEPAGE:=repository/}",
                     90:                "",
                     91:                "Write:",
                     92:                "\tMASTER_SITES=\t${HOMEPAGE}repository/",
                     93:                "",
                     94:                "This expresses the intention of the code more clearly.")
                     95: }
                     96:
                     97: // checkModifiersRange suggests to replace
                     98: // ${VAR:S,^,__magic__,1:M__magic__*:S,^__magic__,,} with the simpler ${VAR:[1]}.
                     99: func (ck *MkVarUseChecker) checkModifiersRange() {
                    100:        varuse := ck.use
                    101:        mods := varuse.modifiers
                    102:
                    103:        if len(mods) != 3 {
                    104:                return
                    105:        }
                    106:
                    107:        m, _, from, to, options := mods[0].MatchSubst()
                    108:        if !m || from != "^" || !matches(to, `^\w+$`) || options != "1" {
                    109:                return
                    110:        }
                    111:
                    112:        magic := to
                    113:        m, positive, pattern, _ := mods[1].MatchMatch()
                    114:        if !m || !positive || pattern != magic+"*" {
                    115:                return
                    116:        }
                    117:
                    118:        m, _, from, to, options = mods[2].MatchSubst()
                    119:        if !m || from != "^"+magic || to != "" || options != "" {
                    120:                return
                    121:        }
                    122:
                    123:        fix := ck.MkLine.Autofix()
                    124:        fix.Notef("The modifier %q can be written as %q.", varuse.Mod(), ":[1]")
                    125:        fix.Explain(
                    126:                "The range modifier is much easier to understand than the",
                    127:                "complicated regular expressions, which were needed before",
                    128:                "the year 2006.")
                    129:        fix.Replace(varuse.Mod(), ":[1]")
                    130:        fix.Apply()
                    131: }
                    132:
                    133: func (ck *MkVarUseChecker) checkVarname() {
                    134:        varname := ck.use.varname
                    135:        if varname == "@" {
                    136:                ck.MkLine.Warnf("Please use %q instead of %q.", "${.TARGET}", "$@")
                    137:                ck.MkLine.Explain(
                    138:                        "It is more readable and prevents confusion with the shell variable",
                    139:                        "of the same name.")
                    140:        }
                    141:
                    142:        if varname == "LOCALBASE" && !G.Infrastructure {
                    143:                fix := ck.MkLine.Autofix()
                    144:                fix.Warnf("Please use PREFIX instead of LOCALBASE.")
                    145:                fix.ReplaceRegex(`\$\{LOCALBASE\b`, "${PREFIX", 1)
                    146:                fix.Apply()
                    147:        }
                    148: }
                    149:
                    150: // checkPermissions checks the permissions when a variable is used,
                    151: // be it in a variable assignment, in a shell command, a conditional, or
                    152: // somewhere else.
                    153: //
                    154: // See checkVarassignLeftPermissions.
                    155: func (ck *MkVarUseChecker) checkPermissions(vuc *VarUseContext) {
                    156:        if !G.Opts.WarnPerm {
                    157:                return
                    158:        }
                    159:        if G.Infrastructure {
                    160:                // As long as vardefs.go doesn't explicitly define permissions for
                    161:                // infrastructure files, skip the check completely. This avoids
                    162:                // many wrong warnings.
                    163:                return
                    164:        }
                    165:
                    166:        if trace.Tracing {
                    167:                defer trace.Call(vuc)()
                    168:        }
                    169:
                    170:        // This is the type of the variable that is being used. Not to
                    171:        // be confused with vuc.vartype, which is the type of the
                    172:        // context in which the variable is used (often a ShellCommand
                    173:        // or, in an assignment, the type of the left hand side variable).
                    174:        varname := ck.use.varname
                    175:        vartype := ck.vartype
                    176:        if vartype == nil {
                    177:                if trace.Tracing {
                    178:                        trace.Step1("No type definition found for %q.", varname)
                    179:                }
                    180:                return
                    181:        }
                    182:
                    183:        if vartype.IsGuessed() {
                    184:                return
                    185:        }
                    186:
                    187:        // Do not warn about unknown infrastructure variables.
                    188:        // These have all permissions to prevent warnings when they are used.
                    189:        // But when other variables are assigned to them it would seem as if
                    190:        // these other variables could become evaluated at load time.
                    191:        // And this is something that most variables do not allow.
                    192:        if vuc.vartype != nil && vuc.vartype.basicType == BtUnknown {
                    193:                return
                    194:        }
                    195:
                    196:        basename := ck.MkLine.Basename
                    197:        if basename == "hacks.mk" {
                    198:                return
                    199:        }
                    200:
                    201:        effPerms := vartype.EffectivePermissions(basename)
                    202:        if effPerms.Contains(aclpUseLoadtime) {
                    203:                ck.checkUseAtLoadTime(vuc.time)
                    204:
                    205:                // Since the variable may be used at load time, it probably
                    206:                // may be used at run time as well. If it weren't, that would
                    207:                // be a rather strange permissions set.
                    208:                return
                    209:        }
                    210:
                    211:        // At this point the variable must not be used at load time.
                    212:        // Now determine whether it is directly used at load time because
                    213:        // the context already says so or, a little trickier, if it might
                    214:        // be used at load time somewhere in the future because it is
                    215:        // assigned to another variable, and that variable is allowed
                    216:        // to be used at load time.
                    217:        directly := vuc.time == VucLoadTime
                    218:        indirectly := !directly && vuc.vartype != nil &&
                    219:                vuc.vartype.Union().Contains(aclpUseLoadtime)
                    220:
                    221:        if !directly && !indirectly && effPerms.Contains(aclpUse) {
                    222:                // At this point the variable is either used at run time, or the
                    223:                // time is not known.
                    224:                return
                    225:        }
                    226:
                    227:        if directly || indirectly {
                    228:                // At this point the variable is used at load time although that
                    229:                // is not allowed by the permissions. The variable could be a tool
                    230:                // variable, and these tool variables have special rules.
                    231:                tool := G.ToolByVarname(ck.MkLines, varname)
                    232:                if tool != nil {
                    233:
                    234:                        // Whether a tool variable may be used at load time depends on
                    235:                        // whether bsd.prefs.mk has been included before. That file
                    236:                        // examines the tools that have been added to USE_TOOLS up to
                    237:                        // this point and makes their variables available for use at
                    238:                        // load time.
                    239:                        if !tool.UsableAtLoadTime(ck.MkLines.Tools.SeenPrefs) {
                    240:                                ck.warnToolLoadTime(varname, tool)
                    241:                        }
                    242:                        return
                    243:                }
                    244:        }
                    245:
                    246:        if ck.MkLines.once.FirstTimeSlice("checkPermissions", varname) {
                    247:                ck.warnPermissions(vuc.vartype, varname, vartype, directly, indirectly)
                    248:        }
                    249: }
                    250:
                    251: func (ck *MkVarUseChecker) warnPermissions(
                    252:        vucVartype *Vartype, varname string, vartype *Vartype, directly, indirectly bool) {
                    253:
                    254:        mkline := ck.MkLine
                    255:
                    256:        anyPerms := vartype.Union()
                    257:        if !anyPerms.Contains(aclpUse) && !anyPerms.Contains(aclpUseLoadtime) {
                    258:                mkline.Warnf("%s should not be used in any file; it is a write-only variable.", varname)
                    259:                ck.explainPermissions(varname, vartype)
                    260:                return
                    261:        }
                    262:
                    263:        if indirectly {
                    264:                // Some of the guessed variables may be used at load time. But since the
                    265:                // variable type and these permissions are guessed, pkglint should not
                    266:                // issue the following warning, since it is often wrong.
                    267:                if vucVartype.IsGuessed() {
                    268:                        return
                    269:                }
                    270:
                    271:                mkline.Warnf("%s should not be used indirectly at load time (via %s).",
                    272:                        varname, mkline.Varname())
                    273:                ck.explainPermissions(varname, vartype,
                    274:                        "The variable on the left-hand side may be evaluated at load time,",
                    275:                        "but the variable on the right-hand side should not.",
                    276:                        "Because of the assignment in this line, the variable might be",
                    277:                        "used indirectly at load time, before it is guaranteed to be",
                    278:                        "properly initialized.")
                    279:                return
                    280:        }
                    281:
                    282:        needed := aclpUse
                    283:        if directly {
                    284:                needed = aclpUseLoadtime
                    285:        }
                    286:        alternativeFiles := vartype.AlternativeFiles(needed)
                    287:
                    288:        loadTimeExplanation := func() []string {
                    289:                return []string{
                    290:                        "Many variables, especially lists of something, get their values incrementally.",
                    291:                        "Therefore it is generally unsafe to rely on their",
                    292:                        "value until it is clear that it will never change again.",
                    293:                        "This point is reached when the whole package Makefile is loaded and",
                    294:                        "execution of the shell commands starts; in some cases earlier.",
                    295:                        "",
                    296:                        "Additionally, when using the \":=\" operator, each $$ is replaced",
                    297:                        "with a single $, so variables that have references to shell",
                    298:                        "variables or regular expressions are modified in a subtle way."}
                    299:        }
                    300:
                    301:        switch {
                    302:        case alternativeFiles == "" && directly:
                    303:                mkline.Warnf("%s should not be used at load time in any file.", varname)
                    304:                ck.explainPermissions(varname, vartype, loadTimeExplanation()...)
                    305:
                    306:        case alternativeFiles == "":
                    307:                mkline.Warnf("%s should not be used in any file.", varname)
                    308:                ck.explainPermissions(varname, vartype, loadTimeExplanation()...)
                    309:
                    310:        case directly:
                    311:                mkline.Warnf(
                    312:                        "%s should not be used at load time in this file; "+
                    313:                                "it would be ok in %s.",
                    314:                        varname, alternativeFiles)
                    315:                ck.explainPermissions(varname, vartype, loadTimeExplanation()...)
                    316:
                    317:        default:
                    318:                mkline.Warnf(
                    319:                        "%s should not be used in this file; it would be ok in %s.",
                    320:                        varname, alternativeFiles)
                    321:                ck.explainPermissions(varname, vartype)
                    322:        }
                    323: }
                    324:
                    325: func (ck *MkVarUseChecker) explainPermissions(varname string, vartype *Vartype, intro ...string) {
                    326:        if !G.Logger.Opts.Explain {
                    327:                return
                    328:        }
                    329:
                    330:        // TODO: Starting with the second explanation, omit the common part. Instead, only list the permission rules.
                    331:
                    332:        var expl []string
                    333:
                    334:        if len(intro) > 0 {
                    335:                expl = append(expl, intro...)
                    336:                expl = append(expl, "")
                    337:        }
                    338:
                    339:        expl = append(expl,
                    340:                "The allowed actions for a variable are determined based on the file",
                    341:                "name in which the variable is used or defined.",
                    342:                sprintf("The rules for %s are:", varname),
                    343:                "")
                    344:
                    345:        for _, rule := range vartype.aclEntries {
                    346:                perms := rule.permissions.HumanString()
                    347:
                    348:                files := rule.matcher.originalPattern
                    349:                if files == "*" {
                    350:                        files = "any file"
                    351:                }
                    352:
                    353:                if perms != "" {
                    354:                        expl = append(expl, sprintf("* in %s, it may be %s", files, perms))
                    355:                } else {
                    356:                        expl = append(expl, sprintf("* in %s, it should not be accessed at all", files))
                    357:                }
                    358:        }
                    359:
                    360:        expl = append(expl,
                    361:                "",
                    362:                "If these rules seem to be incorrect, please ask on the tech-pkg@NetBSD.org mailing list.")
                    363:
                    364:        ck.MkLine.Explain(expl...)
                    365: }
                    366:
                    367: func (ck *MkVarUseChecker) checkUseAtLoadTime(time VucTime) {
                    368:        if time != VucLoadTime {
                    369:                return
                    370:        }
                    371:        if ck.vartype.IsAlwaysInScope() || ck.MkLines.Tools.SeenPrefs {
                    372:                return
                    373:        }
                    374:        if G.Pkg != nil && G.Pkg.seenPrefs {
                    375:                return
                    376:        }
                    377:        mkline := ck.MkLine
                    378:        basename := mkline.Basename
                    379:        if basename == "builtin.mk" {
                    380:                return
                    381:        }
                    382:
                    383:        if ck.vartype.IsPackageSettable() &&
                    384:                basename != "Makefile" && basename != "options.mk" {
                    385:
                    386:                // For package-settable variables, the explanation doesn't
                    387:                // make sense since it talks about completely different
                    388:                // types of variables.
                    389:                return
                    390:        }
                    391:
                    392:        if !ck.MkLines.once.FirstTime("bsd.prefs.mk") {
                    393:                return
                    394:        }
                    395:
                    396:        include := condStr(
                    397:                basename == "buildlink3.mk",
                    398:                "mk/bsd.fast.prefs.mk",
                    399:                "mk/bsd.prefs.mk")
                    400:        currInclude := G.Pkgsrc.File(NewPkgsrcPath(NewPath(include)))
                    401:
                    402:        mkline.Warnf("To use %s at load time, .include %q first.",
                    403:                ck.use.varname, mkline.Rel(currInclude))
                    404:        mkline.Explain(
                    405:                "The user-settable variables and several other variables",
                    406:                "from the pkgsrc infrastructure are only available",
                    407:                "after the preferences have been loaded.",
                    408:                "",
                    409:                "Before that, these variables are undefined.")
                    410: }
                    411:
                    412: // warnToolLoadTime logs a warning that the tool ${varname}
                    413: // should not be used at load time.
                    414: func (ck *MkVarUseChecker) warnToolLoadTime(varname string, tool *Tool) {
                    415:        // TODO: While using a tool by its variable name may be ok at load time,
                    416:        //  doing the same with the plain name of a tool is never ok.
                    417:        //  "VAR!= cat" is never guaranteed to call the correct cat.
                    418:        //  Even for shell builtins like echo and printf, bmake may decide
                    419:        //  to skip the shell and execute the commands via execve, which
                    420:        //  means that even echo is not a shell-builtin anymore.
                    421:
                    422:        // TODO: Replace "parse time" with "load time" everywhere.
                    423:
                    424:        if tool.Validity == AfterPrefsMk {
                    425:                ck.MkLine.Warnf("To use the tool ${%s} at load time, bsd.prefs.mk has to be included before.", varname)
                    426:                return
                    427:        }
                    428:
                    429:        if ck.MkLine.Basename == "Makefile" {
                    430:                pkgsrcTool := G.Pkgsrc.Tools.ByName(tool.Name)
                    431:                if pkgsrcTool != nil && pkgsrcTool.Validity == Nowhere {
                    432:                        // The tool must have been added too late to USE_TOOLS,
                    433:                        // i.e. after bsd.prefs.mk has been included.
                    434:                        ck.MkLine.Warnf("To use the tool ${%s} at load time, it has to be added to USE_TOOLS before including bsd.prefs.mk.", varname)
                    435:                        return
                    436:                }
                    437:        }
                    438:
                    439:        ck.MkLine.Warnf("The tool ${%s} cannot be used at load time.", varname)
                    440:        ck.MkLine.Explain(
                    441:                "To use a tool at load time, it must be declared in the package",
                    442:                "Makefile by adding it to USE_TOOLS.",
                    443:                "After that, bsd.prefs.mk must be included.",
                    444:                "Adding the tool to USE_TOOLS at any later time has no effect,",
                    445:                "which means that the tool can only be used at run time.",
                    446:                "That's the rule for the package Makefiles.",
                    447:                "",
                    448:                "Since any other .mk file can be included from anywhere else, there",
                    449:                "is no guarantee that the tool is properly defined for using it at",
                    450:                "load time (see above for the tricky rules).",
                    451:                "Therefore the tools can only be used at run time,",
                    452:                "except in the package Makefile itself.")
                    453: }
                    454:
                    455: // checkVarUseWords checks whether a variable use of the form ${VAR}
                    456: // or ${VAR:modifiers} is allowed in a certain context.
                    457: func (ck *MkVarUseChecker) checkQuoting(vuc *VarUseContext) {
                    458:        if !G.Opts.WarnQuoting || vuc.quoting == VucQuotUnknown {
                    459:                return
                    460:        }
                    461:
                    462:        varUse := ck.use
                    463:        vartype := ck.vartype
                    464:
                    465:        needsQuoting := ck.MkLine.VariableNeedsQuoting(ck.MkLines, varUse, vartype, vuc)
                    466:        if needsQuoting == unknown {
                    467:                return
                    468:        }
                    469:
                    470:        mod := varUse.Mod()
                    471:
                    472:        // In GNU configure scripts, a few variables need to be passed through
                    473:        // the :M* modifier before they reach the configure scripts. Otherwise
                    474:        // the leading or trailing spaces will lead to strange caching errors
                    475:        // since the GNU configure scripts cannot handle these space characters.
                    476:        //
                    477:        // When doing checks outside a package, the :M* modifier is needed for safety.
                    478:        needMstar := (G.Pkg == nil || G.Pkg.vars.IsDefined("GNU_CONFIGURE")) &&
                    479:                matches(varUse.varname, `^(?:.*_)?(?:CFLAGS|CPPFLAGS|CXXFLAGS|FFLAGS|LDFLAGS|LIBS)$`)
                    480:
                    481:        mkline := ck.MkLine
                    482:        if mod == ":M*:Q" && !needMstar {
                    483:                if !vartype.IsGuessed() {
                    484:                        mkline.Notef("The :M* modifier is not needed here.")
                    485:                }
                    486:
                    487:        } else if needsQuoting == yes {
                    488:                ck.checkQuotingQM(mod, needMstar, vuc)
                    489:        }
                    490:
                    491:        if hasSuffix(mod, ":Q") && needsQuoting == no {
                    492:                ck.warnRedundantModifierQ(mod)
                    493:        }
                    494: }
                    495:
                    496: func (ck *MkVarUseChecker) checkQuotingQM(mod string, needMstar bool, vuc *VarUseContext) {
                    497:        vartype := ck.vartype
                    498:        varname := ck.use.varname
                    499:
                    500:        modNoQ := strings.TrimSuffix(mod, ":Q")
                    501:        modNoM := strings.TrimSuffix(modNoQ, ":M*")
                    502:        correctMod := modNoM + condStr(needMstar, ":M*:Q", ":Q")
                    503:
                    504:        if correctMod == mod+":Q" && vuc.IsWordPart && !vartype.IsShell() {
                    505:
                    506:                isSingleWordConstant := func() bool {
                    507:                        if G.Pkg == nil {
                    508:                                return false
                    509:                        }
                    510:
                    511:                        varinfo := G.Pkg.redundant.vars[varname]
                    512:                        if varinfo == nil || !varinfo.vari.IsConstant() {
                    513:                                return false
                    514:                        }
                    515:
                    516:                        value := varinfo.vari.ConstantValue()
                    517:                        return len(ck.MkLine.ValueFields(value)) == 1
                    518:                }
                    519:
                    520:                if vartype.IsList() && isSingleWordConstant() {
                    521:                        // Do not warn in this special case, which typically occurs
                    522:                        // for BUILD_DIRS or similar package-settable variables.
                    523:
                    524:                } else if vartype.IsList() {
                    525:                        ck.warnListVariableInWord()
                    526:                } else {
                    527:                        ck.warnMissingModifierQInWord()
                    528:                }
                    529:
                    530:        } else if mod != correctMod {
                    531:                if vuc.quoting == VucQuotPlain {
                    532:                        ck.fixQuotingModifiers(correctMod, mod)
                    533:                } else {
                    534:                        ck.warnWrongQuotingModifiers(correctMod, mod)
                    535:                }
                    536:
                    537:        } else if vuc.quoting != VucQuotPlain {
                    538:                ck.warnModifierQInQuotes(mod)
                    539:        }
                    540: }
                    541:
                    542: func (ck *MkVarUseChecker) warnListVariableInWord() {
                    543:        mkline := ck.MkLine
                    544:
                    545:        mkline.Warnf("The list variable %s should not be embedded in a word.",
                    546:                ck.use.varname)
                    547:        mkline.Explain(
                    548:                "When a list variable has multiple elements, this expression expands",
                    549:                "to something unexpected:",
                    550:                "",
                    551:                "Example: ${MASTER_SITE_SOURCEFORGE}directory/ expands to",
                    552:                "",
                    553:                "\thttps://mirror1.sf.net/ https://mirror2.sf.net/directory/",
                    554:                "",
                    555:                "The first URL is missing the directory.",
                    556:                "To fix this, write",
                    557:                "\t${MASTER_SITE_SOURCEFORGE:=directory/}.",
                    558:                "",
                    559:                "Example: -l${LIBS} expands to",
                    560:                "",
                    561:                "\t-llib1 lib2",
                    562:                "",
                    563:                "The second library is missing the -l.",
                    564:                "To fix this, write ${LIBS:S,^,-l,}.")
                    565: }
                    566:
                    567: func (ck *MkVarUseChecker) warnMissingModifierQInWord() {
                    568:        mkline := ck.MkLine
                    569:
                    570:        mkline.Warnf("The variable %s should be quoted as part of a shell word.",
                    571:                ck.use.varname)
                    572:        mkline.Explain(
                    573:                "This variable can contain spaces or other special characters.",
                    574:                "Therefore it should be quoted by replacing ${VAR} with ${VAR:Q}.")
                    575: }
                    576:
                    577: func (ck *MkVarUseChecker) fixQuotingModifiers(correctMod string, mod string) {
                    578:        varname := ck.use.varname
                    579:
                    580:        fix := ck.MkLine.Autofix()
                    581:        fix.Warnf("Please use ${%s%s} instead of ${%s%s}.", varname, correctMod, varname, mod)
                    582:        fix.Explain(
                    583:                seeGuide("Echoing a string exactly as-is", "echo-literal"))
                    584:        fix.Replace("${"+varname+mod+"}", "${"+varname+correctMod+"}")
                    585:        fix.Apply()
                    586: }
                    587:
                    588: func (ck *MkVarUseChecker) warnWrongQuotingModifiers(correctMod string, mod string) {
                    589:        mkline := ck.MkLine
                    590:        varname := ck.use.varname
                    591:
                    592:        mkline.Warnf("Please use ${%s%s} instead of ${%s%s} and make sure"+
                    593:                " the variable appears outside of any quoting characters.", varname, correctMod, varname, mod)
                    594:        mkline.Explain(
                    595:                "The :Q modifier only works reliably when it is used outside of any",
                    596:                "quoting characters like 'single' or \"double\" quotes or `backticks`.",
                    597:                "",
                    598:                "Examples:",
                    599:                "Instead of CFLAGS=\"${CFLAGS:Q}\",",
                    600:                "     write CFLAGS=${CFLAGS:Q}.",
                    601:                "Instead of 's,@CFLAGS@,${CFLAGS:Q},',",
                    602:                "     write 's,@CFLAGS@,'${CFLAGS:Q}','.",
                    603:                "",
                    604:                seeGuide("Echoing a string exactly as-is", "echo-literal"))
                    605: }
                    606:
                    607: func (ck *MkVarUseChecker) warnModifierQInQuotes(mod string) {
                    608:        mkline := ck.MkLine
                    609:
                    610:        mkline.Warnf("Please move ${%s%s} outside of any quoting characters.",
                    611:                ck.use.varname, mod)
                    612:        mkline.Explain(
                    613:                "The :Q modifier only works reliably when it is used outside of any",
                    614:                "quoting characters like 'single' or \"double\" quotes or `backticks`.",
                    615:                "",
                    616:                "Examples:",
                    617:                "Instead of CFLAGS=\"${CFLAGS:Q}\",",
                    618:                "     write CFLAGS=${CFLAGS:Q}.",
                    619:                "Instead of 's,@CFLAGS@,${CFLAGS:Q},',",
                    620:                "     write 's,@CFLAGS@,'${CFLAGS:Q}','.",
                    621:                "",
                    622:                seeGuide("Echoing a string exactly as-is", "echo-literal"))
                    623: }
                    624:
                    625: func (ck *MkVarUseChecker) warnRedundantModifierQ(mod string) {
                    626:        varname := ck.use.varname
                    627:
                    628:        bad := "${" + varname + mod + "}"
                    629:        good := "${" + varname + strings.TrimSuffix(mod, ":Q") + "}"
                    630:
                    631:        fix := ck.MkLine.Line.Autofix()
                    632:        fix.Notef("The :Q modifier isn't necessary for ${%s} here.", varname)
                    633:        fix.Explain(
                    634:                "Many variables in pkgsrc do not need the :Q modifier since they",
                    635:                "are not expected to contain whitespace or other special characters.",
                    636:                "Examples for these \"safe\" variables are:",
                    637:                "",
                    638:                "\t* filenames",
                    639:                "\t* directory names",
                    640:                "\t* user and group names",
                    641:                "\t* tool names and tool paths",
                    642:                "\t* variable names",
                    643:                "\t* package names (but not dependency patterns like pkg>=1.2)")
                    644:        fix.Replace(bad, good)
                    645:        fix.Apply()
                    646: }
                    647:
                    648: func (ck *MkVarUseChecker) checkBuildDefs() {
                    649:        varname := ck.use.varname
                    650:
                    651:        if !G.Pkgsrc.UserDefinedVars.IsDefined(varname) || G.Pkgsrc.IsBuildDef(varname) {
                    652:                return
                    653:        }
                    654:        if ck.MkLines.buildDefs[varname] {
                    655:                return
                    656:        }
                    657:        if !ck.MkLines.once.FirstTimeSlice("BUILD_DEFS", varname) {
                    658:                return
                    659:        }
                    660:
                    661:        ck.MkLine.Warnf("The user-defined variable %s is used but not added to BUILD_DEFS.", varname)
                    662:        ck.MkLine.Explain(
                    663:                "When a pkgsrc package is built, many things can be configured by the",
                    664:                "pkgsrc user in the mk.conf file.",
                    665:                "All these configurations should be recorded in the binary package",
                    666:                "so the package can be reliably rebuilt.",
                    667:                "The BUILD_DEFS variable contains a list of all these",
                    668:                "user-settable variables, so please add your variable to it, too.")
                    669: }
                    670:
                    671: func (ck *MkVarUseChecker) checkDeprecated() {
                    672:        varname := ck.use.varname
                    673:        instead := G.Pkgsrc.Deprecated[varname]
                    674:        if instead == "" {
                    675:                instead = G.Pkgsrc.Deprecated[varnameCanon(varname)]
                    676:        }
                    677:        if instead == "" {
                    678:                return
                    679:        }
                    680:
                    681:        ck.MkLine.Warnf("Use of %q is deprecated. %s", varname, instead)
                    682: }

CVSweb <webmaster@jp.NetBSD.org>