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

Annotation of pkgsrc/pkgtools/pkglint/files/vartypecheck.go, Revision 1.17

1.1       rillig      1: package main
                      2:
                      3: import (
                      4:        "path"
1.13      rillig      5:        "sort"
1.1       rillig      6:        "strings"
                      7: )
                      8:
                      9: type VartypeCheck struct {
1.16      rillig     10:        MkLine     *MkLine
                     11:        Line       *Line
                     12:        Varname    string
                     13:        Op         MkOperator
                     14:        Value      string
                     15:        ValueNoVar string
                     16:        MkComment  string
                     17:        Guessed    bool // Whether the type definition is guessed (based on the variable name) or explicitly defined (see vardefs.go).
1.9       rillig     18: }
                     19:
                     20: type MkOperator uint8
                     21:
                     22: const (
                     23:        opAssign        MkOperator = iota // =
                     24:        opAssignShell                     // !=
                     25:        opAssignEval                      // :=
                     26:        opAssignAppend                    // +=
                     27:        opAssignDefault                   // ?=
1.11      rillig     28:        opUse                             //
                     29:        opUseMatch                        // Used in the :M operator
1.9       rillig     30: )
                     31:
                     32: func NewMkOperator(op string) MkOperator {
                     33:        switch op {
                     34:        case "=":
                     35:                return opAssign
                     36:        case "!=":
                     37:                return opAssignShell
                     38:        case ":=":
                     39:                return opAssignEval
                     40:        case "+=":
                     41:                return opAssignAppend
                     42:        case "?=":
                     43:                return opAssignDefault
                     44:        }
                     45:        return opAssign
                     46: }
                     47:
                     48: func (op MkOperator) String() string {
1.11      rillig     49:        return [...]string{"=", "!=", ":=", "+=", "?=", "use", "use-loadtime", "use-match"}[op]
1.1       rillig     50: }
                     51:
1.12      rillig     52: const (
1.13      rillig     53:        reMachineOpsys = "" + // See mk/platform
                     54:                "AIX|BSDOS|Bitrig|Cygwin|Darwin|DragonFly|FreeBSD|FreeMiNT|GNUkFreeBSD|" +
                     55:                "HPUX|Haiku|IRIX|Interix|Linux|Minix|MirBSD|NetBSD|OSF1|OpenBSD|QNX|SCO_SV|SunOS|UnixWare"
                     56:
                     57:        // See mk/emulator/emulator-vars.mk.
                     58:        reEmulOpsys = "" +
1.12      rillig     59:                "bitrig|bsdos|cygwin|darwin|dragonfly|freebsd|" +
1.13      rillig     60:                "haiku|hpux|interix|irix|linux|mirbsd|netbsd|openbsd|osf1|solaris|sunos"
                     61:
                     62:        // Hardware architectures having the same name in bsd.own.mk and the GNU world.
                     63:        // These are best-effort guesses, since they depend on the operating system.
                     64:        reArch = "" +
                     65:                "aarch64|alpha|amd64|arc|arm|cobalt|convex|dreamcast|i386|" +
                     66:                "hpcmips|hpcsh|hppa|hppa64|ia64|" +
                     67:                "m68k|m88k|mips|mips64|mips64el|mipseb|mipsel|mipsn32|mlrisc|" +
                     68:                "ns32k|pc532|pmax|powerpc|powerpc64|rs6000|s390|sparc|sparc64|vax|x86_64"
                     69:
                     70:        // See mk/bsd.prefs.mk:/^GNU_ARCH\./
                     71:        reMachineArch = "" +
                     72:                reArch + "|" +
                     73:                "aarch64eb|amd64|arm26|arm32|coldfire|earm|earmeb|earmhf|earmhfeb|earmv4|earmv4eb|earmv5|" +
                     74:                "earmv5eb|earmv6|earmv6eb|earmv6hf|earmv6hfeb|earmv7|earmv7eb|earmv7hf|earmv7hfeb|evbarm|" +
                     75:                "i386|i586|i686|m68000|mips|mips64eb|sh3eb|sh3el"
                     76:
                     77:        // See mk/bsd.prefs.mk:/^GNU_ARCH\./
                     78:        reMachineGnuArch = "" +
                     79:                reArch + "|" +
                     80:                "aarch64_be|arm|armeb|armv4|armv4eb|armv6|armv6eb|armv7|armv7eb|" +
                     81:                "i486|m5407|m68010|mips64|mipsel|sh|shle|x86_64"
                     82:
                     83:        reEmulArch = reMachineArch // Just a wild guess.
1.12      rillig     84: )
                     85:
1.13      rillig     86: func enumFromRe(re string) *VarChecker {
                     87:        values := strings.Split(re, "|")
                     88:        sort.Strings(values)
                     89:        seen := make(map[string]bool)
                     90:        var unique []string
                     91:        for _, value := range values {
                     92:                if !seen[value] {
                     93:                        seen[value] = true
                     94:                        unique = append(unique, value)
                     95:                }
                     96:        }
                     97:        return enum(strings.Join(unique, " "))
                     98: }
                     99:
1.12      rillig    100: var (
1.13      rillig    101:        enumMachineOpsys            = enumFromRe(reMachineOpsys)
                    102:        enumMachineArch             = enumFromRe(reMachineArch)
                    103:        enumMachineGnuArch          = enumFromRe(reMachineGnuArch)
                    104:        enumEmulOpsys               = enumFromRe(reEmulOpsys)
                    105:        enumEmulArch                = enumFromRe(reEmulArch)
                    106:        enumMachineGnuPlatformOpsys = enumEmulOpsys
1.12      rillig    107: )
                    108:
