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

Annotation of pkgsrc/pkgtools/pkglint/files/package.go, Revision 1.34

1.1       rillig      1: package main
                      2:
                      3: import (
1.15      rillig      4:        "netbsd.org/pkglint/pkgver"
1.16      rillig      5:        "netbsd.org/pkglint/regex"
                      6:        "netbsd.org/pkglint/trace"
1.27      rillig      7:        "os"
1.1       rillig      8:        "path"
                      9:        "regexp"
                     10:        "strconv"
                     11:        "strings"
                     12: )
                     13:
1.31      rillig     14: const rePkgname = `^([\w\-.+]+)-(\d[.0-9A-Z_a-z]*)$`
1.15      rillig     15:
1.3       rillig     16: // Package contains data for the pkgsrc package that is currently checked.
                     17: type Package struct {
1.34    ! rillig     18:        dir                  string          // The directory of the package, for resolving files
1.22      rillig     19:        Pkgpath              string          // e.g. "category/pkgdir"
                     20:        Pkgdir               string          // PKGDIR from the package Makefile
                     21:        Filesdir             string          // FILESDIR from the package Makefile
                     22:        Patchdir             string          // PATCHDIR from the package Makefile
                     23:        DistinfoFile         string          // DISTINFO_FILE from the package Makefile
                     24:        EffectivePkgname     string          // PKGNAME or DISTNAME from the package Makefile, including nb13
                     25:        EffectivePkgbase     string          // The effective PKGNAME without the version
                     26:        EffectivePkgversion  string          // The version part of the effective PKGNAME, excluding nb13
                     27:        EffectivePkgnameLine MkLine          // The origin of the three effective_* values
                     28:        SeenBsdPrefsMk       bool            // Has bsd.prefs.mk already been included?
                     29:        PlistDirs            map[string]bool // Directories mentioned in the PLIST files
1.30      rillig     30:        PlistFiles           map[string]bool // Regular files mentioned in the PLIST files
1.18      rillig     31:
1.26      rillig     32:        vars                  Scope
                     33:        bl3                   map[string]Line // buildlink3.mk name => line; contains only buildlink3.mk files that are directly included.
1.34    ! rillig     34:        plistSubstCond        map[string]bool // varname => true; all variables that are used as conditions (@comment or nothing) in PLISTs.
1.26      rillig     35:        included              map[string]Line // fname => line
                     36:        seenMakefileCommon    bool            // Does the package have any .includes?
                     37:        loadTimeTools         map[string]bool // true=ok, false=not ok, absent=not mentioned in USE_TOOLS.
1.18      rillig     38:        conditionalIncludes   map[string]MkLine
                     39:        unconditionalIncludes map[string]MkLine
1.21      rillig     40:        once                  Once
1.33      rillig     41:        IgnoreMissingPatches  bool // In distinfo, don't warn about patches that cannot be found.
1.3       rillig     42: }
                     43:
1.34    ! rillig     44: func NewPackage(dir string) *Package {
        !            45:        pkgpath := G.Pkgsrc.ToRel(dir)
        !            46:        if strings.Count(pkgpath, "/") != 1 {
        !            47:                NewLineWhole(dir).Errorf("Package directory %q must be two subdirectories below the pkgsrc root %q.", dir, G.Pkgsrc.File("."))
        !            48:        }
        !            49:
1.3       rillig     50:        pkg := &Package{
1.34    ! rillig     51:                dir:                   dir,
1.12      rillig     52:                Pkgpath:               pkgpath,
1.33      rillig     53:                Pkgdir:                ".",
                     54:                Filesdir:              "files",
                     55:                Patchdir:              "patches",
1.34    ! rillig     56:                DistinfoFile:          "distinfo",
1.22      rillig     57:                PlistDirs:             make(map[string]bool),
1.30      rillig     58:                PlistFiles:            make(map[string]bool),
1.26      rillig     59:                vars:                  NewScope(),
1.20      rillig     60:                bl3:                   make(map[string]Line),
1.12      rillig     61:                plistSubstCond:        make(map[string]bool),
1.20      rillig     62:                included:              make(map[string]Line),
1.12      rillig     63:                loadTimeTools:         make(map[string]bool),
1.18      rillig     64:                conditionalIncludes:   make(map[string]MkLine),
                     65:                unconditionalIncludes: make(map[string]MkLine),
1.3       rillig     66:        }
1.27      rillig     67:        for varname, line := range G.Pkgsrc.UserDefinedVars {
1.26      rillig     68:                pkg.vars.Define(varname, line)
1.3       rillig     69:        }
                     70:        return pkg
                     71: }
                     72:
1.33      rillig     73: // File returns the (possibly absolute) path to relativeFilename,
                     74: // as resolved from the package's directory.
                     75: func (pkg *Package) File(relativeFilename string) string {
1.34    ! rillig     76:        return cleanpath(pkg.dir + "/" + relativeFilename)
1.33      rillig     77: }
                     78:
1.3       rillig     79: func (pkg *Package) varValue(varname string) (string, bool) {
1.8       rillig     80:        switch varname {
                     81:        case "KRB5_TYPE":
                     82:                return "heimdal", true
                     83:        case "PGSQL_VERSION":
                     84:                return "95", true
                     85:        }
1.26      rillig     86:        if mkline := pkg.vars.FirstDefinition(varname); mkline != nil {
1.3       rillig     87:                return mkline.Value(), true
                     88:        }
                     89:        return "", false
                     90: }
1.1       rillig     91:
1.8       rillig     92: func (pkg *Package) setSeenBsdPrefsMk() {
                     93:        if !pkg.SeenBsdPrefsMk {
                     94:                pkg.SeenBsdPrefsMk = true
1.16      rillig     95:                if trace.Tracing {
                     96:                        trace.Stepf("Pkg.setSeenBsdPrefsMk")
1.8       rillig     97:                }
                     98:        }
                     99: }
                    100:
1.3       rillig    101: func (pkg *Package) checkPossibleDowngrade() {
1.16      rillig    102:        if trace.Tracing {
                    103:                defer trace.Call0()()
1.3       rillig    104:        }
                    105:
                    106:        m, _, pkgversion := match2(pkg.EffectivePkgname, rePkgname)
1.1       rillig    107:        if !m {
                    108:                return
                    109:        }
                    110:
1.3       rillig    111:        mkline := pkg.EffectivePkgnameLine
1.1       rillig    112:
1.27      rillig    113:        change := G.Pkgsrc.LastChange[pkg.Pkgpath]
1.1       rillig    114:        if change == nil {
1.16      rillig    115:                if trace.Tracing {
                    116:                        trace.Step1("No change log for package %q", pkg.Pkgpath)
1.3       rillig    117:                }
1.1       rillig    118:                return
                    119:        }
                    120:
1.3       rillig    121:        if change.Action == "Updated" {
1.16      rillig    122:                changeVersion := regex.Compile(`nb\d+$`).ReplaceAllString(change.Version, "")
1.15      rillig    123:                if pkgver.Compare(pkgversion, changeVersion) < 0 {
1.31      rillig    124:                        mkline.Warnf("The package is being downgraded from %s (see %s) to %s.", change.Version, change.Line.ReferenceFrom(mkline.Line), pkgversion)
1.14      rillig    125:                        Explain(
1.4       rillig    126:                                "The files in doc/CHANGES-*, in which all version changes are",
                    127:                                "recorded, have a higher version number than what the package says.",
                    128:                                "This is unusual, since packages are typically upgraded instead of",
                    129:                                "downgraded.")
1.1       rillig    130:                }
                    131:        }
                    132: }
                    133:
1.3       rillig    134: func (pkg *Package) checklinesBuildlink3Inclusion(mklines *MkLines) {
1.16      rillig    135:        if trace.Tracing {
                    136:                defer trace.Call0()()
1.1       rillig    137:        }
                    138:
                    139:        // Collect all the included buildlink3.mk files from the file.
1.18      rillig    140:        includedFiles := make(map[string]MkLine)
1.3       rillig    141:        for _, mkline := range mklines.mklines {
                    142:                if mkline.IsInclude() {
1.31      rillig    143:                        file := mkline.IncludeFile()
1.1       rillig    144:                        if m, bl3 := match1(file, `^\.\./\.\./(.*)/buildlink3\.mk`); m {
1.3       rillig    145:                                includedFiles[bl3] = mkline
                    146:                                if pkg.bl3[bl3] == nil {
1.14      rillig    147:                                        mkline.Warnf("%s/buildlink3.mk is included by this file but not by the package.", bl3)
1.1       rillig    148:                                }
                    149:                        }
                    150:                }
                    151:        }
                    152:
1.16      rillig    153:        if trace.Tracing {
1.8       rillig    154:                for packageBl3 := range pkg.bl3 {
1.1       rillig    155:                        if includedFiles[packageBl3] == nil {
1.16      rillig    156:                                trace.Step1("%s/buildlink3.mk is included by the package but not by the buildlink3.mk file.", packageBl3)
1.1       rillig    157:                        }
                    158:                }
                    159:        }
                    160: }
                    161:
1.34    ! rillig    162: // checkdirPackage checks a complete pkgsrc package, including each
        !           163: // of the files individually, and also when seen in combination.
        !           164: func (pkglint *Pkglint) checkdirPackage(dir string) {
1.16      rillig    165:        if trace.Tracing {
1.34    ! rillig    166:                defer trace.Call1(dir)()
1.3       rillig    167:        }
                    168:
1.34    ! rillig    169:        G.Pkg = NewPackage(dir)
1.3       rillig    170:        defer func() { G.Pkg = nil }()
                    171:        pkg := G.Pkg
1.1       rillig    172:
                    173:        // we need to handle the Makefile first to get some variables
1.33      rillig    174:        lines := pkg.loadPackageMakefile()
1.1       rillig    175:        if lines == nil {
                    176:                return
                    177:        }
                    178:
1.33      rillig    179:        files := dirglob(pkg.File("."))
1.3       rillig    180:        if pkg.Pkgdir != "." {
1.33      rillig    181:                files = append(files, dirglob(pkg.File(pkg.Pkgdir))...)
1.1       rillig    182:        }
                    183:        if G.opts.CheckExtra {
1.33      rillig    184:                files = append(files, dirglob(pkg.File(pkg.Filesdir))...)
1.1       rillig    185:        }
1.33      rillig    186:        files = append(files, dirglob(pkg.File(pkg.Patchdir))...)
1.3       rillig    187:        if pkg.DistinfoFile != "distinfo" && pkg.DistinfoFile != "./distinfo" {
1.33      rillig    188:                files = append(files, pkg.File(pkg.DistinfoFile))
1.1       rillig    189:        }
                    190:        haveDistinfo := false
                    191:        havePatches := false
                    192:
1.22      rillig    193:        // Determine the used variables and PLIST directories before checking any of the Makefile fragments.
1.1       rillig    194:        for _, fname := range files {
                    195:                if (hasPrefix(path.Base(fname), "Makefile.") || hasSuffix(fname, ".mk")) &&
                    196:                        !matches(fname, `patch-`) &&
1.3       rillig    197:                        !contains(fname, pkg.Pkgdir+"/") &&
                    198:                        !contains(fname, pkg.Filesdir+"/") {
1.34    ! rillig    199:                        if mklines := LoadMk(fname, MustSucceed); mklines != nil {
        !           200:                                mklines.DetermineUsedVariables()
1.1       rillig    201:                        }
                    202:                }
1.22      rillig    203:                if hasPrefix(path.Base(fname), "PLIST") {
                    204:                        pkg.loadPlistDirs(fname)
                    205:                }
1.1       rillig    206:        }
                    207:
                    208:        for _, fname := range files {
1.7       rillig    209:                if containsVarRef(fname) {
                    210:                        continue
                    211:                }
1.33      rillig    212:                if fname == pkg.File("Makefile") {
1.27      rillig    213:                        if st, err := os.Lstat(fname); err == nil {
                    214:                                pkglint.checkExecutable(st, fname)
                    215:                        }
1.1       rillig    216:                        if G.opts.CheckMakefile {
1.3       rillig    217:                                pkg.checkfilePackageMakefile(fname, lines)
1.1       rillig    218:                        }
                    219:                } else {
1.26      rillig    220:                        pkglint.Checkfile(fname)
1.1       rillig    221:                }
1.3       rillig    222:                if contains(fname, "/patches/patch-") {
1.1       rillig    223:                        havePatches = true
                    224:                } else if hasSuffix(fname, "/distinfo") {
                    225:                        haveDistinfo = true
                    226:                }
1.9       rillig    227:                pkg.checkLocallyModified(fname)
1.1       rillig    228:        }
                    229:
                    230:        if G.opts.CheckDistinfo && G.opts.CheckPatches {
                    231:                if havePatches && !haveDistinfo {
1.33      rillig    232:                        NewLineWhole(pkg.File(pkg.DistinfoFile)).Warnf("File not found. Please run \"%s makepatchsum\".", confMake)
1.1       rillig    233:                }
                    234:        }
                    235:
1.30      rillig    236:        if G.opts.CheckAlternatives {
                    237:                for _, fname := range files {
                    238:                        if path.Base(fname) == "ALTERNATIVES" {
                    239:                                CheckfileAlternatives(fname, pkg.PlistFiles)
                    240:                        }
                    241:                }
                    242:        }
                    243:
1.33      rillig    244:        if !isEmptyDir(pkg.File("scripts")) {
                    245:                NewLineWhole(pkg.File("scripts")).Warnf("This directory and its contents are deprecated! Please call the script(s) explicitly from the corresponding target(s) in the pkg's Makefile.")
1.1       rillig    246:        }
                    247: }
                    248:
1.33      rillig    249: func (pkg *Package) loadPackageMakefile() *MkLines {
                    250:        fname := pkg.File("Makefile")
1.16      rillig    251:        if trace.Tracing {
                    252:                defer trace.Call1(fname)()
1.3       rillig    253:        }
                    254:
                    255:        mainLines, allLines := NewMkLines(nil), NewMkLines(nil)
1.8       rillig    256:        if !pkg.readMakefile(fname, mainLines, allLines, "") {
1.3       rillig    257:                return nil
                    258:        }
                    259:
                    260:        if G.opts.DumpMakefile {
1.24      rillig    261:                G.logOut.WriteLine("Whole Makefile (with all included files) follows:")
1.3       rillig    262:                for _, line := range allLines.lines {
1.24      rillig    263:                        G.logOut.WriteLine(line.String())
1.3       rillig    264:                }
                    265:        }
                    266:
                    267:        allLines.DetermineUsedVariables()
1.32      rillig    268:        allLines.CheckRedundantVariables()
1.3       rillig    269:
1.15      rillig    270:        pkg.Pkgdir = pkg.expandVariableWithDefault("PKGDIR", ".")
                    271:        pkg.DistinfoFile = pkg.expandVariableWithDefault("DISTINFO_FILE", "distinfo")
                    272:        pkg.Filesdir = pkg.expandVariableWithDefault("FILESDIR", "files")
                    273:        pkg.Patchdir = pkg.expandVariableWithDefault("PATCHDIR", "patches")
1.3       rillig    274:
1.33      rillig    275:        // See lang/php/ext.mk
1.3       rillig    276:        if varIsDefined("PHPEXT_MK") {
                    277:                if !varIsDefined("USE_PHP_EXT_PATCHES") {
                    278:                        pkg.Patchdir = "patches"
                    279:                }
                    280:                if varIsDefined("PECL_VERSION") {
                    281:                        pkg.DistinfoFile = "distinfo"
1.33      rillig    282:                } else {
                    283:                        pkg.IgnoreMissingPatches = true
1.3       rillig    284:                }
1.33      rillig    285:
                    286:                // For PHP modules that are not PECL, this combination means that
                    287:                // the patches in the distinfo cannot be found in PATCHDIR.
1.3       rillig    288:        }
                    289:
1.16      rillig    290:        if trace.Tracing {
                    291:                trace.Step1("DISTINFO_FILE=%s", pkg.DistinfoFile)
                    292:                trace.Step1("FILESDIR=%s", pkg.Filesdir)
                    293:                trace.Step1("PATCHDIR=%s", pkg.Patchdir)
                    294:                trace.Step1("PKGDIR=%s", pkg.Pkgdir)
1.3       rillig    295:        }
                    296:
                    297:        return mainLines
                    298: }
                    299:
1.8       rillig    300: func (pkg *Package) readMakefile(fname string, mainLines *MkLines, allLines *MkLines, includingFnameForUsedCheck string) bool {
1.16      rillig    301:        if trace.Tracing {
                    302:                defer trace.Call1(fname)()
1.3       rillig    303:        }
                    304:
1.34    ! rillig    305:        fileMklines := LoadMk(fname, NotEmpty|LogErrors)
        !           306:        if fileMklines == nil {
1.3       rillig    307:                return false
                    308:        }
                    309:
                    310:        isMainMakefile := len(mainLines.mklines) == 0
1.1       rillig    311:
1.32      rillig    312:        result := true
                    313:        lineAction := func(mkline MkLine) bool {
1.3       rillig    314:                if isMainMakefile {
                    315:                        mainLines.mklines = append(mainLines.mklines, mkline)
1.20      rillig    316:                        mainLines.lines = append(mainLines.lines, mkline.Line)
1.3       rillig    317:                }
                    318:                allLines.mklines = append(allLines.mklines, mkline)
1.20      rillig    319:                allLines.lines = append(allLines.lines, mkline.Line)
1.3       rillig    320:
                    321:                var includeFile, incDir, incBase string
                    322:                if mkline.IsInclude() {
1.31      rillig    323:                        inc := mkline.IncludeFile()
1.18      rillig    324:                        includeFile = resolveVariableRefs(mkline.ResolveVarsInRelativePath(inc, true))
1.3       rillig    325:                        if containsVarRef(includeFile) {
                    326:                                if !contains(fname, "/mk/") {
1.18      rillig    327:                                        mkline.Notef("Skipping include file %q. This may result in false warnings.", includeFile)
1.3       rillig    328:                                }
                    329:                                includeFile = ""
                    330:                        }
                    331:                        incDir, incBase = path.Split(includeFile)
                    332:                }
                    333:
                    334:                if includeFile != "" {
                    335:                        if path.Base(fname) != "buildlink3.mk" {
                    336:                                if m, bl3File := match1(includeFile, `^\.\./\.\./(.*)/buildlink3\.mk$`); m {
1.20      rillig    337:                                        G.Pkg.bl3[bl3File] = mkline.Line
1.16      rillig    338:                                        if trace.Tracing {
                    339:                                                trace.Step1("Buildlink3 file in package: %q", bl3File)
1.3       rillig    340:                                        }
                    341:                                }
                    342:                        }
                    343:                }
                    344:
                    345:                if includeFile != "" && G.Pkg.included[includeFile] == nil {
1.20      rillig    346:                        G.Pkg.included[includeFile] = mkline.Line
1.3       rillig    347:
                    348:                        if matches(includeFile, `^\.\./[^./][^/]*/[^/]+`) {
1.14      rillig    349:                                mkline.Warnf("References to other packages should look like \"../../category/package\", not \"../package\".")
1.18      rillig    350:                                mkline.ExplainRelativeDirs()
1.3       rillig    351:                        }
                    352:
                    353:                        if path.Base(fname) == "Makefile" && !hasPrefix(incDir, "../../mk/") && incBase != "buildlink3.mk" && incBase != "builtin.mk" && incBase != "options.mk" {
1.16      rillig    354:                                if trace.Tracing {
                    355:                                        trace.Step1("Including %q sets seenMakefileCommon.", includeFile)
1.3       rillig    356:                                }
                    357:                                G.Pkg.seenMakefileCommon = true
                    358:                        }
                    359:
1.7       rillig    360:                        skip := contains(fname, "/mk/") || hasSuffix(includeFile, "/bsd.pkg.mk") || hasSuffix(includeFile, "/bsd.prefs.mk")
                    361:                        if !skip {
1.3       rillig    362:                                dirname, _ := path.Split(fname)
                    363:                                dirname = cleanpath(dirname)
                    364:
                    365:                                // Only look in the directory relative to the
                    366:                                // current file and in the current working directory.
1.15      rillig    367:                                // Pkglint doesn't have an include dir list, like make(1) does.
1.3       rillig    368:                                if !fileExists(dirname + "/" + includeFile) {
1.29      rillig    369:
                    370:                                        if fileMklines.indentation.IsCheckedFile(includeFile) {
1.32      rillig    371:                                                return true // See https://github.com/rillig/pkglint/issues/1
1.29      rillig    372:
1.33      rillig    373:                                        } else if dirname != pkg.File(".") { // Prevent unnecessary syscalls
                    374:                                                dirname = pkg.File(".")
1.13      rillig    375:                                                if !fileExists(dirname + "/" + includeFile) {
1.18      rillig    376:                                                        mkline.Errorf("Cannot read %q.", dirname+"/"+includeFile)
1.32      rillig    377:                                                        result = false
1.13      rillig    378:                                                        return false
                    379:                                                }
                    380:                                        }
1.3       rillig    381:                                }
                    382:
1.16      rillig    383:                                if trace.Tracing {
                    384:                                        trace.Step1("Including %q.", dirname+"/"+includeFile)
1.3       rillig    385:                                }
1.29      rillig    386:                                absIncluding := ifelseStr(incBase == "Makefile.common" && incDir != "", fname, "")
                    387:                                absIncluded := dirname + "/" + includeFile
                    388:                                if !pkg.readMakefile(absIncluded, mainLines, allLines, absIncluding) {
1.32      rillig    389:                                        result = false
1.3       rillig    390:                                        return false
                    391:                                }
                    392:                        }
                    393:                }
                    394:
                    395:                if mkline.IsVarassign() {
                    396:                        varname, op, value := mkline.Varname(), mkline.Op(), mkline.Value()
                    397:
1.26      rillig    398:                        if op != opAssignDefault || !G.Pkg.vars.Defined(varname) {
1.16      rillig    399:                                if trace.Tracing {
                    400:                                        trace.Stepf("varassign(%q, %q, %q)", varname, op, value)
1.3       rillig    401:                                }
1.26      rillig    402:                                G.Pkg.vars.Define(varname, mkline)
1.3       rillig    403:                        }
                    404:                }
1.32      rillig    405:                return true
1.3       rillig    406:        }
1.32      rillig    407:        atEnd := func(mkline MkLine) {}
                    408:        fileMklines.ForEach(lineAction, atEnd)
1.3       rillig    409:
                    410:        if includingFnameForUsedCheck != "" {
1.27      rillig    411:                fileMklines.checkForUsedComment(G.Pkgsrc.ToRel(includingFnameForUsedCheck))
1.3       rillig    412:        }
                    413:
1.32      rillig    414:        return result
1.3       rillig    415: }
                    416:
                    417: func (pkg *Package) checkfilePackageMakefile(fname string, mklines *MkLines) {
1.16      rillig    418:        if trace.Tracing {
                    419:                defer trace.Call1(fname)()
1.3       rillig    420:        }
                    421:
1.26      rillig    422:        vars := pkg.vars
                    423:        if !vars.Defined("PLIST_SRC") &&
                    424:                !vars.Defined("GENERATE_PLIST") &&
                    425:                !vars.Defined("META_PACKAGE") &&
1.33      rillig    426:                !fileExists(pkg.File(pkg.Pkgdir+"/PLIST")) &&
                    427:                !fileExists(pkg.File(pkg.Pkgdir+"/PLIST.common")) {
1.31      rillig    428:                NewLineWhole(fname).Warnf("Neither PLIST nor PLIST.common exist, and PLIST_SRC is unset.")
1.1       rillig    429:        }
                    430:
1.33      rillig    431:        if (vars.Defined("NO_CHECKSUM") || vars.Defined("META_PACKAGE")) && isEmptyDir(pkg.File(pkg.Patchdir)) {
                    432:                if distinfoFile := pkg.File(pkg.DistinfoFile); fileExists(distinfoFile) {
1.14      rillig    433:                        NewLineWhole(distinfoFile).Warnf("This file should not exist if NO_CHECKSUM or META_PACKAGE is set.")
1.1       rillig    434:                }
                    435:        } else {
1.33      rillig    436:                if distinfoFile := pkg.File(pkg.DistinfoFile); !containsVarRef(distinfoFile) && !fileExists(distinfoFile) {
1.27      rillig    437:                        NewLineWhole(distinfoFile).Warnf("File not found. Please run \"%s makesum\" or define NO_CHECKSUM=yes in the package Makefile.", confMake)
1.1       rillig    438:                }
                    439:        }
                    440:
1.26      rillig    441:        if perlLine, noconfLine := vars.FirstDefinition("REPLACE_PERL"), vars.FirstDefinition("NO_CONFIGURE"); perlLine != nil && noconfLine != nil {
1.20      rillig    442:                perlLine.Warnf("REPLACE_PERL is ignored when NO_CONFIGURE is set (in %s)", noconfLine.ReferenceFrom(perlLine.Line))
1.1       rillig    443:        }
                    444:
1.26      rillig    445:        if !vars.Defined("LICENSE") && !vars.Defined("META_PACKAGE") && pkg.once.FirstTime("LICENSE") {
1.14      rillig    446:                NewLineWhole(fname).Errorf("Each package must define its LICENSE.")
1.1       rillig    447:        }
                    448:
1.26      rillig    449:        if gnuLine, useLine := vars.FirstDefinition("GNU_CONFIGURE"), vars.FirstDefinition("USE_LANGUAGES"); gnuLine != nil && useLine != nil {
1.11      rillig    450:                if matches(useLine.VarassignComment(), `(?-i)\b(?:c|empty|none)\b`) {
1.1       rillig    451:                        // Don't emit a warning, since the comment
                    452:                        // probably contains a statement that C is
                    453:                        // really not needed.
                    454:
1.23      rillig    455:                } else if !G.Infrastructure && useLine.Filename == "../../mk/compiler.mk" {
                    456:                        // Ignore this one
                    457:
1.5       rillig    458:                } else if !matches(useLine.Value(), `(?:^|\s+)(?:c|c99|objc)(?:\s+|$)`) {
1.14      rillig    459:                        gnuLine.Warnf("GNU_CONFIGURE almost always needs a C compiler, but \"c\" is not added to USE_LANGUAGES in %s.",
1.20      rillig    460:                                useLine.ReferenceFrom(gnuLine.Line))
1.1       rillig    461:                }
                    462:        }
                    463:
1.3       rillig    464:        pkg.determineEffectivePkgVars()
                    465:        pkg.checkPossibleDowngrade()
                    466:
1.26      rillig    467:        if !vars.Defined("COMMENT") {
1.14      rillig    468:                NewLineWhole(fname).Warnf("No COMMENT given.")
1.3       rillig    469:        }
                    470:
1.26      rillig    471:        if imake, x11 := vars.FirstDefinition("USE_IMAKE"), vars.FirstDefinition("USE_X11"); imake != nil && x11 != nil {
1.20      rillig    472:                if !hasSuffix(x11.Filename, "/mk/x11.buildlink3.mk") {
                    473:                        imake.Notef("USE_IMAKE makes USE_X11 in %s superfluous.", x11.ReferenceFrom(imake.Line))
1.7       rillig    474:                }
1.3       rillig    475:        }
                    476:
                    477:        pkg.checkUpdate()
                    478:        mklines.Check()
1.25      rillig    479:        pkg.CheckVarorder(mklines)
1.3       rillig    480:        SaveAutofixChanges(mklines.lines)
                    481: }
                    482:
                    483: func (pkg *Package) getNbpart() string {
1.26      rillig    484:        line := pkg.vars.FirstDefinition("PKGREVISION")
1.3       rillig    485:        if line == nil {
                    486:                return ""
                    487:        }
                    488:        pkgrevision := line.Value()
                    489:        if rev, err := strconv.Atoi(pkgrevision); err == nil {
                    490:                return "nb" + strconv.Itoa(rev)
                    491:        }
                    492:        return ""
                    493: }
                    494:
                    495: func (pkg *Package) determineEffectivePkgVars() {
1.26      rillig    496:        distnameLine := pkg.vars.FirstDefinition("DISTNAME")
                    497:        pkgnameLine := pkg.vars.FirstDefinition("PKGNAME")
1.1       rillig    498:
                    499:        distname := ""
                    500:        if distnameLine != nil {
1.3       rillig    501:                distname = distnameLine.Value()
1.1       rillig    502:        }
                    503:        pkgname := ""
                    504:        if pkgnameLine != nil {
1.3       rillig    505:                pkgname = pkgnameLine.Value()
1.1       rillig    506:        }
                    507:
                    508:        if distname != "" && pkgname != "" {
1.3       rillig    509:                pkgname = pkg.pkgnameFromDistname(pkgname, distname)
1.1       rillig    510:        }
                    511:
1.11      rillig    512:        if pkgname != "" && pkgname == distname && pkgnameLine.VarassignComment() == "" {
1.14      rillig    513:                pkgnameLine.Notef("PKGNAME is ${DISTNAME} by default. You probably don't need to define PKGNAME.")
1.1       rillig    514:        }
                    515:
                    516:        if pkgname == "" && distname != "" && !containsVarRef(distname) && !matches(distname, rePkgname) {
1.14      rillig    517:                distnameLine.Warnf("As DISTNAME is not a valid package name, please define the PKGNAME explicitly.")
1.1       rillig    518:        }
                    519:
1.3       rillig    520:        if pkgname != "" && !containsVarRef(pkgname) {
                    521:                if m, m1, m2 := match2(pkgname, rePkgname); m {
                    522:                        pkg.EffectivePkgname = pkgname + pkg.getNbpart()
                    523:                        pkg.EffectivePkgnameLine = pkgnameLine
                    524:                        pkg.EffectivePkgbase = m1
                    525:                        pkg.EffectivePkgversion = m2
                    526:                }
                    527:        }
                    528:        if pkg.EffectivePkgnameLine == nil && distname != "" && !containsVarRef(distname) {
                    529:                if m, m1, m2 := match2(distname, rePkgname); m {
                    530:                        pkg.EffectivePkgname = distname + pkg.getNbpart()
                    531:                        pkg.EffectivePkgnameLine = distnameLine
                    532:                        pkg.EffectivePkgbase = m1
                    533:                        pkg.EffectivePkgversion = m2
                    534:                }
                    535:        }
                    536:        if pkg.EffectivePkgnameLine != nil {
1.16      rillig    537:                if trace.Tracing {
                    538:                        trace.Stepf("Effective name=%q base=%q version=%q",
1.3       rillig    539:                                pkg.EffectivePkgname, pkg.EffectivePkgbase, pkg.EffectivePkgversion)
                    540:                }
1.1       rillig    541:        }
1.3       rillig    542: }
1.1       rillig    543:
1.3       rillig    544: func (pkg *Package) pkgnameFromDistname(pkgname, distname string) string {
1.9       rillig    545:        tokens := NewMkParser(dummyLine, pkgname, false).MkTokens()
1.1       rillig    546:
1.9       rillig    547:        subst := func(str, smod string) (result string) {
1.16      rillig    548:                if trace.Tracing {
                    549:                        defer trace.Call(str, smod, trace.Ref(result))()
1.9       rillig    550:                }
                    551:                qsep := regexp.QuoteMeta(smod[1:2])
1.19      rillig    552:                if m, left, from, right, to, flags := regex.Match5(smod, regex.Pattern(`^S`+qsep+`(\^?)([^:]*?)(\$?)`+qsep+`([^:]*)`+qsep+`([1g]*)$`)); m {
1.9       rillig    553:                        result := mkopSubst(str, left != "", from, right != "", to, flags)
1.16      rillig    554:                        if trace.Tracing {
                    555:                                trace.Stepf("subst %q %q => %q", str, smod, result)
1.3       rillig    556:                        }
1.9       rillig    557:                        return result
1.3       rillig    558:                }
1.9       rillig    559:                return str
1.1       rillig    560:        }
1.9       rillig    561:
                    562:        result := ""
                    563:        for _, token := range tokens {
                    564:                if token.Varuse != nil && token.Varuse.varname == "DISTNAME" {
                    565:                        newDistname := distname
                    566:                        for _, mod := range token.Varuse.modifiers {
                    567:                                if mod == "tl" {
                    568:                                        newDistname = strings.ToLower(newDistname)
                    569:                                } else if hasPrefix(mod, "S") {
                    570:                                        newDistname = subst(newDistname, mod)
                    571:                                } else {
                    572:                                        newDistname = token.Text
                    573:                                        break
                    574:                                }
                    575:                        }
                    576:                        result += newDistname
                    577:                } else {
                    578:                        result += token.Text
                    579:                }
                    580:        }
                    581:        return result
1.3       rillig    582: }
1.1       rillig    583:
1.15      rillig    584: func (pkg *Package) expandVariableWithDefault(varname, defaultValue string) string {
1.26      rillig    585:        mkline := G.Pkg.vars.FirstDefinition(varname)
1.15      rillig    586:        if mkline == nil {
                    587:                return defaultValue
                    588:        }
                    589:
                    590:        value := mkline.Value()
1.18      rillig    591:        value = mkline.ResolveVarsInRelativePath(value, true)
1.15      rillig    592:        if containsVarRef(value) {
                    593:                value = resolveVariableRefs(value)
                    594:        }
1.16      rillig    595:        if trace.Tracing {
                    596:                trace.Step2("Expanded %q to %q", varname, value)
1.15      rillig    597:        }
                    598:        return value
                    599: }
                    600:
1.3       rillig    601: func (pkg *Package) checkUpdate() {
                    602:        if pkg.EffectivePkgbase != "" {
1.27      rillig    603:                for _, sugg := range G.Pkgsrc.GetSuggestedPackageUpdates() {
1.3       rillig    604:                        if pkg.EffectivePkgbase != sugg.Pkgname {
1.1       rillig    605:                                continue
                    606:                        }
                    607:
1.3       rillig    608:                        suggver, comment := sugg.Version, sugg.Comment
1.1       rillig    609:                        if comment != "" {
                    610:                                comment = " (" + comment + ")"
                    611:                        }
                    612:
1.3       rillig    613:                        pkgnameLine := pkg.EffectivePkgnameLine
1.15      rillig    614:                        cmp := pkgver.Compare(pkg.EffectivePkgversion, suggver)
1.1       rillig    615:                        switch {
                    616:                        case cmp < 0:
1.14      rillig    617:                                pkgnameLine.Warnf("This package should be updated to %s%s.", sugg.Version, comment)
                    618:                                Explain(
1.1       rillig    619:                                        "The wishlist for package updates in doc/TODO mentions that a newer",
                    620:                                        "version of this package is available.")
                    621:                        case cmp > 0:
1.14      rillig    622:                                pkgnameLine.Notef("This package is newer than the update request to %s%s.", suggver, comment)
1.1       rillig    623:                        default:
1.14      rillig    624:                                pkgnameLine.Notef("The update request to %s from doc/TODO%s has been done.", suggver, comment)
1.1       rillig    625:                        }
                    626:                }
                    627:        }
                    628: }
                    629:
1.25      rillig    630: // CheckVarorder checks that in simple package Makefiles,
                    631: // the most common variables appear in a fixed order.
                    632: // The order itself is a little arbitrary but provides
                    633: // at least a bit of consistency.
                    634: func (pkg *Package) CheckVarorder(mklines *MkLines) {
1.16      rillig    635:        if trace.Tracing {
                    636:                defer trace.Call0()()
1.1       rillig    637:        }
                    638:
1.3       rillig    639:        if !G.opts.WarnOrder || pkg.seenMakefileCommon {
1.1       rillig    640:                return
                    641:        }
                    642:
1.25      rillig    643:        type Repetition uint8
1.1       rillig    644:        const (
1.25      rillig    645:                optional Repetition = iota
                    646:                once
1.1       rillig    647:                many
                    648:        )
1.25      rillig    649:        type Variable struct {
                    650:                varname    string
                    651:                repetition Repetition
                    652:        }
                    653:        type Section struct {
                    654:                repetition Repetition
                    655:                vars       []Variable
                    656:        }
                    657:        variable := func(name string, repetition Repetition) Variable { return Variable{name, repetition} }
                    658:        section := func(repetition Repetition, vars ...Variable) Section { return Section{repetition, vars} }
                    659:
                    660:        var sections = []Section{
                    661:                section(once,
                    662:                        variable("GITHUB_PROJECT", optional), // either here or below MASTER_SITES
                    663:                        variable("GITHUB_TAG", optional),
                    664:                        variable("DISTNAME", optional),
                    665:                        variable("PKGNAME", optional),
                    666:                        variable("PKGREVISION", optional),
                    667:                        variable("CATEGORIES", once),
                    668:                        variable("MASTER_SITES", many),
                    669:                        variable("GITHUB_PROJECT", optional), // either here or at the very top
                    670:                        variable("GITHUB_TAG", optional),
                    671:                        variable("DIST_SUBDIR", optional),
                    672:                        variable("EXTRACT_SUFX", optional),
                    673:                        variable("DISTFILES", many),
                    674:                        variable("SITES.*", many)),
                    675:                section(optional,
                    676:                        variable("PATCH_SITES", optional), // or once?
                    677:                        variable("PATCH_SITE_SUBDIR", optional),
                    678:                        variable("PATCHFILES", optional), // or once?
                    679:                        variable("PATCH_DIST_ARGS", optional),
                    680:                        variable("PATCH_DIST_STRIP", optional),
                    681:                        variable("PATCH_DIST_CAT", optional)),
                    682:                section(once,
                    683:                        variable("MAINTAINER", optional),
                    684:                        variable("OWNER", optional),
                    685:                        variable("HOMEPAGE", optional),
                    686:                        variable("COMMENT", once),
                    687:                        variable("LICENSE", once)),
                    688:                section(optional,
                    689:                        variable("LICENSE_FILE", optional),
                    690:                        variable("RESTRICTED", optional),
                    691:                        variable("NO_BIN_ON_CDROM", optional),
                    692:                        variable("NO_BIN_ON_FTP", optional),
                    693:                        variable("NO_SRC_ON_CDROM", optional),
                    694:                        variable("NO_SRC_ON_FTP", optional)),
                    695:                section(optional,
                    696:                        variable("BROKEN_EXCEPT_ON_PLATFORM", many),
                    697:                        variable("BROKEN_ON_PLATFORM", many),
                    698:                        variable("NOT_FOR_PLATFORM", many),
                    699:                        variable("ONLY_FOR_PLATFORM", many),
                    700:                        variable("NOT_FOR_COMPILER", many),
                    701:                        variable("ONLY_FOR_COMPILER", many),
                    702:                        variable("NOT_FOR_UNPRIVILEGED", optional),
                    703:                        variable("ONLY_FOR_UNPRIVILEGED", optional)),
                    704:                section(optional,
                    705:                        variable("BUILD_DEPENDS", many),
                    706:                        variable("TOOL_DEPENDS", many),
                    707:                        variable("DEPENDS", many)),
                    708:        }
                    709:
                    710:        firstRelevant := -1
                    711:        lastRelevant := -1
                    712:        skip := func() bool {
                    713:                relevantVars := make(map[string]bool)
                    714:                for _, section := range sections {
                    715:                        for _, variable := range section.vars {
                    716:                                relevantVars[variable.varname] = true
                    717:                        }
                    718:                }
1.1       rillig    719:
1.25      rillig    720:                firstIrrelevant := -1
                    721:                for i, mkline := range mklines.mklines {
                    722:                        switch {
                    723:                        case mkline.IsVarassign(), mkline.IsCommentedVarassign():
                    724:                                varcanon := mkline.Varcanon()
                    725:                                if relevantVars[varcanon] {
                    726:                                        if firstRelevant == -1 {
                    727:                                                firstRelevant = i
                    728:                                        }
                    729:                                        if firstIrrelevant != -1 {
                    730:                                                if trace.Tracing {
                    731:                                                        trace.Stepf("Skipping varorder because of line %s.",
                    732:                                                                mklines.mklines[firstIrrelevant].Linenos())
                    733:                                                }
                    734:                                                return true
                    735:                                        }
                    736:                                        lastRelevant = i
                    737:                                } else {
                    738:                                        if firstIrrelevant == -1 {
                    739:                                                firstIrrelevant = i
                    740:                                        }
                    741:                                }
1.1       rillig    742:
1.25      rillig    743:                        case mkline.IsComment(), mkline.IsEmpty():
1.1       rillig    744:                                break
1.25      rillig    745:
                    746:                        default:
                    747:                                if firstIrrelevant == -1 {
                    748:                                        firstIrrelevant = i
                    749:                                }
1.1       rillig    750:                        }
                    751:                }
                    752:
1.28      rillig    753:                if firstRelevant == -1 {
                    754:                        return true
                    755:                }
1.25      rillig    756:                interesting := mklines.mklines[firstRelevant : lastRelevant+1]
1.1       rillig    757:
1.25      rillig    758:                varcanon := func() string {
                    759:                        for len(interesting) != 0 && interesting[0].IsComment() {
                    760:                                interesting = interesting[1:]
                    761:                        }
                    762:                        if len(interesting) != 0 && (interesting[0].IsVarassign() || interesting[0].IsCommentedVarassign()) {
                    763:                                return interesting[0].Varcanon()
                    764:                        }
                    765:                        return ""
                    766:                }
1.1       rillig    767:
1.25      rillig    768:                for _, section := range sections {
                    769:                        for _, variable := range section.vars {
                    770:                                switch variable.repetition {
                    771:                                case optional:
                    772:                                        if varcanon() == variable.varname {
                    773:                                                interesting = interesting[1:]
                    774:                                        }
                    775:                                case once:
                    776:                                        if varcanon() == variable.varname {
                    777:                                                interesting = interesting[1:]
                    778:                                        } else if section.repetition == once {
                    779:                                                if variable.varname != "LICENSE" {
                    780:                                                        if trace.Tracing {
                    781:                                                                trace.Stepf("Wrong varorder because %s is missing.", variable.varname)
                    782:                                                        }
                    783:                                                        return false
                    784:                                                }
                    785:                                        }
                    786:                                case many:
                    787:                                        for varcanon() == variable.varname {
                    788:                                                interesting = interesting[1:]
                    789:                                        }
1.1       rillig    790:                                }
                    791:                        }
                    792:
1.25      rillig    793:                        for len(interesting) != 0 && (interesting[0].IsEmpty() || interesting[0].IsComment()) {
                    794:                                interesting = interesting[1:]
1.1       rillig    795:                        }
1.25      rillig    796:                }
1.1       rillig    797:
1.25      rillig    798:                return len(interesting) == 0
                    799:        }
1.1       rillig    800:
1.25      rillig    801:        if skip() {
                    802:                return
                    803:        }
1.1       rillig    804:
1.25      rillig    805:        var canonical []string
                    806:        for _, section := range sections {
                    807:                for _, variable := range section.vars {
                    808:                        found := false
                    809:                        for _, mkline := range mklines.mklines[firstRelevant : lastRelevant+1] {
                    810:                                if mkline.IsVarassign() || mkline.IsCommentedVarassign() {
                    811:                                        if mkline.Varcanon() == variable.varname {
                    812:                                                canonical = append(canonical, mkline.Varname())
                    813:                                                found = true
1.21      rillig    814:                                        }
1.1       rillig    815:                                }
                    816:                        }
1.25      rillig    817:                        if !found && section.repetition == once && variable.repetition == once {
                    818:                                canonical = append(canonical, variable.varname)
1.1       rillig    819:                        }
                    820:                }
1.25      rillig    821:                if len(canonical) != 0 && canonical[len(canonical)-1] != "empty line" {
                    822:                        canonical = append(canonical, "empty line")
                    823:                }
                    824:        }
                    825:        if len(canonical) != 0 && canonical[len(canonical)-1] == "empty line" {
                    826:                canonical = canonical[:len(canonical)-1]
1.1       rillig    827:        }
1.25      rillig    828:
                    829:        mkline := mklines.mklines[firstRelevant]
                    830:        mkline.Warnf("The canonical order of the variables is %s.", strings.Join(canonical, ", "))
                    831:        Explain(
                    832:                "In simple package Makefiles, some common variables should be",
                    833:                "arranged in a specific order.",
                    834:                "",
                    835:                "See doc/Makefile-example or the pkgsrc guide, section",
                    836:                "\"Package components\", subsection \"Makefile\" for more information.")
1.1       rillig    837: }
1.3       rillig    838:
                    839: func (mklines *MkLines) checkForUsedComment(relativeName string) {
                    840:        lines := mklines.lines
                    841:        if len(lines) < 3 {
                    842:                return
                    843:        }
                    844:
                    845:        expected := "# used by " + relativeName
                    846:        for _, line := range lines {
1.20      rillig    847:                if line.Text == expected {
1.3       rillig    848:                        return
                    849:                }
                    850:        }
                    851:
                    852:        i := 0
1.20      rillig    853:        for i < 2 && hasPrefix(lines[i].Text, "#") {
1.3       rillig    854:                i++
                    855:        }
                    856:
1.23      rillig    857:        fix := lines[i].Autofix()
                    858:        fix.Warnf("Please add a line %q here.", expected)
                    859:        fix.Explain(
                    860:                "Since Makefile.common files usually don't have any comments and",
                    861:                "therefore not a clearly defined interface, they should at least",
                    862:                "contain references to all files that include them, so that it is",
                    863:                "easier to see what effects future changes may have.",
                    864:                "",
                    865:                "If there are more than five packages that use a Makefile.common,",
                    866:                "you should think about giving it a proper name (maybe plugin.mk) and",
                    867:                "documenting its interface.")
                    868:        fix.InsertBefore(expected)
                    869:        fix.Apply()
                    870:
1.3       rillig    871:        SaveAutofixChanges(lines)
                    872: }
