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>