1.1       rillig    109: func (cv *VartypeCheck) AwkCommand() {
1.13      rillig    110:        if G.opts.Debug {
1.16      rillig    111:                traceStep1("Unchecked AWK command: %q", cv.Value)
1.9       rillig    112:        }
1.1       rillig    113: }
                    114:
                    115: func (cv *VartypeCheck) BasicRegularExpression() {
1.13      rillig    116:        if G.opts.Debug {
1.16      rillig    117:                traceStep1("Unchecked basic regular expression: %q", cv.Value)
1.9       rillig    118:        }
1.1       rillig    119: }
                    120:
                    121: func (cv *VartypeCheck) BuildlinkDepmethod() {
1.16      rillig    122:        if !containsVarRef(cv.Value) && cv.Value != "build" && cv.Value != "full" {
                    123:                cv.Line.Warn1("Invalid dependency method %q. Valid methods are \"build\" or \"full\".", cv.Value)
1.1       rillig    124:        }
                    125: }
                    126:
                    127: func (cv *VartypeCheck) Category() {
1.16      rillig    128:        if cv.Value != "wip" && fileExists(G.CurrentDir+"/"+G.CurPkgsrcdir+"/"+cv.Value+"/Makefile") {
1.1       rillig    129:                return
                    130:        }
1.16      rillig    131:        switch cv.Value {
1.1       rillig    132:        case
                    133:                "chinese", "crosspkgtools",
                    134:                "gnome", "gnustep",
                    135:                "japanese", "java",
                    136:                "kde", "korean",
                    137:                "linux", "local",
                    138:                "packages", "perl5", "plan9", "python",
                    139:                "ruby",
                    140:                "scm",
                    141:                "tcl", "tk",
                    142:                "windowmaker",
                    143:                "xmms":
                    144:        default:
1.16      rillig    145:                cv.Line.Error1("Invalid category %q.", cv.Value)
1.1       rillig    146:        }
                    147: }
                    148:
                    149: // A single option to the C/C++ compiler.
                    150: func (cv *VartypeCheck) CFlag() {
1.16      rillig    151:        if cv.Op == opUseMatch {
1.11      rillig    152:                return
                    153:        }
1.16      rillig    154:        cflag := cv.Value
1.1       rillig    155:        switch {
1.9       rillig    156:        case matches(cflag, `^-[DILOUWfgm]`),
                    157:                hasPrefix(cflag, "-std="),
                    158:                cflag == "-c99",
                    159:                cflag == "-c",
                    160:                cflag == "-no-integrated-as",
                    161:                cflag == "-pthread",
                    162:                hasPrefix(cflag, "`") && hasSuffix(cflag, "`"),
                    163:                containsVarRef(cflag):
                    164:                return
                    165:        case hasPrefix(cflag, "-"):
1.16      rillig    166:                cv.Line.Warn1("Unknown compiler flag %q.", cflag)
1.9       rillig    167:        default:
1.16      rillig    168:                cv.Line.Warn1("Compiler flag %q should start with a hyphen.", cflag)
1.1       rillig    169:        }
                    170: }
                    171:
                    172: // The single-line description of the package.
                    173: func (cv *VartypeCheck) Comment() {
1.16      rillig    174:        line, value := cv.Line, cv.Value
1.1       rillig    175:
1.9       rillig    176:        if value == "TODO: Short description of the package" { // See pkgtools/url2pkg/files/url2pkg.pl, keyword "COMMENT".
                    177:                line.Error0("COMMENT must be set.")
1.1       rillig    178:        }
                    179:        if m, first := match1(value, `^(?i)(a|an)\s`); m {
1.9       rillig    180:                line.Warn1("COMMENT should not begin with %q.", first)
1.1       rillig    181:        }
                    182:        if matches(value, `^[a-z]`) {
1.9       rillig    183:                line.Warn0("COMMENT should start with a capital letter.")
1.1       rillig    184:        }
                    185:        if hasSuffix(value, ".") {
1.9       rillig    186:                line.Warn0("COMMENT should not end with a period.")
1.1       rillig    187:        }
                    188:        if len(value) > 70 {
1.9       rillig    189:                line.Warn0("COMMENT should not be longer than 70 characters.")
1.1       rillig    190:        }
                    191: }
                    192:
                    193: func (cv *VartypeCheck) Dependency() {
1.16      rillig    194:        line, value := cv.Line, cv.Value
1.1       rillig    195:
1.13      rillig    196:        parser := NewParser(line, value, false)
1.9       rillig    197:        deppat := parser.Dependency()
                    198:        if deppat != nil && deppat.wildcard == "" && (parser.Rest() == "{,nb*}" || parser.Rest() == "{,nb[0-9]*}") {
                    199:                line.Warn0("Dependency patterns of the form pkgbase>=1.0 don't need the \"{,nb*}\" extension.")
                    200:                Explain4(
                    201:                        "The \"{,nb*}\" extension is only necessary for dependencies of the",
                    202:                        "form \"pkgbase-1.2\", since the pattern \"pkgbase-1.2\" doesn't match",
                    203:                        "the version \"pkgbase-1.2nb5\".  For dependency patterns using the",
                    204:                        "comparison operators, this is not necessary.")
                    205:
                    206:        } else if deppat == nil || !parser.EOF() {
                    207:                line.Warn1("Unknown dependency pattern %q.", value)
                    208:                Explain(
                    209:                        "Typical dependencies have the following forms:",
                    210:                        "",
                    211:                        "\tpackage>=2.5",
                    212:                        "\tpackage-[0-9]*",
                    213:                        "\tpackage-3.141",
                    214:                        "\tpackage>=2.71828<=3.1415")
1.1       rillig    215:                return
                    216:        }
                    217:
1.9       rillig    218:        wildcard := deppat.wildcard
                    219:        if m, inside := match1(wildcard, `^\[(.*)\]\*$`); m {
                    220:                if inside != "0-9" {
                    221:                        line.Warn0("Only [0-9]* is allowed in the numeric part of a dependency.")
                    222:                }
                    223:
                    224:        } else if m, ver, suffix := match2(wildcard, `^(\d\w*(?:\.\w+)*)(\.\*|\{,nb\*\}|\{,nb\[0-9\]\*\}|\*|)$`); m {
                    225:                if suffix == "" {
                    226:                        line.Warn2("Please use %q instead of %q as the version pattern.", ver+"{,nb*}", ver)
                    227:                        Explain3(
                    228:                                "Without the \"{,nb*}\" suffix, this version pattern only matches",
                    229:                                "package versions that don't have a PKGREVISION (which is the part",
                    230:                                "after the \"nb\").")
                    231:                }
                    232:                if suffix == "*" {
                    233:                        line.Warn2("Please use %q instead of %q as the version pattern.", ver+".*", ver+"*")
                    234:                        Explain2(
                    235:                                "For example, the version \"1*\" also matches \"10.0.0\", which is",
                    236:                                "probably not intended.")
                    237:                }
                    238:
                    239:        } else if wildcard == "*" {
                    240:                line.Warn1("Please use \"%[1]s-[0-9]*\" instead of \"%[1]s-*\".", deppat.pkgbase)
                    241:                Explain3(
                    242:                        "If you use a * alone, the package specification may match other",
                    243:                        "packages that have the same prefix, but a longer name.  For example,",
                    244:                        "foo-* matches foo-1.2, but also foo-client-1.2 and foo-server-1.2.")
                    245:        }
                    246:
                    247:        if nocclasses := regcomp(`\[[\d-]+\]`).ReplaceAllString(wildcard, ""); contains(nocclasses, "-") {
                    248:                line.Warn1("The version pattern %q should not contain a hyphen.", wildcard)
                    249:                Explain(
                    250:                        "Pkgsrc interprets package names with version numbers like this:",
                    251:                        "",
                    252:                        "\t\"foo-2.0-2.1.x\" => pkgbase \"foo\", version \"2.0-2.1.x\"",
1.1       rillig    253:                        "",
1.9       rillig    254:                        "To make the \"2.0\" above part of the package basename, the hyphen",
                    255:                        "must be omitted, so the full package name becomes \"foo2.0-2.1.x\".")
1.1       rillig    256:        }
                    257: }
                    258:
                    259: func (cv *VartypeCheck) DependencyWithPath() {
1.16      rillig    260:        line, value := cv.Line, cv.Value
                    261:        if value != cv.ValueNoVar {
1.1       rillig    262:                return // It's probably not worth checking this.
                    263:        }
                    264:
1.9       rillig    265:        if m, pattern, relpath, pkg := match3(value, `(.*):(\.\./\.\./[^/]+/([^/]+))$`); m {
1.16      rillig    266:                cv.MkLine.CheckRelativePkgdir(relpath)
1.1       rillig    267:
                    268:                switch pkg {
                    269:                case "msgfmt", "gettext":
1.9       rillig    270:                        line.Warn0("Please use USE_TOOLS+=msgfmt instead of this dependency.")
1.1       rillig    271:                case "perl5":
1.9       rillig    272:                        line.Warn0("Please use USE_TOOLS+=perl:run instead of this dependency.")
1.1       rillig    273:                case "gmake":
1.9       rillig    274:                        line.Warn0("Please use USE_TOOLS+=gmake instead of this dependency.")
1.1       rillig    275:                }
                    276:
1.16      rillig    277:                cv.MkLine.CheckVartypePrimitive(cv.Varname, CheckvarDependency, cv.Op, pattern, cv.MkComment, cv.Guessed)
1.1       rillig    278:                return
                    279:        }
                    280:
                    281:        if matches(value, `:\.\./[^/]+$`) {
1.9       rillig    282:                line.Warn0("Dependencies should have the form \"../../category/package\".")
1.16      rillig    283:                cv.MkLine.explainRelativeDirs()
1.1       rillig    284:                return
                    285:        }
                    286:
1.9       rillig    287:        line.Warn1("Unknown dependency pattern with path %q.", value)
                    288:        Explain4(
                    289:                "Examples for valid dependency patterns with path are:",
1.1       rillig    290:                "  package-[0-9]*:../../category/package",
                    291:                "  package>=3.41:../../category/package",
                    292:                "  package-2.718:../../category/package")
                    293: }
                    294:
                    295: func (cv *VartypeCheck) DistSuffix() {
1.16      rillig    296:        if cv.Value == ".tar.gz" {
                    297:                cv.Line.Note1("%s is \".tar.gz\" by default, so this definition may be redundant.", cv.Varname)
1.1       rillig    298:        }
                    299: }
                    300:
                    301: func (cv *VartypeCheck) EmulPlatform() {
1.12      rillig    302:        const rePart = `(?:\[[^\]]+\]|[^-\[])+`
                    303:        const rePair = `^(` + rePart + `)-(` + rePart + `)$`
1.16      rillig    304:        if m, opsysPattern, archPattern := match2(cv.Value, rePair); m {
1.12      rillig    305:                opsysCv := &VartypeCheck{
1.16      rillig    306:                        cv.MkLine,
                    307:                        cv.Line,
                    308:                        "the operating system part of " + cv.Varname,
                    309:                        cv.Op,
1.12      rillig    310:                        opsysPattern,
                    311:                        opsysPattern,
1.16      rillig    312:                        cv.MkComment,
                    313:                        cv.Guessed}
1.13      rillig    314:                enumEmulOpsys.checker(opsysCv)
1.1       rillig    315:
                    316:                // no check for os_version
                    317:
1.12      rillig    318:                archCv := &VartypeCheck{
1.16      rillig    319:                        cv.MkLine,
                    320:                        cv.Line,
                    321:                        "the hardware architecture part of " + cv.Varname,
                    322:                        cv.Op,
1.12      rillig    323:                        archPattern,
                    324:                        archPattern,
1.16      rillig    325:                        cv.MkComment,
                    326:                        cv.Guessed}
1.13      rillig    327:                enumEmulArch.checker(archCv)
1.1       rillig    328:        } else {
1.16      rillig    329:                cv.Line.Warn1("%q is not a valid emulation platform.", cv.Value)
1.9       rillig    330:                Explain(
1.1       rillig    331:                        "An emulation platform has the form <OPSYS>-<MACHINE_ARCH>.",
1.9       rillig    332:                        "OPSYS is the lower-case name of the operating system, and",
                    333:                        "MACHINE_ARCH is the hardware architecture.",
1.1       rillig    334:                        "",
1.12      rillig    335:                        "Examples:",
                    336:                        "* linux-i386",
                    337:                        "* irix-mipsel")
1.1       rillig    338:        }
                    339: }
                    340:
                    341: func (cv *VartypeCheck) FetchURL() {
1.16      rillig    342:        cv.MkLine.CheckVartypePrimitive(cv.Varname, CheckvarURL, cv.Op, cv.Value, cv.MkComment, cv.Guessed)
1.1       rillig    343:
1.13      rillig    344:        for siteURL, siteName := range G.globalData.MasterSiteURLToVar {
1.16      rillig    345:                if hasPrefix(cv.Value, siteURL) {
                    346:                        subdir := cv.Value[len(siteURL):]
                    347:                        if hasPrefix(cv.Value, "https://github.com/") {
1.1       rillig    348:                                subdir = strings.SplitAfter(subdir, "/")[0]
1.16      rillig    349:                                cv.Line.Warnf("Please use ${%s:=%s} instead of %q and run \"%s help topic=github\" for further tips.",
                    350:                                        siteName, subdir, cv.Value, confMake)
1.9       rillig    351:                        } else {
1.16      rillig    352:                                cv.Line.Warnf("Please use ${%s:=%s} instead of %q.", siteName, subdir, cv.Value)
1.1       rillig    353:                        }
                    354:                        return
                    355:                }
                    356:        }
1.9       rillig    357:
1.16      rillig    358:        if m, name, subdir := match2(cv.Value, `\$\{(MASTER_SITE_[^:]*).*:=(.*)\}$`); m {
1.13      rillig    359:                if G.globalData.MasterSiteVarToURL[name] == "" {
1.16      rillig    360:                        cv.Line.Error1("The site %s does not exist.", name)
1.9       rillig    361:                }
                    362:                if !hasSuffix(subdir, "/") {
1.16      rillig    363:                        cv.Line.Error1("The subdirectory in %s must end with a slash.", name)
1.9       rillig    364:                }
                    365:        }
1.1       rillig    366: }
                    367:
                    368: // See Pathname
                    369: // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_169
                    370: func (cv *VartypeCheck) Filename() {
                    371:        switch {
1.16      rillig    372:        case cv.Op == opUseMatch:
1.11      rillig    373:                break
1.16      rillig    374:        case contains(cv.ValueNoVar, "/"):
                    375:                cv.Line.Warn0("A filename should not contain a slash.")
                    376:        case !matches(cv.ValueNoVar, `^[-0-9@A-Za-z.,_~+%]*$`):
                    377:                cv.Line.Warn1("%q is not a valid filename.", cv.Value)
1.1       rillig    378:        }
                    379: }
                    380:
                    381: func (cv *VartypeCheck) Filemask() {
1.16      rillig    382:        if cv.Op == opUseMatch {
1.11      rillig    383:                return
                    384:        }
1.16      rillig    385:        if !matches(cv.ValueNoVar, `^[-0-9A-Za-z._~+%*?]*$`) {
                    386:                cv.Line.Warn1("%q is not a valid filename mask.", cv.Value)
1.1       rillig    387:        }
                    388: }
                    389:
                    390: func (cv *VartypeCheck) FileMode() {
                    391:        switch {
1.16      rillig    392:        case cv.Value != "" && cv.ValueNoVar == "":
1.1       rillig    393:                // Fine.
1.16      rillig    394:        case matches(cv.Value, `^[0-7]{3,4}`):
1.1       rillig    395:                // Fine.
                    396:        default:
1.16      rillig    397:                cv.Line.Warn1("Invalid file mode %q.", cv.Value)
1.1       rillig    398:        }
                    399: }
                    400:
1.13      rillig    401: func (cv *VartypeCheck) Homepage() {
1.16      rillig    402:        cv.MkLine.CheckVartypePrimitive(cv.Varname, CheckvarURL, cv.Op, cv.Value, cv.MkComment, cv.Guessed)
1.13      rillig    403:
1.16      rillig    404:        if m, wrong, sitename, subdir := match3(cv.Value, `^(\$\{(MASTER_SITE\w+)(?::=([\w\-/]+))?\})`); m {
1.13      rillig    405:                baseURL := G.globalData.MasterSiteVarToURL[sitename]
                    406:                if sitename == "MASTER_SITES" && G.Pkg != nil {
                    407:                        masterSites, _ := G.Pkg.varValue("MASTER_SITES")
                    408:                        if !containsVarRef(masterSites) {
                    409:                                baseURL = masterSites
                    410:                        }
                    411:                }
                    412:                fixedURL := baseURL + subdir
                    413:                explain := false
                    414:                if baseURL != "" {
1.16      rillig    415:                        if !cv.Line.AutofixReplace(wrong, fixedURL) {
                    416:                                cv.Line.Warn1("HOMEPAGE should not be defined in terms of MASTER_SITEs. Use %s directly.", fixedURL)
1.13      rillig    417:                                explain = true
                    418:                        }
                    419:                } else {
1.16      rillig    420:                        cv.Line.Warn0("HOMEPAGE should not be defined in terms of MASTER_SITEs.")
1.13      rillig    421:                        explain = true
                    422:                }
                    423:                if explain {
                    424:                        Explain(
                    425:                                "The HOMEPAGE is a single URL, while MASTER_SITES is a list of URLs.",
                    426:                                "As long as this list has exactly one element, this works, but as",
                    427:                                "soon as another site is added, the HOMEPAGE would not be a valid",
                    428:                                "URL anymore.",
                    429:                                "",
                    430:                                "Defining MASTER_SITES=${HOMEPAGE} is ok, though.")
                    431:                }
                    432:        }
                    433: }
                    434:
1.1       rillig    435: func (cv *VartypeCheck) Identifier() {
1.16      rillig    436:        if cv.Op == opUseMatch {
                    437:                if cv.Value == cv.ValueNoVar && !matches(cv.Value, `^[\w*?]`) {
                    438:                        cv.Line.Warn2("Invalid identifier pattern %q for %s.", cv.Value, cv.Varname)
1.11      rillig    439:                }
                    440:                return
                    441:        }
1.16      rillig    442:        if cv.Value != cv.ValueNoVar {
1.1       rillig    443:                //line.logWarning("Identifiers should be given directly.")
                    444:        }
                    445:        switch {
1.16      rillig    446:        case matches(cv.ValueNoVar, `^[+\-.0-9A-Z_a-z]+$`):
1.1       rillig    447:                // Fine.
1.16      rillig    448:        case cv.Value != "" && cv.ValueNoVar == "":
1.1       rillig    449:                // Don't warn here.
                    450:        default:
1.16      rillig    451:                cv.Line.Warn1("Invalid identifier %q.", cv.Value)
1.1       rillig    452:        }
                    453: }
                    454:
                    455: func (cv *VartypeCheck) Integer() {
1.16      rillig    456:        if !matches(cv.Value, `^\d+$`) {
                    457:                cv.Line.Warn1("Invalid integer %q.", cv.Value)
1.1       rillig    458:        }
                    459: }
                    460:
                    461: func (cv *VartypeCheck) LdFlag() {
1.16      rillig    462:        if cv.Op == opUseMatch {
1.11      rillig    463:                return
                    464:        }
1.16      rillig    465:        ldflag := cv.Value
1.9       rillig    466:        if m, rpathFlag := match1(ldflag, `^(-Wl,(?:-R|-rpath|--rpath))`); m {
1.16      rillig    467:                cv.Line.Warn1("Please use \"${COMPILER_RPATH_FLAG}\" instead of %q.", rpathFlag)
1.1       rillig    468:                return
1.9       rillig    469:        }
1.1       rillig    470:
1.9       rillig    471:        switch {
                    472:        case hasPrefix(ldflag, "-L"),
                    473:                hasPrefix(ldflag, "-l"),
                    474:                ldflag == "-pthread",
                    475:                ldflag == "-static",
                    476:                hasPrefix(ldflag, "-static-"),
                    477:                hasPrefix(ldflag, "-Wl,-"),
                    478:                hasPrefix(ldflag, "`") && hasSuffix(ldflag, "`"),
1.16      rillig    479:                ldflag != cv.ValueNoVar:
1.9       rillig    480:                return
                    481:        case hasPrefix(ldflag, "-"):
1.16      rillig    482:                cv.Line.Warn1("Unknown linker flag %q.", cv.Value)
1.9       rillig    483:        default:
1.16      rillig    484:                cv.Line.Warn1("Linker flag %q should start with a hypen.", cv.Value)
1.1       rillig    485:        }
                    486: }
                    487:
                    488: func (cv *VartypeCheck) License() {
1.16      rillig    489:        checklineLicense(cv.MkLine, cv.Value)
1.1       rillig    490: }
                    491:
1.13      rillig    492: func (cv *VartypeCheck) MachineGnuPlatform() {
1.16      rillig    493:        if cv.Value != cv.ValueNoVar {
1.13      rillig    494:                return
                    495:        }
                    496:
                    497:        const rePart = `(?:\[[^\]]+\]|[^-\[])+`
                    498:        const rePair = `^(` + rePart + `)-(` + rePart + `)$`
                    499:        const reTriple = `^(` + rePart + `)-(` + rePart + `)-(` + rePart + `)$`
                    500:
1.16      rillig    501:        pattern := cv.Value
1.13      rillig    502:        if matches(pattern, rePair) && hasSuffix(pattern, "*") {
                    503:                pattern += "-*"
                    504:        }
                    505:
                    506:        if m, archPattern, vendorPattern, opsysPattern := match3(pattern, reTriple); m {
                    507:                archCv := &VartypeCheck{
1.16      rillig    508:                        cv.MkLine,
                    509:                        cv.Line,
                    510:                        "the hardware architecture part of " + cv.Varname,
1.13      rillig    511:                        opUseMatch, // Always allow patterns, since this is a PlatformPattern.
                    512:                        archPattern,
                    513:                        archPattern,
1.16      rillig    514:                        cv.MkComment,
                    515:                        cv.Guessed}
1.13      rillig    516:                enumMachineGnuArch.checker(archCv)
                    517:
                    518:                _ = vendorPattern
                    519:
                    520:                opsysCv := &VartypeCheck{
1.16      rillig    521:                        cv.MkLine,
                    522:                        cv.Line,
                    523:                        "the operating system part of " + cv.Varname,
1.13      rillig    524:                        opUseMatch, // Always allow patterns, since this is a PlatformPattern.
                    525:                        opsysPattern,
                    526:                        opsysPattern,
1.16      rillig    527:                        cv.MkComment,
                    528:                        cv.Guessed}
1.13      rillig    529:                enumMachineGnuPlatformOpsys.checker(opsysCv)
                    530:
                    531:        } else {
1.16      rillig    532:                cv.Line.Warn1("%q is not a valid platform pattern.", cv.Value)
1.13      rillig    533:                Explain(
                    534:                        "A platform pattern has the form <OPSYS>-<OS_VERSION>-<MACHINE_ARCH>.",
                    535:                        "Each of these components may be a shell globbing expression.",
                    536:                        "",
                    537:                        "Examples:",
                    538:                        "* NetBSD-[456].*-i386",
                    539:                        "* *-*-*",
                    540:                        "* Linux-*-*")
                    541:        }
                    542: }
                    543:
1.1       rillig    544: func (cv *VartypeCheck) MailAddress() {
1.16      rillig    545:        line, value := cv.Line, cv.Value
1.1       rillig    546:
                    547:        if m, _, domain := match2(value, `^([+\-.0-9A-Z_a-z]+)@([-\w\d.]+)$`); m {
                    548:                if strings.EqualFold(domain, "NetBSD.org") && domain != "NetBSD.org" {
1.9       rillig    549:                        line.Warn1("Please write \"NetBSD.org\" instead of %q.", domain)
1.1       rillig    550:                }
                    551:                if matches(value, `(?i)^(tech-pkg|packages)@NetBSD\.org$`) {
1.9       rillig    552:                        line.Error0("This mailing list address is obsolete. Use pkgsrc-users@NetBSD.org instead.")
1.1       rillig    553:                }
                    554:
                    555:        } else {
1.9       rillig    556:                line.Warn1("\"%s\" is not a valid mail address.", value)
1.1       rillig    557:        }
                    558: }
                    559:
                    560: // See ${STEP_MSG}, ${PKG_FAIL_REASON}
                    561: func (cv *VartypeCheck) Message() {
1.16      rillig    562:        line, varname, value := cv.Line, cv.Varname, cv.Value
1.1       rillig    563:
                    564:        if matches(value, `^[\"'].*[\"']$`) {
1.9       rillig    565:                line.Warn1("%s should not be quoted.", varname)
                    566:                Explain(
1.1       rillig    567:                        "The quoting is only needed for variables which are interpreted as",
1.9       rillig    568:                        "multiple words (or, generally speaking, a list of something).  A",
                    569:                        "single text message does not belong to this class, since it is only",
                    570:                        "printed as a whole.",
1.1       rillig    571:                        "",
1.9       rillig    572:                        "On the other hand, PKG_FAIL_REASON is a _list_ of text messages, so",
                    573:                        "in that case, the quoting has to be done.")
1.1       rillig    574:        }
                    575: }
                    576:
                    577: // A package option from options.mk
                    578: func (cv *VartypeCheck) Option() {
1.16      rillig    579:        line, value, valueNovar := cv.Line, cv.Value, cv.ValueNoVar
1.1       rillig    580:
                    581:        if value != valueNovar {
1.13      rillig    582:                if G.opts.Debug {
                    583:                        traceStep1("Unchecked option name: %q", value)
1.9       rillig    584:                }
1.1       rillig    585:                return
                    586:        }
                    587:
1.9       rillig    588:        if m, optname := match1(value, `^-?([a-z][-0-9a-z+]*)$`); m {
                    589:                if _, found := G.globalData.PkgOptions[optname]; !found { // There’s a difference between empty and absent here.
                    590:                        line.Warn1("Unknown option \"%s\".", optname)
                    591:                        Explain4(
1.1       rillig    592:                                "This option is not documented in the mk/defaults/options.description",
1.9       rillig    593:                                "file.  Please think of a brief but precise description and either",
                    594:                                "update that file yourself or suggest a description for this option",
                    595:                                "on the tech-pkg@NetBSD.org mailing list.")
1.1       rillig    596:                }
                    597:                return
                    598:        }
                    599:
                    600:        if matches(value, `^-?([a-z][-0-9a-z_\+]*)$`) {
1.9       rillig    601:                line.Warn0("Use of the underscore character in option names is deprecated.")
1.1       rillig    602:                return
                    603:        }
                    604:
1.9       rillig    605:        line.Error1("Invalid option name %q. Option names must start with a lowercase letter and be all-lowercase.", value)
1.1       rillig    606: }
                    607:
                    608: // The PATH environment variable
                    609: func (cv *VartypeCheck) Pathlist() {
1.16      rillig    610:        if !contains(cv.Value, ":") && cv.Guessed {
                    611:                cv.MkLine.CheckVartypePrimitive(cv.Varname, CheckvarPathname, cv.Op, cv.Value, cv.MkComment, cv.Guessed)
1.1       rillig    612:                return
                    613:        }
                    614:
1.16      rillig    615:        for _, path := range strings.Split(cv.Value, ":") {
1.1       rillig    616:                if contains(path, "${") {
                    617:                        continue
                    618:                }
                    619:
                    620:                if !matches(path, `^[-0-9A-Za-z._~+%/]*$`) {
1.16      rillig    621:                        cv.Line.Warn1("%q is not a valid pathname.", path)
1.1       rillig    622:                }
                    623:
                    624:                if !hasPrefix(path, "/") {
1.16      rillig    625:                        cv.Line.Warn2("All components of %s (in this case %q) should be absolute paths.", cv.Varname, path)
1.1       rillig    626:                }
                    627:        }
                    628: }
                    629:
                    630: // Shell globbing including slashes.
                    631: // See Filemask
                    632: func (cv *VartypeCheck) Pathmask() {
1.16      rillig    633:        if cv.Op == opUseMatch {
1.11      rillig    634:                return
                    635:        }
1.16      rillig    636:        if !matches(cv.ValueNoVar, `^[#\-0-9A-Za-z._~+%*?/\[\]]*`) {
                    637:                cv.Line.Warn1("%q is not a valid pathname mask.", cv.Value)
1.1       rillig    638:        }
1.16      rillig    639:        cv.Line.CheckAbsolutePathname(cv.Value)
1.1       rillig    640: }
                    641:
                    642: // Like Filename, but including slashes
                    643: // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266
                    644: func (cv *VartypeCheck) Pathname() {
1.16      rillig    645:        if cv.Op == opUseMatch {
1.11      rillig    646:                return
                    647:        }
1.16      rillig    648:        if !matches(cv.ValueNoVar, `^[#\-0-9A-Za-z._~+%/]*$`) {
                    649:                cv.Line.Warn1("%q is not a valid pathname.", cv.Value)
1.1       rillig    650:        }
1.16      rillig    651:        cv.Line.CheckAbsolutePathname(cv.Value)
1.1       rillig    652: }
                    653:
                    654: func (cv *VartypeCheck) Perl5Packlist() {
1.16      rillig    655:        if cv.Value != cv.ValueNoVar {
                    656:                cv.Line.Warn1("%s should not depend on other variables.", cv.Varname)
1.1       rillig    657:        }
                    658: }
                    659:
1.14      rillig    660: func (cv *VartypeCheck) Perms() {
1.16      rillig    661:        if cv.Value == "${ROOT_USER}" || cv.Value == "${ROOT_GROUP}" {
                    662:                valuename := cv.Value[2 : len(cv.Value)-1]
                    663:                cv.Line.Error1("%s must not be used in permission definitions. Use REAL_%[1]s instead.", valuename)
1.14      rillig    664:        }
                    665: }
                    666:
