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

1.1       rillig      1: package main
                      2:
                      3: import (
                      4:        "path"
                      5:        "strings"
                      6: )
                      7:
                      8: type VartypeCheck struct {
1.9     ! rillig      9:        mkline      *MkLine
1.1       rillig     10:        line        *Line
                     11:        varname     string
1.9     ! rillig     12:        op          MkOperator
1.1       rillig     13:        value       string
                     14:        valueNovar  string
                     15:        comment     string
                     16:        listContext bool
1.9     ! rillig     17:        guessed     bool // Whether the type definition is guessed (based on the variable name) or explicitly defined (see vardefs.go).
        !            18: }
        !            19:
        !            20: type MkOperator uint8
        !            21:
        !            22: const (
        !            23:        opAssign        MkOperator = iota // =
        !            24:        opAssignShell                     // !=
        !            25:        opAssignEval                      // :=
        !            26:        opAssignAppend                    // +=
        !            27:        opAssignDefault                   // ?=
        !            28:        opUseLoadtime
        !            29:        opUse
        !            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 {
        !            49:        return [...]string{"=", "!=", ":=", "+=", "?=", "use", "use-loadtime"}[op]
1.1       rillig     50: }
                     51:
                     52: func (cv *VartypeCheck) AwkCommand() {
1.9     ! rillig     53:        if G.opts.DebugUnchecked {
        !            54:                cv.line.Debug1("Unchecked AWK command: %q", cv.value)
        !            55:        }
1.1       rillig     56: }
                     57:
                     58: func (cv *VartypeCheck) BasicRegularExpression() {
1.9     ! rillig     59:        if G.opts.DebugUnchecked {
        !            60:                cv.line.Debug1("Unchecked basic regular expression: %q", cv.value)
        !            61:        }
1.1       rillig     62: }
                     63:
                     64: func (cv *VartypeCheck) BuildlinkDepmethod() {
                     65:        if !containsVarRef(cv.value) && cv.value != "build" && cv.value != "full" {
1.9     ! rillig     66:                cv.line.Warn1("Invalid dependency method %q. Valid methods are \"build\" or \"full\".", cv.value)
1.1       rillig     67:        }
                     68: }
                     69:
                     70: func (cv *VartypeCheck) Category() {
1.9     ! rillig     71:        if fileExists(G.CurrentDir + "/" + G.CurPkgsrcdir + "/" + cv.value + "/Makefile") {
1.1       rillig     72:                return
                     73:        }
                     74:        switch cv.value {
                     75:        case
                     76:                "chinese", "crosspkgtools",
                     77:                "gnome", "gnustep",
                     78:                "japanese", "java",
                     79:                "kde", "korean",
                     80:                "linux", "local",
                     81:                "packages", "perl5", "plan9", "python",
                     82:                "ruby",
                     83:                "scm",
                     84:                "tcl", "tk",
                     85:                "windowmaker",
                     86:                "xmms":
                     87:        default:
1.9     ! rillig     88:                cv.line.Error1("Invalid category %q.", cv.value)
1.1       rillig     89:        }
                     90: }
                     91:
                     92: // A single option to the C/C++ compiler.
                     93: func (cv *VartypeCheck) CFlag() {
1.9     ! rillig     94:        cflag := cv.value
1.1       rillig     95:        switch {
1.9     ! rillig     96:        case matches(cflag, `^-[DILOUWfgm]`),
        !            97:                hasPrefix(cflag, "-std="),
        !            98:                cflag == "-c99",
        !            99:                cflag == "-c",
        !           100:                cflag == "-no-integrated-as",
        !           101:                cflag == "-pthread",
        !           102:                hasPrefix(cflag, "`") && hasSuffix(cflag, "`"),
        !           103:                containsVarRef(cflag):
        !           104:                return
        !           105:        case hasPrefix(cflag, "-"):
        !           106:                cv.line.Warn1("Unknown compiler flag %q.", cflag)
        !           107:        default:
        !           108:                cv.line.Warn1("Compiler flag %q should start with a hyphen.", cflag)
1.1       rillig    109:        }
                    110: }
                    111:
                    112: // The single-line description of the package.
                    113: func (cv *VartypeCheck) Comment() {
                    114:        line, value := cv.line, cv.value
                    115:
1.9     ! rillig    116:        if value == "TODO: Short description of the package" { // See pkgtools/url2pkg/files/url2pkg.pl, keyword "COMMENT".
        !           117:                line.Error0("COMMENT must be set.")
1.1       rillig    118:        }
                    119:        if m, first := match1(value, `^(?i)(a|an)\s`); m {
1.9     ! rillig    120:                line.Warn1("COMMENT should not begin with %q.", first)
1.1       rillig    121:        }
                    122:        if matches(value, `^[a-z]`) {
1.9     ! rillig    123:                line.Warn0("COMMENT should start with a capital letter.")
1.1       rillig    124:        }
                    125:        if hasSuffix(value, ".") {
1.9     ! rillig    126:                line.Warn0("COMMENT should not end with a period.")
1.1       rillig    127:        }
                    128:        if len(value) > 70 {
1.9     ! rillig    129:                line.Warn0("COMMENT should not be longer than 70 characters.")
1.1       rillig    130:        }
                    131: }
                    132:
                    133: func (cv *VartypeCheck) Dependency() {
                    134:        line, value := cv.line, cv.value
                    135:
1.9     ! rillig    136:        parser := NewParser(value)
        !           137:        deppat := parser.Dependency()
        !           138:        if deppat != nil && deppat.wildcard == "" && (parser.Rest() == "{,nb*}" || parser.Rest() == "{,nb[0-9]*}") {
        !           139:                line.Warn0("Dependency patterns of the form pkgbase>=1.0 don't need the \"{,nb*}\" extension.")
        !           140:                Explain4(
        !           141:                        "The \"{,nb*}\" extension is only necessary for dependencies of the",
        !           142:                        "form \"pkgbase-1.2\", since the pattern \"pkgbase-1.2\" doesn't match",
        !           143:                        "the version \"pkgbase-1.2nb5\".  For dependency patterns using the",
        !           144:                        "comparison operators, this is not necessary.")
        !           145:
        !           146:        } else if deppat == nil || !parser.EOF() {
        !           147:                line.Warn1("Unknown dependency pattern %q.", value)
        !           148:                Explain(
        !           149:                        "Typical dependencies have the following forms:",
        !           150:                        "",
        !           151:                        "\tpackage>=2.5",
        !           152:                        "\tpackage-[0-9]*",
        !           153:                        "\tpackage-3.141",
        !           154:                        "\tpackage>=2.71828<=3.1415")
1.1       rillig    155:                return
                    156:        }
                    157:
1.9     ! rillig    158:        wildcard := deppat.wildcard
        !           159:        if m, inside := match1(wildcard, `^\[(.*)\]\*$`); m {
        !           160:                if inside != "0-9" {
        !           161:                        line.Warn0("Only [0-9]* is allowed in the numeric part of a dependency.")
        !           162:                }
        !           163:
        !           164:        } else if m, ver, suffix := match2(wildcard, `^(\d\w*(?:\.\w+)*)(\.\*|\{,nb\*\}|\{,nb\[0-9\]\*\}|\*|)$`); m {
        !           165:                if suffix == "" {
        !           166:                        line.Warn2("Please use %q instead of %q as the version pattern.", ver+"{,nb*}", ver)
        !           167:                        Explain3(
        !           168:                                "Without the \"{,nb*}\" suffix, this version pattern only matches",
        !           169:                                "package versions that don't have a PKGREVISION (which is the part",
        !           170:                                "after the \"nb\").")
        !           171:                }
        !           172:                if suffix == "*" {
        !           173:                        line.Warn2("Please use %q instead of %q as the version pattern.", ver+".*", ver+"*")
        !           174:                        Explain2(
        !           175:                                "For example, the version \"1*\" also matches \"10.0.0\", which is",
        !           176:                                "probably not intended.")
        !           177:                }
        !           178:
        !           179:        } else if wildcard == "*" {
        !           180:                line.Warn1("Please use \"%[1]s-[0-9]*\" instead of \"%[1]s-*\".", deppat.pkgbase)
        !           181:                Explain3(
        !           182:                        "If you use a * alone, the package specification may match other",
        !           183:                        "packages that have the same prefix, but a longer name.  For example,",
        !           184:                        "foo-* matches foo-1.2, but also foo-client-1.2 and foo-server-1.2.")
        !           185:        }
        !           186:
        !           187:        if nocclasses := regcomp(`\[[\d-]+\]`).ReplaceAllString(wildcard, ""); contains(nocclasses, "-") {
        !           188:                line.Warn1("The version pattern %q should not contain a hyphen.", wildcard)
        !           189:                Explain(
        !           190:                        "Pkgsrc interprets package names with version numbers like this:",
        !           191:                        "",
        !           192:                        "\t\"foo-2.0-2.1.x\" => pkgbase \"foo\", version \"2.0-2.1.x\"",
1.1       rillig    193:                        "",
1.9     ! rillig    194:                        "To make the \"2.0\" above part of the package basename, the hyphen",
        !           195:                        "must be omitted, so the full package name becomes \"foo2.0-2.1.x\".")
1.1       rillig    196:        }
                    197: }
                    198:
                    199: func (cv *VartypeCheck) DependencyWithPath() {
                    200:        line, value := cv.line, cv.value
                    201:        if value != cv.valueNovar {
                    202:                return // It's probably not worth checking this.
                    203:        }
                    204:
1.9     ! rillig    205:        if m, pattern, relpath, pkg := match3(value, `(.*):(\.\./\.\./[^/]+/([^/]+))$`); m {
        !           206:                cv.mkline.CheckRelativePkgdir(relpath)
1.1       rillig    207:
                    208:                switch pkg {
                    209:                case "msgfmt", "gettext":
1.9     ! rillig    210:                        line.Warn0("Please use USE_TOOLS+=msgfmt instead of this dependency.")
1.1       rillig    211:                case "perl5":
1.9     ! rillig    212:                        line.Warn0("Please use USE_TOOLS+=perl:run instead of this dependency.")
1.1       rillig    213:                case "gmake":
1.9     ! rillig    214:                        line.Warn0("Please use USE_TOOLS+=gmake instead of this dependency.")
1.1       rillig    215:                }
                    216:
1.9     ! rillig    217:                cv.mkline.CheckVartypePrimitive(cv.varname, CheckvarDependency, cv.op, pattern, cv.comment, cv.listContext, cv.guessed)
1.1       rillig    218:                return
                    219:        }
                    220:
                    221:        if matches(value, `:\.\./[^/]+$`) {
1.9     ! rillig    222:                line.Warn0("Dependencies should have the form \"../../category/package\".")
        !           223:                cv.mkline.explainRelativeDirs()
1.1       rillig    224:                return
                    225:        }
                    226:
1.9     ! rillig    227:        line.Warn1("Unknown dependency pattern with path %q.", value)
        !           228:        Explain4(
        !           229:                "Examples for valid dependency patterns with path are:",
1.1       rillig    230:                "  package-[0-9]*:../../category/package",
                    231:                "  package>=3.41:../../category/package",
                    232:                "  package-2.718:../../category/package")
                    233: }
                    234:
                    235: func (cv *VartypeCheck) DistSuffix() {
                    236:        if cv.value == ".tar.gz" {
1.9     ! rillig    237:                cv.line.Note1("%s is \".tar.gz\" by default, so this definition may be redundant.", cv.varname)
1.1       rillig    238:        }
                    239: }
                    240:
                    241: func (cv *VartypeCheck) EmulPlatform() {
                    242:
                    243:        if m, opsys, arch := match2(cv.value, `^(\w+)-(\w+)$`); m {
1.8       dholland  244:                if !matches(opsys, `^(?:bitrig|bsdos|cygwin|darwin|dragonfly|freebsd|haiku|hpux|interix|irix|linux|mirbsd|netbsd|openbsd|osf1|sunos|solaris)$`) {
1.9     ! rillig    245:                        cv.line.Warnf("Unknown operating system: %s", opsys)
1.1       rillig    246:                }
                    247:                // no check for os_version
                    248:                if !matches(arch, `^(?:i386|alpha|amd64|arc|arm|arm32|cobalt|convex|dreamcast|hpcmips|hpcsh|hppa|ia64|m68k|m88k|mips|mips64|mipsel|mipseb|mipsn32|ns32k|pc532|pmax|powerpc|rs6000|s390|sparc|sparc64|vax|x86_64)$`) {
1.9     ! rillig    249:                        cv.line.Warn1("Unknown hardware architecture: %s", arch)
1.1       rillig    250:                }
                    251:
                    252:        } else {
1.9     ! rillig    253:                cv.line.Warn1("%q is not a valid emulation platform.", cv.value)
        !           254:                Explain(
1.1       rillig    255:                        "An emulation platform has the form <OPSYS>-<MACHINE_ARCH>.",
1.9     ! rillig    256:                        "OPSYS is the lower-case name of the operating system, and",
        !           257:                        "MACHINE_ARCH is the hardware architecture.",
1.1       rillig    258:                        "",
                    259:                        "Examples: linux-i386, irix-mipsel.")
                    260:        }
                    261: }
                    262:
                    263: func (cv *VartypeCheck) FetchURL() {
1.9     ! rillig    264:        cv.mkline.CheckVartypePrimitive(cv.varname, CheckvarURL, cv.op, cv.value, cv.comment, cv.listContext, cv.guessed)
1.1       rillig    265:
1.9     ! rillig    266:        for siteURL, siteName := range G.globalData.MasterSiteUrls {
        !           267:                if hasPrefix(cv.value, siteURL) {
        !           268:                        subdir := cv.value[len(siteURL):]
        !           269:                        if hasPrefix(cv.value, "https://github.com/") {
1.1       rillig    270:                                subdir = strings.SplitAfter(subdir, "/")[0]
1.9     ! rillig    271:                                cv.line.Warnf("Please use ${%s:=%s} instead of %q and run \"%s help topic=github\" for further tips.",
        !           272:                                        siteName, subdir, cv.value, confMake)
        !           273:                        } else {
        !           274:                                cv.line.Warnf("Please use ${%s:=%s} instead of %q.", siteName, subdir, cv.value)
1.1       rillig    275:                        }
                    276:                        return
                    277:                }
                    278:        }
1.9     ! rillig    279:
        !           280:        if m, name, subdir := match2(cv.value, `\$\{(MASTER_SITE_[^:]*).*:=(.*)\}$`); m {
        !           281:                if !G.globalData.MasterSiteVars[name] {
        !           282:                        cv.line.Error1("%s does not exist.", name)
        !           283:                }
        !           284:                if !hasSuffix(subdir, "/") {
        !           285:                        cv.line.Error1("The subdirectory in %s must end with a slash.", name)
        !           286:                }
        !           287:        }
1.1       rillig    288: }
                    289:
                    290: // See Pathname
                    291: // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_169
                    292: func (cv *VartypeCheck) Filename() {
                    293:        switch {
                    294:        case contains(cv.valueNovar, "/"):
1.9     ! rillig    295:                cv.line.Warn0("A filename should not contain a slash.")
1.1       rillig    296:        case !matches(cv.valueNovar, `^[-0-9@A-Za-z.,_~+%]*$`):
1.9     ! rillig    297:                cv.line.Warn1("%q is not a valid filename.", cv.value)
1.1       rillig    298:        }
                    299: }
                    300:
                    301: func (cv *VartypeCheck) Filemask() {
                    302:        if !matches(cv.valueNovar, `^[-0-9A-Za-z._~+%*?]*$`) {
1.9     ! rillig    303:                cv.line.Warn1("%q is not a valid filename mask.", cv.value)
1.1       rillig    304:        }
                    305: }
                    306:
                    307: func (cv *VartypeCheck) FileMode() {
                    308:        switch {
                    309:        case cv.value != "" && cv.valueNovar == "":
                    310:                // Fine.
                    311:        case matches(cv.value, `^[0-7]{3,4}`):
                    312:                // Fine.
                    313:        default:
1.9     ! rillig    314:                cv.line.Warn1("Invalid file mode %q.", cv.value)
1.1       rillig    315:        }
                    316: }
                    317:
                    318: func (cv *VartypeCheck) Identifier() {
                    319:        if cv.value != cv.valueNovar {
                    320:                //line.logWarning("Identifiers should be given directly.")
                    321:        }
                    322:        switch {
                    323:        case matches(cv.valueNovar, `^[+\-.0-9A-Z_a-z]+$`):
                    324:                // Fine.
                    325:        case cv.value != "" && cv.valueNovar == "":
                    326:                // Don't warn here.
                    327:        default:
1.9     ! rillig    328:                cv.line.Warn1("Invalid identifier %q.", cv.value)
1.1       rillig    329:        }
                    330: }
                    331:
                    332: func (cv *VartypeCheck) Integer() {
                    333:        if !matches(cv.value, `^\d+$`) {
1.9     ! rillig    334:                cv.line.Warn1("Invalid integer %q.", cv.value)
1.1       rillig    335:        }
                    336: }
                    337:
                    338: func (cv *VartypeCheck) LdFlag() {
1.9     ! rillig    339:        ldflag := cv.value
        !           340:        if m, rpathFlag := match1(ldflag, `^(-Wl,(?:-R|-rpath|--rpath))`); m {
        !           341:                cv.line.Warn1("Please use \"${COMPILER_RPATH_FLAG}\" instead of %q.", rpathFlag)
1.1       rillig    342:                return
1.9     ! rillig    343:        }
1.1       rillig    344:
1.9     ! rillig    345:        switch {
        !           346:        case hasPrefix(ldflag, "-L"),
        !           347:                hasPrefix(ldflag, "-l"),
        !           348:                ldflag == "-pthread",
        !           349:                ldflag == "-static",
        !           350:                hasPrefix(ldflag, "-static-"),
        !           351:                hasPrefix(ldflag, "-Wl,-"),
        !           352:                hasPrefix(ldflag, "`") && hasSuffix(ldflag, "`"),
        !           353:                ldflag != cv.valueNovar:
        !           354:                return
        !           355:        case hasPrefix(ldflag, "-"):
        !           356:                cv.line.Warn1("Unknown linker flag %q.", cv.value)
        !           357:        default:
        !           358:                cv.line.Warn1("Linker flag %q should start with a hypen.", cv.value)
1.1       rillig    359:        }
                    360: }
                    361:
                    362: func (cv *VartypeCheck) License() {
1.9     ! rillig    363:        checklineLicense(cv.mkline, cv.value)
1.1       rillig    364: }
                    365:
                    366: func (cv *VartypeCheck) MailAddress() {
                    367:        line, value := cv.line, cv.value
                    368:
                    369:        if m, _, domain := match2(value, `^([+\-.0-9A-Z_a-z]+)@([-\w\d.]+)$`); m {
                    370:                if strings.EqualFold(domain, "NetBSD.org") && domain != "NetBSD.org" {
1.9     ! rillig    371:                        line.Warn1("Please write \"NetBSD.org\" instead of %q.", domain)
1.1       rillig    372:                }
                    373:                if matches(value, `(?i)^(tech-pkg|packages)@NetBSD\.org$`) {
1.9     ! rillig    374:                        line.Error0("This mailing list address is obsolete. Use pkgsrc-users@NetBSD.org instead.")
1.1       rillig    375:                }
                    376:
                    377:        } else {
1.9     ! rillig    378:                line.Warn1("\"%s\" is not a valid mail address.", value)
1.1       rillig    379:        }
                    380: }
                    381:
                    382: // See ${STEP_MSG}, ${PKG_FAIL_REASON}
                    383: func (cv *VartypeCheck) Message() {
                    384:        line, varname, value := cv.line, cv.varname, cv.value
                    385:
                    386:        if matches(value, `^[\"'].*[\"']$`) {
1.9     ! rillig    387:                line.Warn1("%s should not be quoted.", varname)
        !           388:                Explain(
1.1       rillig    389:                        "The quoting is only needed for variables which are interpreted as",
1.9     ! rillig    390:                        "multiple words (or, generally speaking, a list of something).  A",
        !           391:                        "single text message does not belong to this class, since it is only",
        !           392:                        "printed as a whole.",
1.1       rillig    393:                        "",
1.9     ! rillig    394:                        "On the other hand, PKG_FAIL_REASON is a _list_ of text messages, so",
        !           395:                        "in that case, the quoting has to be done.")
1.1       rillig    396:        }
                    397: }
                    398:
                    399: // A package option from options.mk
                    400: func (cv *VartypeCheck) Option() {
                    401:        line, value, valueNovar := cv.line, cv.value, cv.valueNovar
                    402:
                    403:        if value != valueNovar {
1.9     ! rillig    404:                if G.opts.DebugUnchecked {
        !           405:                        line.Debug1("Unchecked option name: %q", value)
        !           406:                }
1.1       rillig    407:                return
                    408:        }
                    409:
1.9     ! rillig    410:        if m, optname := match1(value, `^-?([a-z][-0-9a-z+]*)$`); m {
        !           411:                if _, found := G.globalData.PkgOptions[optname]; !found { // There’s a difference between empty and absent here.
        !           412:                        line.Warn1("Unknown option \"%s\".", optname)
        !           413:                        Explain4(
1.1       rillig    414:                                "This option is not documented in the mk/defaults/options.description",
1.9     ! rillig    415:                                "file.  Please think of a brief but precise description and either",
        !           416:                                "update that file yourself or suggest a description for this option",
        !           417:                                "on the tech-pkg@NetBSD.org mailing list.")
1.1       rillig    418:                }
                    419:                return
                    420:        }
                    421:
                    422:        if matches(value, `^-?([a-z][-0-9a-z_\+]*)$`) {
1.9     ! rillig    423:                line.Warn0("Use of the underscore character in option names is deprecated.")
1.1       rillig    424:                return
                    425:        }
                    426:
1.9     ! rillig    427:        line.Error1("Invalid option name %q. Option names must start with a lowercase letter and be all-lowercase.", value)
1.1       rillig    428: }
                    429:
                    430: // The PATH environment variable
                    431: func (cv *VartypeCheck) Pathlist() {
1.9     ! rillig    432:        if !contains(cv.value, ":") && cv.guessed {
        !           433:                cv.mkline.CheckVartypePrimitive(cv.varname, CheckvarPathname, cv.op, cv.value, cv.comment, cv.listContext, cv.guessed)
1.1       rillig    434:                return
                    435:        }
                    436:
                    437:        for _, path := range strings.Split(cv.value, ":") {
                    438:                if contains(path, "${") {
                    439:                        continue
                    440:                }
                    441:
                    442:                if !matches(path, `^[-0-9A-Za-z._~+%/]*$`) {
1.9     ! rillig    443:                        cv.line.Warn1("%q is not a valid pathname.", path)
1.1       rillig    444:                }
                    445:
                    446:                if !hasPrefix(path, "/") {
1.9     ! rillig    447:                        cv.line.Warn2("All components of %s (in this case %q) should be absolute paths.", cv.varname, path)
1.1       rillig    448:                }
                    449:        }
                    450: }
                    451:
                    452: // Shell globbing including slashes.
                    453: // See Filemask
                    454: func (cv *VartypeCheck) Pathmask() {
                    455:        if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%*?/\[\]]*`) {
1.9     ! rillig    456:                cv.line.Warn1("%q is not a valid pathname mask.", cv.value)
1.1       rillig    457:        }
1.9     ! rillig    458:        cv.line.CheckAbsolutePathname(cv.value)
1.1       rillig    459: }
                    460:
                    461: // Like Filename, but including slashes
                    462: // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266
                    463: func (cv *VartypeCheck) Pathname() {
                    464:        if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%/]*$`) {
1.9     ! rillig    465:                cv.line.Warn1("%q is not a valid pathname.", cv.value)
1.1       rillig    466:        }
1.9     ! rillig    467:        cv.line.CheckAbsolutePathname(cv.value)
1.1       rillig    468: }
                    469:
                    470: func (cv *VartypeCheck) Perl5Packlist() {
                    471:        if cv.value != cv.valueNovar {
1.9     ! rillig    472:                cv.line.Warn1("%s should not depend on other variables.", cv.varname)
1.1       rillig    473:        }
                    474: }
                    475:
                    476: func (cv *VartypeCheck) PkgName() {
                    477:        if cv.value == cv.valueNovar && !matches(cv.value, rePkgname) {
1.9     ! rillig    478:                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    479:        }
                    480: }
                    481:
                    482: func (cv *VartypeCheck) PkgOptionsVar() {
1.9     ! rillig    483:        cv.mkline.CheckVartypePrimitive(cv.varname, CheckvarVarname, cv.op, cv.value, cv.comment, false, cv.guessed)
1.1       rillig    484:        if matches(cv.value, `\$\{PKGBASE[:\}]`) {
1.9     ! rillig    485:                cv.line.Error0("PKGBASE must not be used in PKG_OPTIONS_VAR.")
        !           486:                Explain3(
1.1       rillig    487:                        "PKGBASE is defined in bsd.pkg.mk, which is included as the",
                    488:                        "very last file, but PKG_OPTIONS_VAR is evaluated earlier.",
                    489:                        "Use ${PKGNAME:C/-[0-9].*//} instead.")
                    490:        }
                    491: }
                    492:
                    493: // A directory name relative to the top-level pkgsrc directory.
                    494: // Despite its name, it is more similar to RelativePkgDir than to RelativePkgPath.
                    495: func (cv *VartypeCheck) PkgPath() {
1.9     ! rillig    496:        cv.mkline.CheckRelativePkgdir(G.CurPkgsrcdir + "/" + cv.value)
1.1       rillig    497: }
                    498:
                    499: func (cv *VartypeCheck) PkgRevision() {
                    500:        if !matches(cv.value, `^[1-9]\d*$`) {
1.9     ! rillig    501:                cv.line.Warn1("%s must be a positive integer number.", cv.varname)
1.1       rillig    502:        }
1.9     ! rillig    503:        if path.Base(cv.line.Fname) != "Makefile" {
        !           504:                cv.line.Error1("%s only makes sense directly in the package Makefile.", cv.varname)
        !           505:                Explain(
1.1       rillig    506:                        "Usually, different packages using the same Makefile.common have",
1.9     ! rillig    507:                        "different dependencies and will be bumped at different times (e.g.",
        !           508:                        "for shlib major bumps) and thus the PKGREVISIONs must be in the",
        !           509:                        "separate Makefiles.  There is no practical way of having this",
        !           510:                        "information in a commonly used Makefile.")
1.1       rillig    511:        }
                    512: }
                    513:
                    514: func (cv *VartypeCheck) PlatformTriple() {
                    515:        if cv.value != cv.valueNovar {
                    516:                return
                    517:        }
                    518:
                    519:        rePart := `(?:\[[^\]]+\]|[^-\[])+`
                    520:        reTriple := `^(` + rePart + `)-(` + rePart + `)-(` + rePart + `)$`
                    521:        if m, opsys, _, arch := match3(cv.value, reTriple); m {
1.8       dholland  522:                if !matches(opsys, `^(?:\*|Bitrig|BSDOS|Cygwin|Darwin|DragonFly|FreeBSD|Haiku|HPUX|Interix|IRIX|Linux|MirBSD|NetBSD|OpenBSD|OSF1|QNX|SunOS)$`) {
1.9     ! rillig    523:                        cv.line.Warnf("Unknown operating system: %s", opsys)
1.1       rillig    524:                }
                    525:                // no check for os_version
                    526:                if !matches(arch, `^(?:\*|i386|alpha|amd64|arc|arm|arm32|cobalt|convex|dreamcast|hpcmips|hpcsh|hppa|ia64|m68k|m88k|mips|mips64|mipsel|mipseb|mipsn32|ns32k|pc532|pmax|powerpc|rs6000|s390|sparc|sparc64|vax|x86_64)$`) {
1.9     ! rillig    527:                        cv.line.Warn1("Unknown hardware architecture: %s", arch)
1.1       rillig    528:                }
                    529:
                    530:        } else {
1.9     ! rillig    531:                cv.line.Warn1("%q is not a valid platform triple.", cv.value)
        !           532:                Explain3(
1.1       rillig    533:                        "A platform triple has the form <OPSYS>-<OS_VERSION>-<MACHINE_ARCH>.",
                    534:                        "Each of these components may be a shell globbing expression.",
                    535:                        "Examples: NetBSD-*-i386, *-*-*, Linux-*-*.")
                    536:        }
                    537: }
                    538:
                    539: func (cv *VartypeCheck) PrefixPathname() {
                    540:        if m, mansubdir := match1(cv.value, `^man/(.+)`); m {
1.9     ! rillig    541:                cv.line.Warn2("Please use \"${PKGMANDIR}/%s\" instead of %q.", mansubdir, cv.value)
1.1       rillig    542:        }
                    543: }
                    544:
                    545: func (cv *VartypeCheck) PythonDependency() {
                    546:        if cv.value != cv.valueNovar {
1.9     ! rillig    547:                cv.line.Warn0("Python dependencies should not contain variables.")
        !           548:        } else if !matches(cv.valueNovar, `^[+\-.0-9A-Z_a-z]+(?:|:link|:build)$`) {
        !           549:                cv.line.Warn1("Invalid Python dependency %q.", cv.value)
        !           550:                Explain4(
        !           551:                        "Python dependencies must be an identifier for a package, as",
        !           552:                        "specified in lang/python/versioned_dependencies.mk.  This",
        !           553:                        "identifier may be followed by :build for a build-time only",
        !           554:                        "dependency, or by :link for a run-time only dependency.")
1.1       rillig    555:        }
                    556: }
                    557:
                    558: // Refers to a package directory.
                    559: func (cv *VartypeCheck) RelativePkgDir() {
1.9     ! rillig    560:        cv.mkline.CheckRelativePkgdir(cv.value)
1.1       rillig    561: }
                    562:
                    563: // Refers to a file or directory.
                    564: func (cv *VartypeCheck) RelativePkgPath() {
1.9     ! rillig    565:        cv.mkline.CheckRelativePath(cv.value, true)
1.1       rillig    566: }
                    567:
                    568: func (cv *VartypeCheck) Restricted() {
                    569:        if cv.value != "${RESTRICTED}" {
1.9     ! rillig    570:                cv.line.Warn1("The only valid value for %s is ${RESTRICTED}.", cv.varname)
        !           571:                Explain3(
        !           572:                        "These variables are used to control which files may be mirrored on",
        !           573:                        "FTP servers or CD-ROM collections.  They are not intended to mark",
        !           574:                        "packages whose only MASTER_SITES are on ftp.NetBSD.org.")
1.1       rillig    575:        }
                    576: }
                    577:
                    578: func (cv *VartypeCheck) SedCommand() {
                    579: }
                    580:
                    581: func (cv *VartypeCheck) SedCommands() {
                    582:        line := cv.line
1.9     ! rillig    583:        mkline := cv.mkline
        !           584:        shline := NewShellLine(mkline)
1.1       rillig    585:
1.9     ! rillig    586:        tokens, rest := splitIntoShellTokens(line, cv.value)
1.1       rillig    587:        if rest != "" {
1.9     ! rillig    588:                if strings.Contains(line.Text, "#") {
        !           589:                        line.Error1("Invalid shell words %q in sed commands.", rest)
        !           590:                        Explain4(
1.1       rillig    591:                                "When sed commands have embedded \"#\" characters, they need to be",
                    592:                                "escaped with a backslash, otherwise make(1) will interpret them as a",
                    593:                                "comment, no matter if they occur in single or double quotes or",
                    594:                                "whatever.")
                    595:                }
                    596:                return
                    597:        }
                    598:
1.9     ! rillig    599:        ntokens := len(tokens)
1.1       rillig    600:        ncommands := 0
                    601:
1.9     ! rillig    602:        for i := 0; i < ntokens; i++ {
        !           603:                token := tokens[i]
        !           604:                shline.CheckToken(token, true)
1.1       rillig    605:
                    606:                switch {
1.9     ! rillig    607:                case token == "-e":
        !           608:                        if i+1 < ntokens {
1.1       rillig    609:                                // Check the real sed command here.
                    610:                                i++
                    611:                                ncommands++
                    612:                                if ncommands > 1 {
1.9     ! rillig    613:                                        line.Note0("Each sed command should appear in an assignment of its own.")
        !           614:                                        Explain(
1.1       rillig    615:                                                "For example, instead of",
                    616:                                                "    SUBST_SED.foo+=        -e s,command1,, -e s,command2,,",
                    617:                                                "use",
                    618:                                                "    SUBST_SED.foo+=        -e s,command1,,",
                    619:                                                "    SUBST_SED.foo+=        -e s,command2,,",
                    620:                                                "",
                    621:                                                "This way, short sed commands cannot be hidden at the end of a line.")
                    622:                                }
1.9     ! rillig    623:                                shline.CheckToken(tokens[i-1], true)
        !           624:                                shline.CheckToken(tokens[i], true)
        !           625:                                mkline.CheckVartypePrimitive(cv.varname, CheckvarSedCommand, cv.op, tokens[i], cv.comment, cv.listContext, cv.guessed)
1.1       rillig    626:                        } else {
1.9     ! rillig    627:                                line.Error0("The -e option to sed requires an argument.")
1.1       rillig    628:                        }
1.9     ! rillig    629:                case token == "-E":
1.1       rillig    630:                        // Switch to extended regular expressions mode.
                    631:
1.9     ! rillig    632:                case token == "-n":
1.1       rillig    633:                        // Don't print lines per default.
                    634:
1.9     ! rillig    635:                case i == 0 && matches(token, `^(["']?)(?:\d*|/.*/)s.+["']?$`):
        !           636:                        line.Note0("Please always use \"-e\" in sed commands, even if there is only one substitution.")
1.1       rillig    637:
                    638:                default:
1.9     ! rillig    639:                        line.Warn1("Unknown sed command %q.", token)
1.1       rillig    640:                }
                    641:        }
                    642: }
                    643:
                    644: func (cv *VartypeCheck) ShellCommand() {
1.9     ! rillig    645:        setE := true
        !           646:        NewShellLine(cv.mkline).CheckShellCommand(cv.value, &setE)
        !           647: }
        !           648:
        !           649: // Zero or more shell commands, each terminated with a semicolon.
        !           650: func (cv *VartypeCheck) ShellCommands() {
        !           651:        NewShellLine(cv.mkline).CheckShellCommands(cv.value)
1.1       rillig    652: }
                    653:
                    654: func (cv *VartypeCheck) ShellWord() {
                    655:        if !cv.listContext {
1.9     ! rillig    656:                NewShellLine(cv.mkline).CheckToken(cv.value, true)
1.1       rillig    657:        }
                    658: }
                    659:
                    660: func (cv *VartypeCheck) Stage() {
                    661:        if !matches(cv.value, `^(?:pre|do|post)-(?:extract|patch|configure|build|test|install)`) {
1.9     ! rillig    662:                cv.line.Warn1("Invalid stage name %q. Use one of {pre,do,post}-{extract,patch,configure,build,test,install}.", cv.value)
1.1       rillig    663:        }
                    664: }
                    665:
                    666: func (cv *VartypeCheck) String() {
                    667:        // No further checks possible.
                    668: }
                    669:
                    670: func (cv *VartypeCheck) Tool() {
1.9     ! rillig    671:        if cv.varname == "TOOLS_NOOP" && cv.op == opAssignAppend {
1.1       rillig    672:                // no warning for package-defined tool definitions
                    673:
                    674:        } else if m, toolname, tooldep := match2(cv.value, `^([-\w]+|\[)(?::(\w+))?$`); m {
1.9     ! rillig    675:                if !G.globalData.Tools[toolname] {
        !           676:                        cv.line.Error1("Unknown tool %q.", toolname)
1.1       rillig    677:                }
                    678:                switch tooldep {
                    679:                case "", "bootstrap", "build", "pkgsrc", "run":
                    680:                default:
1.9     ! rillig    681:                        cv.line.Error1("Unknown tool dependency %q. Use one of \"build\", \"pkgsrc\" or \"run\".", tooldep)
1.1       rillig    682:                }
                    683:        } else {
1.9     ! rillig    684:                cv.line.Error1("Invalid tool syntax: %q.", cv.value)
1.1       rillig    685:        }
                    686: }
                    687:
                    688: func (cv *VartypeCheck) Unchecked() {
                    689:        // Do nothing, as the name says.
                    690: }
                    691:
                    692: func (cv *VartypeCheck) URL() {
                    693:        line, value := cv.line, cv.value
                    694:
                    695:        if value == "" && hasPrefix(cv.comment, "#") {
                    696:                // Ok
                    697:
                    698:        } else if containsVarRef(value) {
                    699:                // No further checks
                    700:
1.9     ! rillig    701:        } else if m, _, host, _, _ := match4(value, `^(https?|ftp|gopher)://([-0-9A-Za-z.]+)(?::(\d+))?/([-%&+,./0-9:;=?@A-Z_a-z~]|#)*$`); m {
1.1       rillig    702:                if matches(host, `(?i)\.NetBSD\.org$`) && !matches(host, `\.NetBSD\.org$`) {
1.9     ! rillig    703:                        line.Warn1("Please write NetBSD.org instead of %s.", host)
1.1       rillig    704:                }
                    705:
                    706:        } else if m, scheme, _, absPath := match3(value, `^([0-9A-Za-z]+)://([^/]+)(.*)$`); m {
                    707:                switch {
                    708:                case scheme != "ftp" && scheme != "http" && scheme != "https" && scheme != "gopher":
1.9     ! rillig    709:                        line.Warn1("%q is not a valid URL. Only ftp, gopher, http, and https URLs are allowed here.", value)
1.1       rillig    710:
                    711:                case absPath == "":
1.9     ! rillig    712:                        line.Note1("For consistency, please add a trailing slash to %q.", value)
1.1       rillig    713:
                    714:                default:
1.9     ! rillig    715:                        line.Warn1("%q is not a valid URL.", value)
1.1       rillig    716:                }
                    717:
                    718:        } else {
1.9     ! rillig    719:                line.Warn1("%q is not a valid URL.", value)
1.1       rillig    720:        }
                    721: }
                    722:
                    723: func (cv *VartypeCheck) UserGroupName() {
                    724:        if cv.value == cv.valueNovar && !matches(cv.value, `^[0-9_a-z]+$`) {
1.9     ! rillig    725:                cv.line.Warn1("Invalid user or group name %q.", cv.value)
1.1       rillig    726:        }
                    727: }
                    728:
                    729: func (cv *VartypeCheck) Varname() {
                    730:        if cv.value == cv.valueNovar && !matches(cv.value, `^[A-Z_][0-9A-Z_]*(?:[.].*)?$`) {
1.9     ! rillig    731:                cv.line.Warn1("%q is not a valid variable name.", cv.value)
        !           732:                Explain(
1.1       rillig    733:                        "Variable names are restricted to only uppercase letters and the",
                    734:                        "underscore in the basename, and arbitrary characters in the",
                    735:                        "parameterized part, following the dot.",
                    736:                        "",
                    737:                        "Examples:",
                    738:                        "\t* PKGNAME",
                    739:                        "\t* PKG_OPTIONS.gnuchess")
                    740:        }
                    741: }
                    742:
                    743: func (cv *VartypeCheck) Version() {
                    744:        if !matches(cv.value, `^([\d.])+$`) {
1.9     ! rillig    745:                cv.line.Warn1("Invalid version number %q.", cv.value)
1.1       rillig    746:        }
                    747: }
                    748:
                    749: func (cv *VartypeCheck) WrapperReorder() {
                    750:        if !matches(cv.value, `^reorder:l:([\w\-]+):([\w\-]+)$`) {
1.9     ! rillig    751:                cv.line.Warn1("Unknown wrapper reorder command %q.", cv.value)
1.1       rillig    752:        }
                    753: }
                    754:
                    755: func (cv *VartypeCheck) WrapperTransform() {
1.9     ! rillig    756:        cmd := cv.value
        !           757:        if hasPrefix(cmd, "rm:-") ||
        !           758:                matches(cmd, `^(R|l|rpath):([^:]+):(.+)$`) ||
        !           759:                matches(cmd, `^'?(opt|rename|rm-optarg|rmdir):.*$`) ||
        !           760:                cmd == "-e" ||
        !           761:                matches(cmd, `^["']?s[|:,]`) {
        !           762:                return
1.1       rillig    763:        }
1.9     ! rillig    764:        cv.line.Warn1("Unknown wrapper transform command %q.", cmd)
1.1       rillig    765: }
                    766:
                    767: func (cv *VartypeCheck) WrkdirSubdirectory() {
1.9     ! rillig    768:        cv.mkline.CheckVartypePrimitive(cv.varname, CheckvarPathname, cv.op, cv.value, cv.comment, cv.listContext, cv.guessed)
1.1       rillig    769: }
                    770:
                    771: // A directory relative to ${WRKSRC}, for use in CONFIGURE_DIRS and similar variables.
                    772: func (cv *VartypeCheck) WrksrcSubdirectory() {
                    773:        if m, _, rest := match2(cv.value, `^(\$\{WRKSRC\})(?:/(.*))?`); m {
                    774:                if rest == "" {
                    775:                        rest = "."
                    776:                }
1.9     ! rillig    777:                cv.line.Note2("You can use %q instead of %q.", rest, cv.value)
        !           778:                Explain1(
        !           779:                        "These directories are interpreted relative to ${WRKSRC}.")
1.1       rillig    780:
                    781:        } else if cv.value != "" && cv.valueNovar == "" {
                    782:                // The value of another variable
                    783:
                    784:        } else if !matches(cv.valueNovar, `^(?:\.|[0-9A-Za-z_@][-0-9A-Za-z_@./+]*)$`) {
1.9     ! rillig    785:                cv.line.Warn1("%q is not a valid subdirectory of ${WRKSRC}.", cv.value)
1.1       rillig    786:        }
                    787: }
                    788:
                    789: // Used for variables that are checked using `.if defined(VAR)`.
                    790: func (cv *VartypeCheck) Yes() {
                    791:        if !matches(cv.value, `^(?:YES|yes)(?:\s+#.*)?$`) {
1.9     ! rillig    792:                cv.line.Warn1("%s should be set to YES or yes.", cv.varname)
        !           793:                Explain4(
1.1       rillig    794:                        "This variable means \"yes\" if it is defined, and \"no\" if it is",
1.9     ! rillig    795:                        "undefined.  Even when it has the value \"no\", this means \"yes\".",
1.1       rillig    796:                        "Therefore when it is defined, its value should correspond to its",
                    797:                        "meaning.")
                    798:        }
                    799: }
                    800:
                    801: // The type YesNo is used for variables that are checked using
                    802: //     .if defined(VAR) && !empty(VAR:M[Yy][Ee][Ss])
                    803: //
                    804: func (cv *VartypeCheck) YesNo() {
                    805:        if !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) {
1.9     ! rillig    806:                cv.line.Warn1("%s should be set to YES, yes, NO, or no.", cv.varname)
1.1       rillig    807:        }
                    808: }
                    809:
                    810: // Like YesNo, but the value may be produced by a shell command using the
                    811: // != operator.
1.3       rillig    812: func (cv *VartypeCheck) YesNoIndirectly() {
1.1       rillig    813:        if cv.valueNovar != "" && !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) {
1.9     ! rillig    814:                cv.line.Warn1("%s should be set to YES, yes, NO, or no.", cv.varname)
1.1       rillig    815:        }
                    816: }

CVSweb <webmaster@jp.NetBSD.org>