1.9       rillig    873:
                    874: func (pkg *Package) checkLocallyModified(fname string) {
1.16      rillig    875:        if trace.Tracing {
                    876:                defer trace.Call(fname)()
1.9       rillig    877:        }
                    878:
1.26      rillig    879:        ownerLine := pkg.vars.FirstDefinition("OWNER")
                    880:        maintainerLine := pkg.vars.FirstDefinition("MAINTAINER")
1.9       rillig    881:        owner := ""
                    882:        maintainer := ""
                    883:        if ownerLine != nil && !containsVarRef(ownerLine.Value()) {
                    884:                owner = ownerLine.Value()
                    885:        }
                    886:        if maintainerLine != nil && !containsVarRef(maintainerLine.Value()) && maintainerLine.Value() != "pkgsrc-users@NetBSD.org" {
                    887:                maintainer = maintainerLine.Value()
                    888:        }
                    889:        if owner == "" && maintainer == "" {
                    890:                return
                    891:        }
                    892:
1.13      rillig    893:        username := G.CurrentUsername
1.16      rillig    894:        if trace.Tracing {
                    895:                trace.Stepf("user=%q owner=%q maintainer=%q", username, owner, maintainer)
1.9       rillig    896:        }
                    897:
                    898:        if username == strings.Split(owner, "@")[0] || username == strings.Split(maintainer, "@")[0] {
                    899:                return
                    900:        }
                    901:
                    902:        if isLocallyModified(fname) {
                    903:                if owner != "" {
1.14      rillig    904:                        NewLineWhole(fname).Warnf("Don't commit changes to this file without asking the OWNER, %s.", owner)
                    905:                        Explain(
1.9       rillig    906:                                "See the pkgsrc guide, section \"Package components\",",
                    907:                                "keyword \"owner\", for more information.")
                    908:                }
                    909:                if maintainer != "" {
1.14      rillig    910:                        NewLineWhole(fname).Notef("Please only commit changes that %s would approve.", maintainer)
                    911:                        Explain(
1.9       rillig    912:                                "See the pkgsrc guide, section \"Package components\",",
                    913:                                "keyword \"maintainer\", for more information.")
                    914:                }
                    915:        }
                    916: }