1.1       rillig    667: func (cv *VartypeCheck) PkgName() {
1.16      rillig    668:        if cv.Op != opUseMatch && cv.Value == cv.ValueNoVar && !matches(cv.Value, rePkgname) {
                    669:                cv.Line.Warn1("%q is not a valid package name. A valid package name has the form packagename-version, where version consists only of digits, letters and dots.", cv.Value)
1.1       rillig    670:        }
                    671: }
                    672:
                    673: func (cv *VartypeCheck) PkgOptionsVar() {
1.16      rillig    674:        cv.MkLine.CheckVartypePrimitive(cv.Varname, CheckvarVariableName, cv.Op, cv.Value, cv.MkComment, cv.Guessed)
                    675:        if matches(cv.Value, `\$\{PKGBASE[:\}]`) {
                    676:                cv.Line.Error0("PKGBASE must not be used in PKG_OPTIONS_VAR.")
1.9       rillig    677:                Explain3(
1.1       rillig    678:                        "PKGBASE is defined in bsd.pkg.mk, which is included as the",
                    679:                        "very last file, but PKG_OPTIONS_VAR is evaluated earlier.",
                    680:                        "Use ${PKGNAME:C/-[0-9].*//} instead.")
                    681:        }
1.15      rillig    682:
                    683:        // PR 46570, item "6. It should complain in PKG_OPTIONS_VAR is wrong"
1.16      rillig    684:        if !hasPrefix(cv.Value, "PKG_OPTIONS.") {
                    685:                cv.Line.Error2("PKG_OPTIONS_VAR must be of the form %q, not %q.", "PKG_OPTIONS.*", cv.Value)
1.15      rillig    686:        }
1.1       rillig    687: }
                    688:
                    689: // A directory name relative to the top-level pkgsrc directory.
                    690: // Despite its name, it is more similar to RelativePkgDir than to RelativePkgPath.
                    691: func (cv *VartypeCheck) PkgPath() {
1.16      rillig    692:        cv.MkLine.CheckRelativePkgdir(G.CurPkgsrcdir + "/" + cv.Value)
1.1       rillig    693: }
                    694:
                    695: func (cv *VartypeCheck) PkgRevision() {
1.16      rillig    696:        if !matches(cv.Value, `^[1-9]\d*$`) {
                    697:                cv.Line.Warn1("%s must be a positive integer number.", cv.Varname)
1.1       rillig    698:        }
1.16      rillig    699:        if path.Base(cv.Line.Fname) != "Makefile" {
                    700:                cv.Line.Error1("%s only makes sense directly in the package Makefile.", cv.Varname)
1.9       rillig    701:                Explain(
1.1       rillig    702:                        "Usually, different packages using the same Makefile.common have",
1.9       rillig    703:                        "different dependencies and will be bumped at different times (e.g.",
                    704:                        "for shlib major bumps) and thus the PKGREVISIONs must be in the",
                    705:                        "separate Makefiles.  There is no practical way of having this",
                    706:                        "information in a commonly used Makefile.")
1.1       rillig    707:        }
                    708: }
                    709:
1.13      rillig    710: func (cv *VartypeCheck) MachinePlatform() {
                    711:        cv.MachinePlatformPattern()
                    712: }
                    713:
                    714: func (cv *VartypeCheck) MachinePlatformPattern() {
1.16      rillig    715:        if cv.Value != cv.ValueNoVar {
1.1       rillig    716:                return
                    717:        }
                    718:
1.12      rillig    719:        const rePart = `(?:\[[^\]]+\]|[^-\[])+`
1.13      rillig    720:        const rePair = `^(` + rePart + `)-(` + rePart + `)$`
1.12      rillig    721:        const reTriple = `^(` + rePart + `)-(` + rePart + `)-(` + rePart + `)$`
1.13      rillig    722:
1.16      rillig    723:        pattern := cv.Value
1.13      rillig    724:        if matches(pattern, rePair) && hasSuffix(pattern, "*") {
                    725:                pattern += "-*"
                    726:        }
                    727:
                    728:        if m, opsysPattern, _, archPattern := match3(pattern, reTriple); m {
1.12      rillig    729:                opsysCv := &VartypeCheck{
1.16      rillig    730:                        cv.MkLine,
                    731:                        cv.Line,
                    732:                        "the operating system part of " + cv.Varname,
1.12      rillig    733:                        opUseMatch, // Always allow patterns, since this is a PlatformPattern.
                    734:                        opsysPattern,
                    735:                        opsysPattern,
1.16      rillig    736:                        cv.MkComment,
                    737:                        cv.Guessed}
1.13      rillig    738:                enumMachineOpsys.checker(opsysCv)
1.12      rillig    739:
1.1       rillig    740:                // no check for os_version
1.12      rillig    741:
                    742:                archCv := &VartypeCheck{
1.16      rillig    743:                        cv.MkLine,
                    744:                        cv.Line,
                    745:                        "the hardware architecture part of " + cv.Varname,
1.12      rillig    746:                        opUseMatch, // Always allow patterns, since this is a PlatformPattern.
                    747:                        archPattern,
                    748:                        archPattern,
1.16      rillig    749:                        cv.MkComment,
                    750:                        cv.Guessed}
1.13      rillig    751:                enumMachineArch.checker(archCv)
1.1       rillig    752:
                    753:        } else {
1.16      rillig    754:                cv.Line.Warn1("%q is not a valid platform pattern.", cv.Value)
1.12      rillig    755:                Explain(
                    756:                        "A platform pattern has the form <OPSYS>-<OS_VERSION>-<MACHINE_ARCH>.",
1.1       rillig    757:                        "Each of these components may be a shell globbing expression.",
1.12      rillig    758:                        "",
                    759:                        "Examples:",
                    760:                        "* NetBSD-[456].*-i386",
                    761:                        "* *-*-*",
                    762:                        "* Linux-*-*")
1.1       rillig    763:        }
                    764: }
                    765:
1.12      rillig    766: // A pathname relative to ${PREFIX}.
1.1       rillig    767: func (cv *VartypeCheck) PrefixPathname() {
1.16      rillig    768:        if m, mansubdir := match1(cv.Value, `^man/(.+)`); m {
                    769:                cv.Line.Warn2("Please use \"${PKGMANDIR}/%s\" instead of %q.", mansubdir, cv.Value)
1.1       rillig    770:        }
                    771: }
                    772:
                    773: func (cv *VartypeCheck) PythonDependency() {
1.16      rillig    774:        if cv.Value != cv.ValueNoVar {
                    775:                cv.Line.Warn0("Python dependencies should not contain variables.")
                    776:        } else if !matches(cv.ValueNoVar, `^[+\-.0-9A-Z_a-z]+(?:|:link|:build)$`) {
                    777:                cv.Line.Warn1("Invalid Python dependency %q.", cv.Value)
1.9       rillig    778:                Explain4(
                    779:                        "Python dependencies must be an identifier for a package, as",
                    780:                        "specified in lang/python/versioned_dependencies.mk.  This",
                    781:                        "identifier may be followed by :build for a build-time only",
                    782:                        "dependency, or by :link for a run-time only dependency.")
1.1       rillig    783:        }
                    784: }
                    785:
1.12      rillig    786: // Refers to a package directory, e.g. ../../category/pkgbase.
1.1       rillig    787: func (cv *VartypeCheck) RelativePkgDir() {
1.16      rillig    788:        cv.MkLine.CheckRelativePkgdir(cv.Value)
1.1       rillig    789: }
                    790:
1.12      rillig    791: // Refers to a file or directory, e.g. ../../category/pkgbase, ../../category/pkgbase/Makefile.
1.1       rillig    792: func (cv *VartypeCheck) RelativePkgPath() {
1.16      rillig    793:        cv.MkLine.CheckRelativePath(cv.Value, true)
1.1       rillig    794: }
                    795:
                    796: func (cv *VartypeCheck) Restricted() {
1.16      rillig    797:        if cv.Value != "${RESTRICTED}" {
                    798:                cv.Line.Warn1("The only valid value for %s is ${RESTRICTED}.", cv.Varname)
1.9       rillig    799:                Explain3(
                    800:                        "These variables are used to control which files may be mirrored on",
                    801:                        "FTP servers or CD-ROM collections.  They are not intended to mark",
                    802:                        "packages whose only MASTER_SITES are on ftp.NetBSD.org.")
1.1       rillig    803:        }
                    804: }
                    805:
                    806: func (cv *VartypeCheck) SedCommand() {
                    807: }
                    808:
                    809: func (cv *VartypeCheck) SedCommands() {
1.16      rillig    810:        line := cv.Line
                    811:        mkline := cv.MkLine
1.1       rillig    812:
1.16      rillig    813:        tokens, rest := splitIntoShellTokens(line, cv.Value)
1.1       rillig    814:        if rest != "" {
1.9       rillig    815:                if strings.Contains(line.Text, "#") {
                    816:                        line.Error1("Invalid shell words %q in sed commands.", rest)
                    817:                        Explain4(
1.1       rillig    818:                                "When sed commands have embedded \"#\" characters, they need to be",
                    819:                                "escaped with a backslash, otherwise make(1) will interpret them as a",
                    820:                                "comment, no matter if they occur in single or double quotes or",
                    821:                                "whatever.")
                    822:                }
                    823:                return
                    824:        }
                    825:
1.9       rillig    826:        ntokens := len(tokens)
1.1       rillig    827:        ncommands := 0
                    828:
1.9       rillig    829:        for i := 0; i < ntokens; i++ {
                    830:                token := tokens[i]
1.1       rillig    831:
                    832:                switch {
1.9       rillig    833:                case token == "-e":
                    834:                        if i+1 < ntokens {
1.1       rillig    835:                                // Check the real sed command here.
                    836:                                i++
                    837:                                ncommands++
                    838:                                if ncommands > 1 {
1.9       rillig    839:                                        line.Note0("Each sed command should appear in an assignment of its own.")
                    840:                                        Explain(
1.1       rillig    841:                                                "For example, instead of",
                    842:                                                "    SUBST_SED.foo+=        -e s,command1,, -e s,command2,,",
                    843:                                                "use",
                    844:                                                "    SUBST_SED.foo+=        -e s,command1,,",
                    845:                                                "    SUBST_SED.foo+=        -e s,command2,,",
                    846:                                                "",
                    847:                                                "This way, short sed commands cannot be hidden at the end of a line.")
                    848:                                }
1.16      rillig    849:                                mkline.CheckVartypePrimitive(cv.Varname, CheckvarSedCommand, cv.Op, tokens[i], cv.MkComment, cv.Guessed)
1.1       rillig    850:                        } else {
1.9       rillig    851:                                line.Error0("The -e option to sed requires an argument.")
1.1       rillig    852:                        }
1.9       rillig    853:                case token == "-E":
1.1       rillig    854:                        // Switch to extended regular expressions mode.
                    855:
1.9       rillig    856:                case token == "-n":
1.1       rillig    857:                        // Don't print lines per default.
                    858:
1.9       rillig    859:                case i == 0 && matches(token, `^(["']?)(?:\d*|/.*/)s.+["']?$`):
                    860:                        line.Note0("Please always use \"-e\" in sed commands, even if there is only one substitution.")
1.1       rillig    861:
                    862:                default:
1.9       rillig    863:                        line.Warn1("Unknown sed command %q.", token)
1.1       rillig    864:                }
                    865:        }
                    866: }
                    867:
                    868: func (cv *VartypeCheck) ShellCommand() {
1.16      rillig    869:        if cv.Op == opUseMatch || cv.Op == opUse {
1.11      rillig    870:                return
                    871:        }
1.9       rillig    872:        setE := true
1.16      rillig    873:        NewShellLine(cv.MkLine).CheckShellCommand(cv.Value, &setE)
1.9       rillig    874: }
                    875:
                    876: // Zero or more shell commands, each terminated with a semicolon.
                    877: func (cv *VartypeCheck) ShellCommands() {
1.16      rillig    878:        NewShellLine(cv.MkLine).CheckShellCommands(cv.Value)
1.1       rillig    879: }
                    880:
                    881: func (cv *VartypeCheck) ShellWord() {
1.16      rillig    882:        NewShellLine(cv.MkLine).CheckWord(cv.Value, true)
1.1       rillig    883: }
                    884:
                    885: func (cv *VartypeCheck) Stage() {
1.16      rillig    886:        if !matches(cv.Value, `^(?:pre|do|post)-(?:extract|patch|configure|build|test|install)`) {
                    887:                cv.Line.Warn1("Invalid stage name %q. Use one of {pre,do,post}-{extract,patch,configure,build,test,install}.", cv.Value)
1.1       rillig    888:        }
                    889: }
                    890:
                    891: func (cv *VartypeCheck) String() {
                    892:        // No further checks possible.
                    893: }
                    894:
                    895: func (cv *VartypeCheck) Tool() {
1.16      rillig    896:        if cv.Varname == "TOOLS_NOOP" && cv.Op == opAssignAppend {
1.1       rillig    897:                // no warning for package-defined tool definitions
                    898:
1.16      rillig    899:        } else if m, toolname, tooldep := match2(cv.Value, `^([-\w]+|\[)(?::(\w+))?$`); m {
1.13      rillig    900:                if G.globalData.Tools.byName[toolname] == nil {
1.16      rillig    901:                        cv.Line.Error1("Unknown tool %q.", toolname)
1.1       rillig    902:                }
                    903:                switch tooldep {
                    904:                case "", "bootstrap", "build", "pkgsrc", "run":
                    905:                default:
1.16      rillig    906:                        cv.Line.Error1("Unknown tool dependency %q. Use one of \"build\", \"pkgsrc\" or \"run\".", tooldep)
1.1       rillig    907:                }
1.16      rillig    908:        } else if cv.Op != opUseMatch {
                    909:                cv.Line.Error1("Invalid tool syntax: %q.", cv.Value)
1.1       rillig    910:        }
                    911: }
                    912:
                    913: func (cv *VartypeCheck) Unchecked() {
                    914:        // Do nothing, as the name says.
                    915: }
                    916:
                    917: func (cv *VartypeCheck) URL() {
1.16      rillig    918:        line, value := cv.Line, cv.Value
1.1       rillig    919:
1.16      rillig    920:        if value == "" && hasPrefix(cv.MkComment, "#") {
1.1       rillig    921:                // Ok
                    922:
                    923:        } else if containsVarRef(value) {
                    924:                // No further checks
                    925:
1.9       rillig    926:        } else if m, _, host, _, _ := match4(value, `^(https?|ftp|gopher)://([-0-9A-Za-z.]+)(?::(\d+))?/([-%&+,./0-9:;=?@A-Z_a-z~]|#)*$`); m {
1.1       rillig    927:                if matches(host, `(?i)\.NetBSD\.org$`) && !matches(host, `\.NetBSD\.org$`) {
1.9       rillig    928:                        line.Warn1("Please write NetBSD.org instead of %s.", host)
1.1       rillig    929:                }
                    930:
                    931:        } else if m, scheme, _, absPath := match3(value, `^([0-9A-Za-z]+)://([^/]+)(.*)$`); m {
                    932:                switch {
                    933:                case scheme != "ftp" && scheme != "http" && scheme != "https" && scheme != "gopher":
1.9       rillig    934:                        line.Warn1("%q is not a valid URL. Only ftp, gopher, http, and https URLs are allowed here.", value)
1.1       rillig    935:
                    936:                case absPath == "":
1.9       rillig    937:                        line.Note1("For consistency, please add a trailing slash to %q.", value)
1.1       rillig    938:
                    939:                default:
1.9       rillig    940:                        line.Warn1("%q is not a valid URL.", value)
1.1       rillig    941:                }
                    942:
                    943:        } else {
1.9       rillig    944:                line.Warn1("%q is not a valid URL.", value)
1.1       rillig    945:        }
                    946: }
                    947:
                    948: func (cv *VartypeCheck) UserGroupName() {
1.16      rillig    949:        if cv.Value == cv.ValueNoVar && !matches(cv.Value, `^[0-9_a-z]+$`) {
                    950:                cv.Line.Warn1("Invalid user or group name %q.", cv.Value)
1.1       rillig    951:        }
                    952: }
                    953:
