[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.3

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:
1.3     ! rillig     28:        ck.checkVarname(vuc.time)
1.1       rillig     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:
1.3     ! rillig    133: func (ck *MkVarUseChecker) checkVarname(time VucTime) {
1.1       rillig    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:
1.3     ! rillig    142:        if varname == "LOCALBASE" && !G.Infrastructure && time == VucRunTime {
1.1       rillig    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:
1.3     ! rillig    383:        if ck.vartype.IsPackageSettable() {
        !           384:                // For package-settable variables, the explanation below
        !           385:                // doesn't make sense since including bsd.prefs.mk won't
        !           386:                // define any package-settable variables.
1.1       rillig    387:                return
                    388:        }
                    389:
                    390:        if !ck.MkLines.once.FirstTime("bsd.prefs.mk") {
                    391:                return
                    392:        }
                    393:
                    394:        include := condStr(
                    395:                basename == "buildlink3.mk",
                    396:                "mk/bsd.fast.prefs.mk",
                    397:                "mk/bsd.prefs.mk")
                    398:        currInclude := G.Pkgsrc.File(NewPkgsrcPath(NewPath(include)))
                    399:
                    400:        mkline.Warnf("To use %s at load time, .include %q first.",
                    401:                ck.use.varname, mkline.Rel(currInclude))
                    402:        mkline.Explain(
                    403:                "The user-settable variables and several other variables",
                    404:                "from the pkgsrc infrastructure are only available",
                    405:                "after the preferences have been loaded.",
                    406:                "",
                    407:                "Before that, these variables are undefined.")
                    408: }
                    409:
                    410: // warnToolLoadTime logs a warning that the tool ${varname}
                    411: // should not be used at load time.
                    412: func (ck *MkVarUseChecker) warnToolLoadTime(varname string, tool *Tool) {
                    413:        // TODO: While using a tool by its variable name may be ok at load time,
                    414:        //  doing the same with the plain name of a tool is never ok.
                    415:        //  "VAR!= cat" is never guaranteed to call the correct cat.
                    416:        //  Even for shell builtins like echo and printf, bmake may decide
                    417:        //  to skip the shell and execute the commands via execve, which
                    418:        //  means that even echo is not a shell-builtin anymore.
                    419:
                    420:        // TODO: Replace "parse time" with "load time" everywhere.
                    421:
                    422:        if tool.Validity == AfterPrefsMk {
                    423:                ck.MkLine.Warnf("To use the tool ${%s} at load time, bsd.prefs.mk has to be included before.", varname)
                    424:                return
                    425:        }
                    426:
                    427:        if ck.MkLine.Basename == "Makefile" {
                    428:                pkgsrcTool := G.Pkgsrc.Tools.ByName(tool.Name)
                    429:                if pkgsrcTool != nil && pkgsrcTool.Validity == Nowhere {
                    430:                        // The tool must have been added too late to USE_TOOLS,
                    431:                        // i.e. after bsd.prefs.mk has been included.
                    432:                        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)
                    433:                        return
                    434:                }
                    435:        }
                    436:
                    437:        ck.MkLine.Warnf("The tool ${%s} cannot be used at load time.", varname)
                    438:        ck.MkLine.Explain(
                    439:                "To use a tool at load time, it must be declared in the package",
                    440:                "Makefile by adding it to USE_TOOLS.",
                    441:                "After that, bsd.prefs.mk must be included.",
                    442:                "Adding the tool to USE_TOOLS at any later time has no effect,",
                    443:                "which means that the tool can only be used at run time.",
                    444:                "That's the rule for the package Makefiles.",
                    445:                "",
                    446:                "Since any other .mk file can be included from anywhere else, there",
                    447:                "is no guarantee that the tool is properly defined for using it at",
                    448:                "load time (see above for the tricky rules).",
                    449:                "Therefore the tools can only be used at run time,",
                    450:                "except in the package Makefile itself.")
                    451: }
                    452:
                    453: // checkVarUseWords checks whether a variable use of the form ${VAR}
                    454: // or ${VAR:modifiers} is allowed in a certain context.
                    455: func (ck *MkVarUseChecker) checkQuoting(vuc *VarUseContext) {
                    456:        if !G.Opts.WarnQuoting || vuc.quoting == VucQuotUnknown {
                    457:                return
                    458:        }
                    459:
                    460:        varUse := ck.use
                    461:        vartype := ck.vartype
                    462:
                    463:        needsQuoting := ck.MkLine.VariableNeedsQuoting(ck.MkLines, varUse, vartype, vuc)
                    464:        if needsQuoting == unknown {
                    465:                return
                    466:        }
                    467:
                    468:        mod := varUse.Mod()
                    469:
                    470:        // In GNU configure scripts, a few variables need to be passed through
                    471:        // the :M* modifier before they reach the configure scripts. Otherwise
                    472:        // the leading or trailing spaces will lead to strange caching errors
                    473:        // since the GNU configure scripts cannot handle these space characters.
                    474:        //
                    475:        // When doing checks outside a package, the :M* modifier is needed for safety.
                    476:        needMstar := (G.Pkg == nil || G.Pkg.vars.IsDefined("GNU_CONFIGURE")) &&
                    477:                matches(varUse.varname, `^(?:.*_)?(?:CFLAGS|CPPFLAGS|CXXFLAGS|FFLAGS|LDFLAGS|LIBS)$`)
                    478:
                    479:        mkline := ck.MkLine
                    480:        if mod == ":M*:Q" && !needMstar {
                    481:                if !vartype.IsGuessed() {
                    482:                        mkline.Notef("The :M* modifier is not needed here.")
                    483:                }
                    484:
                    485:        } else if needsQuoting == yes {
                    486:                ck.checkQuotingQM(mod, needMstar, vuc)
                    487:        }
                    488:
                    489:        if hasSuffix(mod, ":Q") && needsQuoting == no {
                    490:                ck.warnRedundantModifierQ(mod)
                    491:        }
                    492: }
                    493:
                    494: func (ck *MkVarUseChecker) checkQuotingQM(mod string, needMstar bool, vuc *VarUseContext) {
                    495:        vartype := ck.vartype
                    496:        varname := ck.use.varname
                    497:
                    498:        modNoQ := strings.TrimSuffix(mod, ":Q")
                    499:        modNoM := strings.TrimSuffix(modNoQ, ":M*")
                    500:        correctMod := modNoM + condStr(needMstar, ":M*:Q", ":Q")
                    501:
                    502:        if correctMod == mod+":Q" && vuc.IsWordPart && !vartype.IsShell() {
                    503:
                    504:                isSingleWordConstant := func() bool {
                    505:                        if G.Pkg == nil {
                    506:                                return false
                    507:                        }
                    508:
                    509:                        varinfo := G.Pkg.redundant.vars[varname]
                    510:                        if varinfo == nil || !varinfo.vari.IsConstant() {
                    511:                                return false
                    512:                        }
                    513:
                    514:                        value := varinfo.vari.ConstantValue()
                    515:                        return len(ck.MkLine.ValueFields(value)) == 1
                    516:                }
                    517:
                    518:                if vartype.IsList() && isSingleWordConstant() {
                    519:                        // Do not warn in this special case, which typically occurs
                    520:                        // for BUILD_DIRS or similar package-settable variables.
                    521:
                    522:                } else if vartype.IsList() {
                    523:                        ck.warnListVariableInWord()
                    524:                } else {
                    525:                        ck.warnMissingModifierQInWord()
                    526:                }
                    527:
                    528:        } else if mod != correctMod {
                    529:                if vuc.quoting == VucQuotPlain {
                    530:                        ck.fixQuotingModifiers(correctMod, mod)
                    531:                } else {
                    532:                        ck.warnWrongQuotingModifiers(correctMod, mod)
                    533:                }
                    534:
                    535:        } else if vuc.quoting != VucQuotPlain {
                    536:                ck.warnModifierQInQuotes(mod)
                    537:        }
                    538: }
                    539:
                    540: func (ck *MkVarUseChecker) warnListVariableInWord() {
                    541:        mkline := ck.MkLine
                    542:
                    543:        mkline.Warnf("The list variable %s should not be embedded in a word.",
                    544:                ck.use.varname)
                    545:        mkline.Explain(
                    546:                "When a list variable has multiple elements, this expression expands",
                    547:                "to something unexpected:",
                    548:                "",
                    549:                "Example: ${MASTER_SITE_SOURCEFORGE}directory/ expands to",
                    550:                "",
                    551:                "\thttps://mirror1.sf.net/ https://mirror2.sf.net/directory/",
                    552:                "",
                    553:                "The first URL is missing the directory.",
                    554:                "To fix this, write",
                    555:                "\t${MASTER_SITE_SOURCEFORGE:=directory/}.",
                    556:                "",
                    557:                "Example: -l${LIBS} expands to",
                    558:                "",
                    559:                "\t-llib1 lib2",
                    560:                "",
                    561:                "The second library is missing the -l.",
                    562:                "To fix this, write ${LIBS:S,^,-l,}.")
                    563: }
                    564:
                    565: func (ck *MkVarUseChecker) warnMissingModifierQInWord() {
                    566:        mkline := ck.MkLine
                    567:
                    568:        mkline.Warnf("The variable %s should be quoted as part of a shell word.",
                    569:                ck.use.varname)
                    570:        mkline.Explain(
                    571:                "This variable can contain spaces or other special characters.",
                    572:                "Therefore it should be quoted by replacing ${VAR} with ${VAR:Q}.")
                    573: }
                    574:
                    575: func (ck *MkVarUseChecker) fixQuotingModifiers(correctMod string, mod string) {
                    576:        varname := ck.use.varname
                    577:
                    578:        fix := ck.MkLine.Autofix()
                    579:        fix.Warnf("Please use ${%s%s} instead of ${%s%s}.", varname, correctMod, varname, mod)
                    580:        fix.Explain(
                    581:                seeGuide("Echoing a string exactly as-is", "echo-literal"))
                    582:        fix.Replace("${"+varname+mod+"}", "${"+varname+correctMod+"}")
                    583:        fix.Apply()
                    584: }
                    585:
                    586: func (ck *MkVarUseChecker) warnWrongQuotingModifiers(correctMod string, mod string) {
                    587:        mkline := ck.MkLine
                    588:        varname := ck.use.varname
                    589:
                    590:        mkline.Warnf("Please use ${%s%s} instead of ${%s%s} and make sure"+
                    591:                " the variable appears outside of any quoting characters.", varname, correctMod, varname, mod)
                    592:        mkline.Explain(
                    593:                "The :Q modifier only works reliably when it is used outside of any",
                    594:                "quoting characters like 'single' or \"double\" quotes or `backticks`.",
                    595:                "",
                    596:                "Examples:",
                    597:                "Instead of CFLAGS=\"${CFLAGS:Q}\",",
                    598:                "     write CFLAGS=${CFLAGS:Q}.",
                    599:                "Instead of 's,@CFLAGS@,${CFLAGS:Q},',",
                    600:                "     write 's,@CFLAGS@,'${CFLAGS:Q}','.",
                    601:                "",
                    602:                seeGuide("Echoing a string exactly as-is", "echo-literal"))
                    603: }
                    604:
                    605: func (ck *MkVarUseChecker) warnModifierQInQuotes(mod string) {
                    606:        mkline := ck.MkLine
                    607:
                    608:        mkline.Warnf("Please move ${%s%s} outside of any quoting characters.",
                    609:                ck.use.varname, mod)
                    610:        mkline.Explain(
                    611:                "The :Q modifier only works reliably when it is used outside of any",
                    612:                "quoting characters like 'single' or \"double\" quotes or `backticks`.",
                    613:                "",
                    614:                "Examples:",
                    615:                "Instead of CFLAGS=\"${CFLAGS:Q}\",",
                    616:                "     write CFLAGS=${CFLAGS:Q}.",
                    617:                "Instead of 's,@CFLAGS@,${CFLAGS:Q},',",
                    618:                "     write 's,@CFLAGS@,'${CFLAGS:Q}','.",
                    619:                "",
                    620:                seeGuide("Echoing a string exactly as-is", "echo-literal"))
                    621: }
                    622:
                    623: func (ck *MkVarUseChecker) warnRedundantModifierQ(mod string) {
                    624:        varname := ck.use.varname
                    625:
                    626:        bad := "${" + varname + mod + "}"
                    627:        good := "${" + varname + strings.TrimSuffix(mod, ":Q") + "}"
                    628:
                    629:        fix := ck.MkLine.Line.Autofix()
                    630:        fix.Notef("The :Q modifier isn't necessary for ${%s} here.", varname)
                    631:        fix.Explain(
                    632:                "Many variables in pkgsrc do not need the :Q modifier since they",
                    633:                "are not expected to contain whitespace or other special characters.",
                    634:                "Examples for these \"safe\" variables are:",
                    635:                "",
                    636:                "\t* filenames",
                    637:                "\t* directory names",
                    638:                "\t* user and group names",
                    639:                "\t* tool names and tool paths",
                    640:                "\t* variable names",
                    641:                "\t* package names (but not dependency patterns like pkg>=1.2)")
                    642:        fix.Replace(bad, good)
                    643:        fix.Apply()
                    644: }
                    645:
                    646: func (ck *MkVarUseChecker) checkBuildDefs() {
                    647:        varname := ck.use.varname
                    648:
                    649:        if !G.Pkgsrc.UserDefinedVars.IsDefined(varname) || G.Pkgsrc.IsBuildDef(varname) {
                    650:                return
                    651:        }
                    652:        if ck.MkLines.buildDefs[varname] {
                    653:                return
                    654:        }
                    655:        if !ck.MkLines.once.FirstTimeSlice("BUILD_DEFS", varname) {
                    656:                return
                    657:        }
                    658:
                    659:        ck.MkLine.Warnf("The user-defined variable %s is used but not added to BUILD_DEFS.", varname)
                    660:        ck.MkLine.Explain(
                    661:                "When a pkgsrc package is built, many things can be configured by the",
                    662:                "pkgsrc user in the mk.conf file.",
                    663:                "All these configurations should be recorded in the binary package",
                    664:                "so the package can be reliably rebuilt.",
                    665:                "The BUILD_DEFS variable contains a list of all these",
                    666:                "user-settable variables, so please add your variable to it, too.")
                    667: }
                    668:
                    669: func (ck *MkVarUseChecker) checkDeprecated() {
                    670:        varname := ck.use.varname
                    671:        instead := G.Pkgsrc.Deprecated[varname]
                    672:        if instead == "" {
                    673:                instead = G.Pkgsrc.Deprecated[varnameCanon(varname)]
                    674:        }
                    675:        if instead == "" {
                    676:                return
                    677:        }
                    678:
                    679:        ck.MkLine.Warnf("Use of %q is deprecated. %s", varname, instead)
                    680: }

CVSweb <webmaster@jp.NetBSD.org>