1.12      rillig    917:
1.18      rillig    918: func (pkg *Package) CheckInclude(mkline MkLine, indentation *Indentation) {
1.34    ! rillig    919:        conditionalVars := mkline.ConditionalVars()
        !           920:        if conditionalVars == "" {
        !           921:                conditionalVars = indentation.Varnames()
        !           922:                mkline.SetConditionalVars(conditionalVars)
1.12      rillig    923:        }
                    924:
1.33      rillig    925:        if path.Dir(abspath(mkline.Filename)) == abspath(pkg.File(".")) {
1.31      rillig    926:                includefile := mkline.IncludeFile()
1.12      rillig    927:
                    928:                if indentation.IsConditional() {
                    929:                        pkg.conditionalIncludes[includefile] = mkline
                    930:                        if other := pkg.unconditionalIncludes[includefile]; other != nil {
1.16      rillig    931:                                mkline.Warnf("%q is included conditionally here (depending on %s) and unconditionally in %s.",
1.34    ! rillig    932:                                        cleanpath(includefile), mkline.ConditionalVars(), other.ReferenceFrom(mkline.Line))
1.12      rillig    933:                        }
                    934:                } else {
                    935:                        pkg.unconditionalIncludes[includefile] = mkline
                    936:                        if other := pkg.conditionalIncludes[includefile]; other != nil {
1.16      rillig    937:                                mkline.Warnf("%q is included unconditionally here and conditionally in %s (depending on %s).",
1.34    ! rillig    938:                                        cleanpath(includefile), other.ReferenceFrom(mkline.Line), other.ConditionalVars())
1.12      rillig    939:                        }
                    940:                }
                    941:        }
                    942: }
1.22      rillig    943:
                    944: func (pkg *Package) loadPlistDirs(plistFilename string) {
1.34    ! rillig    945:        lines := Load(plistFilename, MustSucceed)
1.22      rillig    946:        for _, line := range lines {
                    947:                text := line.Text
1.34    ! rillig    948:                pkg.PlistFiles[text] = true // XXX: ignores PLIST conditions for now
1.22      rillig    949:                // Keep in sync with PlistChecker.collectFilesAndDirs
                    950:                if !contains(text, "$") && !contains(text, "@") {
                    951:                        for dir := path.Dir(text); dir != "."; dir = path.Dir(dir) {
                    952:                                pkg.PlistDirs[dir] = true
                    953:                        }
                    954:                }
                    955:        }
                    956: }

CVSweb <webmaster@jp.NetBSD.org>