1.16      rillig    954: func (cv *VartypeCheck) VariableName() {
                    955:        if cv.Value == cv.ValueNoVar && !matches(cv.Value, `^[A-Z_][0-9A-Z_]*(?:[.].*)?$`) {
                    956:                cv.Line.Warn1("%q is not a valid variable name.", cv.Value)
1.9       rillig    957:                Explain(
1.1       rillig    958:                        "Variable names are restricted to only uppercase letters and the",
                    959:                        "underscore in the basename, and arbitrary characters in the",
                    960:                        "parameterized part, following the dot.",
                    961:                        "",
                    962:                        "Examples:",
                    963:                        "\t* PKGNAME",
                    964:                        "\t* PKG_OPTIONS.gnuchess")
                    965:        }
                    966: }
                    967:
                    968: func (cv *VartypeCheck) Version() {
1.16      rillig    969:        if cv.Op == opUseMatch {
                    970:                if !matches(cv.Value, `^[\d?\[][\w\-.*?\[\]]+$`) {
                    971:                        cv.Line.Warn1("Invalid version number pattern %q.", cv.Value)
1.11      rillig    972:                }
1.17    ! rillig    973:        } else if cv.Value == cv.ValueNoVar && !matches(cv.Value, `^\d[\w.]*$`) {
1.16      rillig    974:                cv.Line.Warn1("Invalid version number %q.", cv.Value)
1.1       rillig    975:        }
                    976: }
                    977:
                    978: func (cv *VartypeCheck) WrapperReorder() {
1.16      rillig    979:        if !matches(cv.Value, `^reorder:l:([\w\-]+):([\w\-]+)$`) {
                    980:                cv.Line.Warn1("Unknown wrapper reorder command %q.", cv.Value)
1.1       rillig    981:        }
                    982: }
                    983:
                    984: func (cv *VartypeCheck) WrapperTransform() {
1.16      rillig    985:        cmd := cv.Value
1.9       rillig    986:        if hasPrefix(cmd, "rm:-") ||
                    987:                matches(cmd, `^(R|l|rpath):([^:]+):(.+)$`) ||
                    988:                matches(cmd, `^'?(opt|rename|rm-optarg|rmdir):.*$`) ||
                    989:                cmd == "-e" ||
                    990:                matches(cmd, `^["']?s[|:,]`) {
                    991:                return
1.1       rillig    992:        }
1.16      rillig    993:        cv.Line.Warn1("Unknown wrapper transform command %q.", cmd)
1.1       rillig    994: }
                    995:
                    996: func (cv *VartypeCheck) WrkdirSubdirectory() {
1.16      rillig    997:        cv.MkLine.CheckVartypePrimitive(cv.Varname, CheckvarPathname, cv.Op, cv.Value, cv.MkComment, cv.Guessed)
1.1       rillig    998: }
                    999:
                   1000: // A directory relative to ${WRKSRC}, for use in CONFIGURE_DIRS and similar variables.
                   1001: func (cv *VartypeCheck) WrksrcSubdirectory() {
1.16      rillig   1002:        if m, _, rest := match2(cv.Value, `^(\$\{WRKSRC\})(?:/(.*))?`); m {
1.1       rillig   1003:                if rest == "" {
                   1004:                        rest = "."
                   1005:                }
1.16      rillig   1006:                cv.Line.Note2("You can use %q instead of %q.", rest, cv.Value)
1.9       rillig   1007:                Explain1(
                   1008:                        "These directories are interpreted relative to ${WRKSRC}.")
1.1       rillig   1009:
1.16      rillig   1010:        } else if cv.Value != "" && cv.ValueNoVar == "" {
1.1       rillig   1011:                // The value of another variable
                   1012:
1.16      rillig   1013:        } else if !matches(cv.ValueNoVar, `^(?:\.|[0-9A-Za-z_@][-0-9A-Za-z_@./+]*)$`) {
                   1014:                cv.Line.Warn1("%q is not a valid subdirectory of ${WRKSRC}.", cv.Value)
1.1       rillig   1015:        }
                   1016: }
                   1017:
                   1018: func (cv *VartypeCheck) Yes() {
1.16      rillig   1019:        switch cv.Op {
1.11      rillig   1020:        case opUseMatch:
1.16      rillig   1021:                cv.Line.Warn1("%s should only be used in a \".if defined(...)\" conditional.", cv.Varname)
1.11      rillig   1022:                Explain(
                   1023:                        "This variable can have only two values: defined or undefined.",
                   1024:                        "When it is defined, it means \"yes\", even when its value is",
                   1025:                        "\"no\" or the empty string.",
                   1026:                        "",
                   1027:                        "Therefore, it should not be checked by comparing its value",
                   1028:                        "but using \".if defined(VARNAME)\" alone.")
                   1029:
                   1030:        default:
1.16      rillig   1031:                if !matches(cv.Value, `^(?:YES|yes)(?:\s+#.*)?$`) {
                   1032:                        cv.Line.Warn1("%s should be set to YES or yes.", cv.Varname)
1.11      rillig   1033:                        Explain4(
                   1034:                                "This variable means \"yes\" if it is defined, and \"no\" if it is",
                   1035:                                "undefined.  Even when it has the value \"no\", this means \"yes\".",
                   1036:                                "Therefore when it is defined, its value should correspond to its",
                   1037:                                "meaning.")
                   1038:                }
1.1       rillig   1039:        }
                   1040: }
                   1041:
                   1042: func (cv *VartypeCheck) YesNo() {
1.13      rillig   1043:        const (
                   1044:                yes1 = "[yY][eE][sS]"
                   1045:                yes2 = "[Yy][Ee][Ss]"
                   1046:                no1  = "[nN][oO]"
                   1047:                no2  = "[Nn][Oo]"
                   1048:        )
1.16      rillig   1049:        if cv.Op == opUseMatch {
                   1050:                switch cv.Value {
1.13      rillig   1051:                case yes1, yes2, no1, no2:
1.11      rillig   1052:                default:
1.16      rillig   1053:                        cv.Line.Warnf("%s should be matched against %q or %q, not %q.", cv.Varname, yes1, no1, cv.Value)
1.11      rillig   1054:                }
1.16      rillig   1055:        } else if cv.Op == opUse {
                   1056:                cv.Line.Warnf("%s should be matched against %q or %q, not compared with %q.", cv.Varname, yes1, no1, cv.Value)
1.13      rillig   1057:                Explain(
                   1058:                        "The yes/no value can be written in either upper or lower case, and",
                   1059:                        "both forms are actually used.  As long as this is the case, when",
                   1060:                        "checking the variable value, both must be accepted.")
1.16      rillig   1061:        } else if !matches(cv.Value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) {
                   1062:                cv.Line.Warn1("%s should be set to YES, yes, NO, or no.", cv.Varname)
1.1       rillig   1063:        }
                   1064: }
                   1065:
1.3       rillig   1066: func (cv *VartypeCheck) YesNoIndirectly() {
1.16      rillig   1067:        if cv.ValueNoVar != "" {
1.11      rillig   1068:                cv.YesNo()
1.1       rillig   1069:        }
                   1070: }

CVSweb <webmaster@jp.NetBSD.org>