Annotation of pkgsrc/pkgtools/pkglint/files/package.go, Revision 1.56
1.41 rillig 1: package pkglint
1.1 rillig 2:
3: import (
1.15 rillig 4: "netbsd.org/pkglint/pkgver"
1.48 rillig 5: "os"
1.1 rillig 6: "path"
7: "strconv"
8: "strings"
9: )
10:
1.41 rillig 11: // TODO: What about package names that refer to other variables?
1.31 rillig 12: const rePkgname = `^([\w\-.+]+)-(\d[.0-9A-Z_a-z]*)$`
1.15 rillig 13:
1.41 rillig 14: // Package is the pkgsrc package that is currently checked.
15: //
16: // Most of the information is loaded first, and after loading the actual checks take place.
17: // This is necessary because variables in Makefiles may be used before they are defined,
18: // and such dependencies often span multiple files that are included indirectly.
1.3 rillig 19: type Package struct {
1.38 rillig 20: dir string // The directory of the package, for resolving files
21: Pkgpath string // e.g. "category/pkgdir"
22: Pkgdir string // PKGDIR from the package Makefile
23: Filesdir string // FILESDIR from the package Makefile
24: Patchdir string // PATCHDIR from the package Makefile
25: DistinfoFile string // DISTINFO_FILE from the package Makefile
26: EffectivePkgname string // PKGNAME or DISTNAME from the package Makefile, including nb13
1.41 rillig 27: EffectivePkgbase string // EffectivePkgname without the version
1.38 rillig 28: EffectivePkgversion string // The version part of the effective PKGNAME, excluding nb13
1.41 rillig 29: EffectivePkgnameLine MkLine // The origin of the three Effective* values
1.38 rillig 30: Plist PlistContent // Files and directories mentioned in the PLIST files
1.18 rillig 31:
1.47 rillig 32: vars Scope
33: bl3 map[string]MkLine // buildlink3.mk name => line; contains only buildlink3.mk files that are directly included.
34:
35: // Remembers the Makefile fragments that have already been included.
36: // The key to the map is the filename relative to the package directory.
37: // Typical keys are "../../category/package/buildlink3.mk".
38: //
39: // TODO: Include files with multiple-inclusion guard only once.
40: //
41: // TODO: Include files without multiple-inclusion guard as often as needed.
42: //
43: // TODO: Set an upper limit, to prevent denial of service.
44: included Once
45:
46: seenMakefileCommon bool // Does the package have any .includes?
1.41 rillig 47:
48: // Files from .include lines that are nested inside .if.
49: // They often depend on OPSYS or on the existence of files in the build environment.
50: conditionalIncludes map[string]MkLine
51: // Files from .include lines that are not nested.
52: // These are cross-checked with buildlink3.mk whether they are unconditional there, too.
1.18 rillig 53: unconditionalIncludes map[string]MkLine
1.41 rillig 54:
55: once Once
56: IgnoreMissingPatches bool // In distinfo, don't warn about patches that cannot be found.
1.3 rillig 57: }
58:
1.34 rillig 59: func NewPackage(dir string) *Package {
60: pkgpath := G.Pkgsrc.ToRel(dir)
61: if strings.Count(pkgpath, "/") != 1 {
1.56 ! rillig 62: assertf(false, "Package directory %q must be two subdirectories below the pkgsrc root %q.",
1.41 rillig 63: dir, G.Pkgsrc.File("."))
1.34 rillig 64: }
65:
1.39 rillig 66: pkg := Package{
1.34 rillig 67: dir: dir,
1.12 rillig 68: Pkgpath: pkgpath,
1.33 rillig 69: Pkgdir: ".",
1.41 rillig 70: Filesdir: "files", // TODO: Redundant, see the vars.Fallback below.
71: Patchdir: "patches", // TODO: Redundant, see the vars.Fallback below.
72: DistinfoFile: "${PKGDIR}/distinfo", // TODO: Redundant, see the vars.Fallback below.
1.38 rillig 73: Plist: NewPlistContent(),
1.26 rillig 74: vars: NewScope(),
1.41 rillig 75: bl3: make(map[string]MkLine),
1.47 rillig 76: included: Once{},
1.18 rillig 77: conditionalIncludes: make(map[string]MkLine),
78: unconditionalIncludes: make(map[string]MkLine),
1.3 rillig 79: }
1.35 rillig 80: pkg.vars.DefineAll(G.Pkgsrc.UserDefinedVars)
1.36 rillig 81:
82: pkg.vars.Fallback("PKGDIR", ".")
83: pkg.vars.Fallback("DISTINFO_FILE", "${PKGDIR}/distinfo")
84: pkg.vars.Fallback("FILESDIR", "files")
85: pkg.vars.Fallback("PATCHDIR", "patches")
86: pkg.vars.Fallback("KRB5_TYPE", "heimdal")
87: pkg.vars.Fallback("PGSQL_VERSION", "95")
1.50 rillig 88:
89: // In reality, this is an absolute pathname. Since this variable is
90: // typically used in the form ${.CURDIR}/../../somewhere, this doesn't
91: // matter much.
92: pkg.vars.Fallback(".CURDIR", ".")
1.36 rillig 93:
1.39 rillig 94: return &pkg
1.3 rillig 95: }
96:
1.38 rillig 97: // File returns the (possibly absolute) path to relativeFileName,
1.33 rillig 98: // as resolved from the package's directory.
1.36 rillig 99: // Variables that are known in the package are resolved, e.g. ${PKGDIR}.
1.38 rillig 100: func (pkg *Package) File(relativeFileName string) string {
1.49 rillig 101: return cleanpath(resolveVariableRefs(nil, pkg.dir+"/"+relativeFileName))
1.33 rillig 102: }
103:
1.3 rillig 104: func (pkg *Package) checkPossibleDowngrade() {
1.16 rillig 105: if trace.Tracing {
106: defer trace.Call0()()
1.3 rillig 107: }
108:
109: m, _, pkgversion := match2(pkg.EffectivePkgname, rePkgname)
1.1 rillig 110: if !m {
111: return
112: }
113:
1.3 rillig 114: mkline := pkg.EffectivePkgnameLine
1.1 rillig 115:
1.27 rillig 116: change := G.Pkgsrc.LastChange[pkg.Pkgpath]
1.1 rillig 117: if change == nil {
1.16 rillig 118: if trace.Tracing {
119: trace.Step1("No change log for package %q", pkg.Pkgpath)
1.3 rillig 120: }
1.1 rillig 121: return
122: }
123:
1.3 rillig 124: if change.Action == "Updated" {
1.50 rillig 125: pkgversionNorev := replaceAll(pkgversion, `nb\d+$`, "")
126: changeNorev := replaceAll(change.Version, `nb\d+$`, "")
127: cmp := pkgver.Compare(pkgversionNorev, changeNorev)
128: switch {
129: case cmp < 0:
1.41 rillig 130: mkline.Warnf("The package is being downgraded from %s (see %s) to %s.",
1.44 rillig 131: change.Version, mkline.Line.RefToLocation(change.Location), pkgversion)
1.50 rillig 132: mkline.Explain(
1.4 rillig 133: "The files in doc/CHANGES-*, in which all version changes are",
134: "recorded, have a higher version number than what the package says.",
135: "This is unusual, since packages are typically upgraded instead of",
136: "downgraded.")
1.41 rillig 137:
1.52 rillig 138: case cmp > 0 && !isLocallyModified(mkline.Filename):
1.50 rillig 139: mkline.Notef("Package version %q is greater than the latest %q from %s.",
140: pkgversion, change.Version, mkline.Line.RefToLocation(change.Location))
141: mkline.Explain(
142: "Each update to a package should be mentioned in the doc/CHANGES file.",
143: "To do this after updating a package, run",
144: sprintf("%q,", bmake("cce")),
145: "which is the abbreviation for commit-changes-entry.")
1.1 rillig 146: }
147: }
148: }
149:
1.42 rillig 150: // checkLinesBuildlink3Inclusion checks whether the package Makefile and
1.41 rillig 151: // the corresponding buildlink3.mk agree for all included buildlink3.mk
152: // files whether they are included conditionally or unconditionally.
1.42 rillig 153: func (pkg *Package) checkLinesBuildlink3Inclusion(mklines MkLines) {
1.16 rillig 154: if trace.Tracing {
155: defer trace.Call0()()
1.1 rillig 156: }
157:
158: // Collect all the included buildlink3.mk files from the file.
1.18 rillig 159: includedFiles := make(map[string]MkLine)
1.3 rillig 160: for _, mkline := range mklines.mklines {
161: if mkline.IsInclude() {
1.39 rillig 162: includedFile := mkline.IncludedFile()
1.44 rillig 163: if matches(includedFile, `^\.\./\.\./.*/buildlink3\.mk`) {
164: includedFiles[includedFile] = mkline
165: if pkg.bl3[includedFile] == nil {
166: mkline.Warnf("%s is included by this file but not by the package.", includedFile)
1.1 rillig 167: }
168: }
169: }
170: }
171:
1.16 rillig 172: if trace.Tracing {
1.8 rillig 173: for packageBl3 := range pkg.bl3 {
1.1 rillig 174: if includedFiles[packageBl3] == nil {
1.44 rillig 175: trace.Step1("%s is included by the package but not by the buildlink3.mk file.", packageBl3)
1.1 rillig 176: }
177: }
178: }
179: }
180:
1.48 rillig 181: func (pkg *Package) load() ([]string, MkLines, MkLines) {
182: // Load the package Makefile and all included files,
183: // to collect all used and defined variables and similar data.
184: mklines, allLines := pkg.loadPackageMakefile()
185: if mklines == nil {
186: return nil, nil, nil
187: }
188:
189: files := dirglob(pkg.File("."))
190: if pkg.Pkgdir != "." {
191: files = append(files, dirglob(pkg.File(pkg.Pkgdir))...)
192: }
193: if G.Opts.CheckExtra {
194: files = append(files, dirglob(pkg.File(pkg.Filesdir))...)
195: }
196: files = append(files, dirglob(pkg.File(pkg.Patchdir))...)
197: if pkg.DistinfoFile != pkg.vars.fallback["DISTINFO_FILE"] {
198: files = append(files, pkg.File(pkg.DistinfoFile))
199: }
200:
201: // Determine the used variables and PLIST directories before checking any of the Makefile fragments.
202: // TODO: Why is this code necessary? What effect does it have?
203: for _, filename := range files {
204: basename := path.Base(filename)
205: if (hasPrefix(basename, "Makefile.") || hasSuffix(filename, ".mk")) &&
206: !matches(filename, `patch-`) &&
207: !contains(filename, pkg.Pkgdir+"/") &&
208: !contains(filename, pkg.Filesdir+"/") {
209: if fragmentMklines := LoadMk(filename, MustSucceed); fragmentMklines != nil {
210: fragmentMklines.collectUsedVariables()
211: }
212: }
213: if hasPrefix(basename, "PLIST") {
214: pkg.loadPlistDirs(filename)
215: }
216: }
217:
218: return files, mklines, allLines
219: }
220:
1.53 rillig 221: func (pkg *Package) check(filenames []string, mklines, allLines MkLines) {
1.48 rillig 222: haveDistinfo := false
223: havePatches := false
224:
1.53 rillig 225: for _, filename := range filenames {
1.48 rillig 226: if containsVarRef(filename) {
227: if trace.Tracing {
228: trace.Stepf("Skipping file %q because the name contains an unresolved variable.", filename)
229: }
230: continue
231: }
232:
233: st, err := os.Lstat(filename)
234: switch {
235: case err != nil:
236: // For missing custom distinfo file, an error message is already generated
237: // for the line where DISTINFO_FILE is defined.
238: //
239: // For all other cases it is next to impossible to reach this branch
240: // since all those files come from calls to dirglob.
241: break
242:
243: case path.Base(filename) == "Makefile":
244: G.checkExecutable(filename, st.Mode())
245: pkg.checkfilePackageMakefile(filename, mklines, allLines)
246:
247: default:
248: G.checkDirent(filename, st.Mode())
249: }
250:
251: if contains(filename, "/patches/patch-") {
252: havePatches = true
253: } else if hasSuffix(filename, "/distinfo") {
254: haveDistinfo = true
255: }
256: pkg.checkLocallyModified(filename)
257: }
258:
259: if pkg.Pkgdir == "." {
260: if havePatches && !haveDistinfo {
1.51 rillig 261: line := NewLineWhole(pkg.File(pkg.DistinfoFile))
262: line.Warnf("A package with patches should have a distinfo file.")
263: line.Explain(
264: "To generate a distinfo file for the existing patches, run",
265: sprintf("%q.", bmake("makepatchsum")))
1.48 rillig 266: }
267: }
268: }
269:
1.47 rillig 270: func (pkg *Package) loadPackageMakefile() (MkLines, MkLines) {
1.39 rillig 271: filename := pkg.File("Makefile")
1.16 rillig 272: if trace.Tracing {
1.39 rillig 273: defer trace.Call1(filename)()
1.3 rillig 274: }
275:
1.39 rillig 276: mainLines := NewMkLines(NewLines(filename, nil))
1.38 rillig 277: allLines := NewMkLines(NewLines("", nil))
1.39 rillig 278: if _, result := pkg.readMakefile(filename, mainLines, allLines, ""); !result {
279: LoadMk(filename, NotEmpty|LogErrors) // Just for the LogErrors.
1.47 rillig 280: return nil, nil
1.3 rillig 281: }
282:
1.41 rillig 283: // TODO: Is this still necessary? This code is 20 years old and was introduced
1.46 rillig 284: // when pkglint loaded the package Makefile including all included files into
285: // a single string. Maybe it makes sense to print the file inclusion hierarchy
286: // to quickly see files that cannot be included because of unresolved variables.
1.38 rillig 287: if G.Opts.DumpMakefile {
1.54 rillig 288: G.Logger.out.WriteLine("Whole Makefile (with all included files) follows:")
1.38 rillig 289: for _, line := range allLines.lines.Lines {
1.54 rillig 290: G.Logger.out.WriteLine(line.String())
1.3 rillig 291: }
292: }
293:
1.41 rillig 294: // See mk/tools/cmake.mk
1.37 rillig 295: if pkg.vars.Defined("USE_CMAKE") {
1.55 rillig 296: allLines.Tools.def("cmake", "", false, AtRunTime, nil)
297: allLines.Tools.def("cpack", "", false, AtRunTime, nil)
1.37 rillig 298: }
299:
1.40 rillig 300: allLines.collectUsedVariables()
1.3 rillig 301:
1.45 rillig 302: pkg.Pkgdir = pkg.vars.LastValue("PKGDIR")
303: pkg.DistinfoFile = pkg.vars.LastValue("DISTINFO_FILE")
304: pkg.Filesdir = pkg.vars.LastValue("FILESDIR")
305: pkg.Patchdir = pkg.vars.LastValue("PATCHDIR")
1.3 rillig 306:
1.33 rillig 307: // See lang/php/ext.mk
1.49 rillig 308: if pkg.vars.DefinedSimilar("PHPEXT_MK") {
309: if !pkg.vars.DefinedSimilar("USE_PHP_EXT_PATCHES") {
1.3 rillig 310: pkg.Patchdir = "patches"
311: }
1.49 rillig 312: if pkg.vars.DefinedSimilar("PECL_VERSION") {
1.3 rillig 313: pkg.DistinfoFile = "distinfo"
1.33 rillig 314: } else {
315: pkg.IgnoreMissingPatches = true
1.3 rillig 316: }
1.33 rillig 317:
318: // For PHP modules that are not PECL, this combination means that
319: // the patches in the distinfo cannot be found in PATCHDIR.
1.3 rillig 320: }
321:
1.16 rillig 322: if trace.Tracing {
323: trace.Step1("DISTINFO_FILE=%s", pkg.DistinfoFile)
324: trace.Step1("FILESDIR=%s", pkg.Filesdir)
325: trace.Step1("PATCHDIR=%s", pkg.Patchdir)
326: trace.Step1("PKGDIR=%s", pkg.Pkgdir)
1.3 rillig 327: }
328:
1.47 rillig 329: return mainLines, allLines
1.3 rillig 330: }
331:
1.41 rillig 332: // TODO: What is allLines used for, is it still necessary? Would it be better as a field in Package?
333: func (pkg *Package) readMakefile(filename string, mainLines MkLines, allLines MkLines, includingFileForUsedCheck string) (exists bool, result bool) {
1.16 rillig 334: if trace.Tracing {
1.39 rillig 335: defer trace.Call1(filename)()
1.3 rillig 336: }
337:
1.41 rillig 338: fileMklines := LoadMk(filename, NotEmpty) // TODO: Document why omitting LogErrors is correct here.
1.34 rillig 339: if fileMklines == nil {
1.36 rillig 340: return false, false
1.3 rillig 341: }
1.47 rillig 342:
1.36 rillig 343: exists = true
1.47 rillig 344: result = true
1.3 rillig 345:
346: isMainMakefile := len(mainLines.mklines) == 0
1.1 rillig 347:
1.47 rillig 348: handleIncludeLine := func(mkline MkLine) YesNoUnknown {
349: includedFile, incDir, incBase := pkg.findIncludedFile(mkline, filename)
350:
351: if includedFile == "" {
352: return unknown
1.3 rillig 353: }
354:
1.47 rillig 355: dirname, _ := path.Split(filename)
356: dirname = cleanpath(dirname)
357: fullIncluded := dirname + "/" + includedFile
358: relIncludedFile := relpath(pkg.dir, fullIncluded)
359:
360: if !pkg.diveInto(filename, includedFile) {
361: return unknown
362: }
1.3 rillig 363:
1.47 rillig 364: if !pkg.included.FirstTime(relIncludedFile) {
365: return unknown
366: }
1.3 rillig 367:
1.47 rillig 368: pkg.collectUsedBy(mkline, incDir, incBase, includedFile)
369:
370: if trace.Tracing {
371: trace.Step1("Including %q.", fullIncluded)
372: }
373: fullIncluding := ifelseStr(incBase == "Makefile.common" && incDir != "", filename, "")
374: innerExists, innerResult := pkg.readMakefile(fullIncluded, mainLines, allLines, fullIncluding)
1.3 rillig 375:
1.47 rillig 376: if !innerExists {
377: if fileMklines.indentation.IsCheckedFile(includedFile) {
378: return yes // See https://github.com/rillig/pkglint/issues/1
379: }
1.3 rillig 380:
1.47 rillig 381: // Only look in the directory relative to the
382: // current file and in the package directory.
383: // Make(1) has a list of include directories, but pkgsrc
384: // doesn't make use of that, so pkglint also doesn't
385: // need this extra complexity.
386: pkgBasedir := pkg.File(".")
387: if dirname != pkgBasedir { // Prevent unnecessary syscalls
388: dirname = pkgBasedir
1.3 rillig 389:
1.47 rillig 390: fullIncludedFallback := dirname + "/" + includedFile
391: innerExists, innerResult = pkg.readMakefile(fullIncludedFallback, mainLines, allLines, fullIncluding)
392: }
1.29 rillig 393:
1.47 rillig 394: if !innerExists {
395: mkline.Errorf("Cannot read %q.", includedFile)
396: }
397: }
1.36 rillig 398:
1.47 rillig 399: if !innerResult {
400: result = false
401: return no
402: }
1.36 rillig 403:
1.47 rillig 404: return unknown
405: }
1.29 rillig 406:
1.47 rillig 407: lineAction := func(mkline MkLine) bool {
408: if isMainMakefile {
409: mainLines.mklines = append(mainLines.mklines, mkline)
410: mainLines.lines.Lines = append(mainLines.lines.Lines, mkline.Line)
411: }
412: allLines.mklines = append(allLines.mklines, mkline)
413: allLines.lines.Lines = append(allLines.lines.Lines, mkline.Line)
1.3 rillig 414:
1.47 rillig 415: if mkline.IsInclude() {
416: includeResult := handleIncludeLine(mkline)
417: if includeResult != unknown {
418: return includeResult == yes
1.3 rillig 419: }
420: }
421:
422: if mkline.IsVarassign() {
423: varname, op, value := mkline.Varname(), mkline.Op(), mkline.Value()
424:
1.36 rillig 425: if op != opAssignDefault || !pkg.vars.Defined(varname) {
1.16 rillig 426: if trace.Tracing {
427: trace.Stepf("varassign(%q, %q, %q)", varname, op, value)
1.3 rillig 428: }
1.36 rillig 429: pkg.vars.Define(varname, mkline)
1.3 rillig 430: }
431: }
1.32 rillig 432: return true
1.3 rillig 433: }
1.47 rillig 434:
1.32 rillig 435: atEnd := func(mkline MkLine) {}
1.35 rillig 436: fileMklines.ForEachEnd(lineAction, atEnd)
1.3 rillig 437:
1.41 rillig 438: if includingFileForUsedCheck != "" {
439: fileMklines.CheckForUsedComment(G.Pkgsrc.ToRel(includingFileForUsedCheck))
1.3 rillig 440: }
441:
1.39 rillig 442: // For every included buildlink3.mk, include the corresponding builtin.mk
443: // automatically since the pkgsrc infrastructure does the same.
1.44 rillig 444: if path.Base(filename) == "buildlink3.mk" {
1.47 rillig 445: builtin := cleanpath(path.Dir(filename) + "/builtin.mk")
446: builtinRel := relpath(pkg.dir, builtin)
447: if pkg.included.FirstTime(builtinRel) && fileExists(builtin) {
1.39 rillig 448: pkg.readMakefile(builtin, mainLines, allLines, "")
449: }
450: }
451:
1.36 rillig 452: return
1.3 rillig 453: }
454:
1.47 rillig 455: func (pkg *Package) diveInto(includingFile string, includedFile string) bool {
1.48 rillig 456:
457: // The variables that appear in these files are largely modeled by
458: // pkglint in the file vardefs.go. Therefore parsing these files again
459: // doesn't make much sense.
460: if hasSuffix(includedFile, "/bsd.pkg.mk") || IsPrefs(includedFile) {
461: return false
462: }
463:
464: // All files that are included from outside of the pkgsrc infrastructure
465: // are relevant. This is typically mk/compiler.mk or the various
466: // mk/*.buildlink3.mk files.
467: if !contains(includingFile, "/mk/") {
468: return true
469: }
470:
471: // The mk/*.buildlink3.mk files often come with a companion file called
472: // mk/*.builtin.mk, which also defines variables that are visible from
473: // the package.
474: //
475: // This case is needed for getting the redundancy check right. Without it
476: // there will be warnings about redundant assignments to the
477: // BUILTIN_CHECK.pthread variable.
478: if contains(includingFile, "buildlink3.mk") && contains(includedFile, "builtin.mk") {
479: return true
1.47 rillig 480: }
1.48 rillig 481:
482: return false
1.47 rillig 483: }
484:
1.41 rillig 485: func (pkg *Package) collectUsedBy(mkline MkLine, incDir string, incBase string, includedFile string) {
486: switch {
487: case
488: mkline.Basename != "Makefile",
489: hasPrefix(incDir, "../../mk/"),
490: incBase == "buildlink3.mk",
491: incBase == "builtin.mk",
492: incBase == "options.mk":
493: return
494: }
495:
496: if trace.Tracing {
497: trace.Step1("Including %q sets seenMakefileCommon.", includedFile)
498: }
499: pkg.seenMakefileCommon = true
500: }
501:
502: func (pkg *Package) findIncludedFile(mkline MkLine, includingFilename string) (includedFile, incDir, incBase string) {
503:
1.47 rillig 504: // TODO: resolveVariableRefs uses G.Pkg implicitly. It should be made explicit.
505: // TODO: Try to combine resolveVariableRefs and ResolveVarsInRelativePath.
1.49 rillig 506: includedFile = resolveVariableRefs(nil, mkline.ResolveVarsInRelativePath(mkline.IncludedFile()))
1.47 rillig 507: if containsVarRef(includedFile) {
508: if trace.Tracing && !contains(includingFilename, "/mk/") {
509: trace.Stepf("%s:%s: Skipping include file %q. This may result in false warnings.",
510: mkline.Filename, mkline.Linenos(), includedFile)
1.41 rillig 511: }
1.47 rillig 512: includedFile = ""
1.41 rillig 513: }
1.47 rillig 514: incDir, incBase = path.Split(includedFile)
1.41 rillig 515:
516: if includedFile != "" {
517: if mkline.Basename != "buildlink3.mk" {
1.44 rillig 518: if matches(includedFile, `^\.\./\.\./(.*)/buildlink3\.mk$`) {
519: pkg.bl3[includedFile] = mkline
1.41 rillig 520: if trace.Tracing {
1.44 rillig 521: trace.Step1("Buildlink3 file in package: %q", includedFile)
1.41 rillig 522: }
523: }
524: }
525: }
526:
527: return
528: }
529:
1.47 rillig 530: func (pkg *Package) checkfilePackageMakefile(filename string, mklines MkLines, allLines MkLines) {
1.16 rillig 531: if trace.Tracing {
1.39 rillig 532: defer trace.Call1(filename)()
1.3 rillig 533: }
534:
1.26 rillig 535: vars := pkg.vars
536: if !vars.Defined("PLIST_SRC") &&
537: !vars.Defined("GENERATE_PLIST") &&
538: !vars.Defined("META_PACKAGE") &&
1.33 rillig 539: !fileExists(pkg.File(pkg.Pkgdir+"/PLIST")) &&
540: !fileExists(pkg.File(pkg.Pkgdir+"/PLIST.common")) {
1.41 rillig 541: // TODO: Move these technical details into the explanation, making space for an understandable warning.
1.39 rillig 542: NewLineWhole(filename).Warnf("Neither PLIST nor PLIST.common exist, and PLIST_SRC is unset.")
1.1 rillig 543: }
544:
1.51 rillig 545: if (vars.Defined("NO_CHECKSUM") || vars.Defined("META_PACKAGE")) &&
546: isEmptyDir(pkg.File(pkg.Patchdir)) {
1.41 rillig 547:
1.51 rillig 548: distinfoFile := pkg.File(pkg.DistinfoFile)
549: if fileExists(distinfoFile) {
550: NewLineWhole(distinfoFile).Warnf("This file should not exist since NO_CHECKSUM or META_PACKAGE is set.")
1.1 rillig 551: }
552: } else {
1.51 rillig 553: distinfoFile := pkg.File(pkg.DistinfoFile)
554: if !containsVarRef(distinfoFile) && !fileExists(distinfoFile) {
555: line := NewLineWhole(distinfoFile)
556: line.Warnf("A package that downloads files should have a distinfo file.")
557: line.Explain(
558: sprintf("To generate the distinfo file, run %q.", bmake("makesum")),
559: "",
560: "To mark the package as not needing a distinfo file, set",
561: "NO_CHECKSUM=yes in the package Makefile.")
1.1 rillig 562: }
563: }
564:
1.41 rillig 565: // TODO: There are other REPLACE_* variables which are probably also affected by NO_CONFIGURE.
566: if noConfigureLine := vars.FirstDefinition("NO_CONFIGURE"); noConfigureLine != nil {
567: if replacePerlLine := vars.FirstDefinition("REPLACE_PERL"); replacePerlLine != nil {
568: replacePerlLine.Warnf("REPLACE_PERL is ignored when NO_CONFIGURE is set (in %s).",
569: replacePerlLine.RefTo(noConfigureLine))
570: }
1.1 rillig 571: }
572:
1.26 rillig 573: if !vars.Defined("LICENSE") && !vars.Defined("META_PACKAGE") && pkg.once.FirstTime("LICENSE") {
1.47 rillig 574: line := NewLineWhole(filename)
575: line.Errorf("Each package must define its LICENSE.")
1.41 rillig 576: // TODO: Explain why the LICENSE is necessary.
1.47 rillig 577: line.Explain(
578: "To take a good guess on the license of a package,",
579: sprintf("run %q.", bmake("guess-license")))
1.1 rillig 580: }
581:
1.47 rillig 582: scope := NewRedundantScope()
583: scope.Check(allLines) // Updates the variables in the scope
584: pkg.checkGnuConfigureUseLanguages(scope)
1.50 rillig 585: pkg.checkUseLanguagesCompilerMk(allLines)
1.47 rillig 586:
1.3 rillig 587: pkg.determineEffectivePkgVars()
588: pkg.checkPossibleDowngrade()
589:
1.26 rillig 590: if !vars.Defined("COMMENT") {
1.41 rillig 591: NewLineWhole(filename).Warnf("Each package should define a COMMENT.")
1.3 rillig 592: }
593:
1.41 rillig 594: if imake := vars.FirstDefinition("USE_IMAKE"); imake != nil {
595: if x11 := vars.FirstDefinition("USE_X11"); x11 != nil {
596: if !hasSuffix(x11.Filename, "/mk/x11.buildlink3.mk") {
597: imake.Notef("USE_IMAKE makes USE_X11 in %s redundant.", imake.RefTo(x11))
598: }
1.7 rillig 599: }
1.3 rillig 600: }
601:
602: pkg.checkUpdate()
1.55 rillig 603: allLines.collectDefinedVariables() // To get the tool definitions
604: mklines.Tools = allLines.Tools // TODO: also copy the other collected data
1.3 rillig 605: mklines.Check()
1.25 rillig 606: pkg.CheckVarorder(mklines)
1.3 rillig 607: SaveAutofixChanges(mklines.lines)
608: }
609:
1.47 rillig 610: func (pkg *Package) checkGnuConfigureUseLanguages(s *RedundantScope) {
611:
612: gnuConfigure := s.vars["GNU_CONFIGURE"]
613: if gnuConfigure == nil || !gnuConfigure.vari.Constant() {
614: return
615: }
616:
617: useLanguages := s.vars["USE_LANGUAGES"]
618: if useLanguages == nil || !useLanguages.vari.Constant() {
619: return
620: }
621:
622: var wrongLines []MkLine
623: for _, mkline := range useLanguages.vari.WriteLocations() {
624:
625: if G.Pkgsrc.IsInfra(mkline.Line.Filename) {
626: continue
627: }
1.36 rillig 628:
1.47 rillig 629: if matches(mkline.VarassignComment(), `(?-i)\b(?:c|empty|none)\b`) {
630: // Don't emit a warning since the comment probably contains a
631: // statement that C is really not needed.
632: return
633: }
1.45 rillig 634:
1.47 rillig 635: languages := mkline.Value()
636: if matches(languages, `(?:^|[\t ]+)(?:c|c99|objc)(?:[\t ]+|$)`) {
637: return
1.36 rillig 638: }
1.47 rillig 639:
640: wrongLines = append(wrongLines, mkline)
641: }
642:
643: gnuLine := gnuConfigure.vari.WriteLocations()[0]
644: for _, useLine := range wrongLines {
645: gnuLine.Warnf(
646: "GNU_CONFIGURE almost always needs a C compiler, "+
647: "but \"c\" is not added to USE_LANGUAGES in %s.",
648: gnuLine.RefTo(useLine))
1.36 rillig 649: }
650: }
651:
1.41 rillig 652: // nbPart determines the smallest part of the package version number,
653: // typically "nb13" or an empty string.
654: //
655: // It is only used inside pkgsrc to mark changes that are
656: // independent from the upstream package.
657: func (pkg *Package) nbPart() string {
1.45 rillig 658: pkgrevision := pkg.vars.LastValue("PKGREVISION")
1.3 rillig 659: if rev, err := strconv.Atoi(pkgrevision); err == nil {
660: return "nb" + strconv.Itoa(rev)
661: }
662: return ""
663: }
664:
665: func (pkg *Package) determineEffectivePkgVars() {
1.26 rillig 666: distnameLine := pkg.vars.FirstDefinition("DISTNAME")
667: pkgnameLine := pkg.vars.FirstDefinition("PKGNAME")
1.1 rillig 668:
669: distname := ""
670: if distnameLine != nil {
1.3 rillig 671: distname = distnameLine.Value()
1.1 rillig 672: }
1.48 rillig 673:
1.1 rillig 674: pkgname := ""
675: if pkgnameLine != nil {
1.3 rillig 676: pkgname = pkgnameLine.Value()
1.1 rillig 677: }
678:
1.48 rillig 679: effname := pkgname
680: if distname != "" && effname != "" {
681: merged, ok := pkg.pkgnameFromDistname(effname, distname)
682: if ok {
683: effname = merged
684: }
1.1 rillig 685: }
686:
1.48 rillig 687: if pkgname != "" && (pkgname == distname || pkgname == "${DISTNAME}") {
688: if pkgnameLine.VarassignComment() == "" {
689: pkgnameLine.Notef("This assignment is probably redundant " +
690: "since PKGNAME is ${DISTNAME} by default.")
691: pkgnameLine.Explain(
692: "To mark this assignment as necessary, add a comment to the end of this line.")
693: }
1.1 rillig 694: }
695:
696: if pkgname == "" && distname != "" && !containsVarRef(distname) && !matches(distname, rePkgname) {
1.14 rillig 697: distnameLine.Warnf("As DISTNAME is not a valid package name, please define the PKGNAME explicitly.")
1.1 rillig 698: }
699:
1.48 rillig 700: if pkgname != "" {
701: distname = ""
702: }
703:
704: if effname != "" && !containsVarRef(effname) {
705: if m, m1, m2 := match2(effname, rePkgname); m {
706: pkg.EffectivePkgname = effname + pkg.nbPart()
1.3 rillig 707: pkg.EffectivePkgnameLine = pkgnameLine
708: pkg.EffectivePkgbase = m1
709: pkg.EffectivePkgversion = m2
710: }
711: }
1.41 rillig 712:
1.3 rillig 713: if pkg.EffectivePkgnameLine == nil && distname != "" && !containsVarRef(distname) {
714: if m, m1, m2 := match2(distname, rePkgname); m {
1.41 rillig 715: pkg.EffectivePkgname = distname + pkg.nbPart()
1.3 rillig 716: pkg.EffectivePkgnameLine = distnameLine
717: pkg.EffectivePkgbase = m1
718: pkg.EffectivePkgversion = m2
719: }
720: }
1.41 rillig 721:
1.3 rillig 722: if pkg.EffectivePkgnameLine != nil {
1.16 rillig 723: if trace.Tracing {
724: trace.Stepf("Effective name=%q base=%q version=%q",
1.3 rillig 725: pkg.EffectivePkgname, pkg.EffectivePkgbase, pkg.EffectivePkgversion)
726: }
1.1 rillig 727: }
1.3 rillig 728: }
1.1 rillig 729:
1.48 rillig 730: func (pkg *Package) pkgnameFromDistname(pkgname, distname string) (string, bool) {
1.39 rillig 731: tokens := NewMkParser(nil, pkgname, false).MkTokens()
1.1 rillig 732:
1.41 rillig 733: // TODO: Make this resolving of variable references available to all other variables as well.
734:
1.48 rillig 735: var result strings.Builder
1.9 rillig 736: for _, token := range tokens {
1.48 rillig 737: if token.Varuse != nil {
738: if token.Varuse.varname != "DISTNAME" {
739: return "", false
740: }
741:
1.9 rillig 742: newDistname := distname
743: for _, mod := range token.Varuse.modifiers {
1.38 rillig 744: if mod.IsToLower() {
1.9 rillig 745: newDistname = strings.ToLower(newDistname)
1.48 rillig 746: } else if subst, ok := mod.Subst(newDistname); ok {
747: newDistname = subst
1.9 rillig 748: } else {
1.48 rillig 749: return "", false
1.9 rillig 750: }
751: }
1.48 rillig 752: result.WriteString(newDistname)
1.9 rillig 753: } else {
1.48 rillig 754: result.WriteString(token.Text)
1.9 rillig 755: }
756: }
1.48 rillig 757: return result.String(), true
1.3 rillig 758: }
1.1 rillig 759:
1.3 rillig 760: func (pkg *Package) checkUpdate() {
1.41 rillig 761: if pkg.EffectivePkgbase == "" {
762: return
763: }
764:
1.43 rillig 765: for _, sugg := range G.Pkgsrc.SuggestedUpdates() {
1.41 rillig 766: if pkg.EffectivePkgbase != sugg.Pkgname {
767: continue
768: }
769:
770: suggver, comment := sugg.Version, sugg.Comment
771: if comment != "" {
772: comment = " (" + comment + ")"
773: }
774:
775: pkgnameLine := pkg.EffectivePkgnameLine
776: cmp := pkgver.Compare(pkg.EffectivePkgversion, suggver)
777: switch {
1.1 rillig 778:
1.41 rillig 779: case cmp < 0:
780: pkgnameLine.Warnf("This package should be updated to %s%s.",
781: sugg.Version, comment)
1.50 rillig 782: pkgnameLine.Explain(
1.41 rillig 783: "The wishlist for package updates in doc/TODO mentions that a newer",
784: "version of this package is available.")
1.1 rillig 785:
1.41 rillig 786: case cmp > 0:
787: pkgnameLine.Notef("This package is newer than the update request to %s%s.",
788: suggver, comment)
789:
790: default:
791: pkgnameLine.Notef("The update request to %s from doc/TODO%s has been done.",
792: suggver, comment)
1.1 rillig 793: }
794: }
795: }
796:
1.25 rillig 797: // CheckVarorder checks that in simple package Makefiles,
798: // the most common variables appear in a fixed order.
799: // The order itself is a little arbitrary but provides
800: // at least a bit of consistency.
1.38 rillig 801: func (pkg *Package) CheckVarorder(mklines MkLines) {
1.16 rillig 802: if trace.Tracing {
803: defer trace.Call0()()
1.1 rillig 804: }
805:
1.45 rillig 806: if pkg.seenMakefileCommon {
1.1 rillig 807: return
808: }
809:
1.56 ! rillig 810: // TODO: Extract all this code into a separate VarOrderChecker
! 811: // since it is equally useful for PKG_OPTIONS in options.mk.
! 812:
1.25 rillig 813: type Repetition uint8
1.1 rillig 814: const (
1.25 rillig 815: optional Repetition = iota
816: once
1.1 rillig 817: many
818: )
1.41 rillig 819:
1.25 rillig 820: type Variable struct {
821: varname string
822: repetition Repetition
823: }
1.41 rillig 824:
1.25 rillig 825: type Section struct {
826: repetition Repetition
827: vars []Variable
828: }
1.41 rillig 829:
1.25 rillig 830: variable := func(name string, repetition Repetition) Variable { return Variable{name, repetition} }
831: section := func(repetition Repetition, vars ...Variable) Section { return Section{repetition, vars} }
832:
1.41 rillig 833: // See doc/Makefile-example.
834: // See https://netbsd.org/docs/pkgsrc/pkgsrc.html#components.Makefile.
1.25 rillig 835: var sections = []Section{
836: section(once,
837: variable("GITHUB_PROJECT", optional), // either here or below MASTER_SITES
838: variable("GITHUB_TAG", optional),
839: variable("DISTNAME", optional),
840: variable("PKGNAME", optional),
841: variable("PKGREVISION", optional),
842: variable("CATEGORIES", once),
843: variable("MASTER_SITES", many),
844: variable("GITHUB_PROJECT", optional), // either here or at the very top
845: variable("GITHUB_TAG", optional),
846: variable("DIST_SUBDIR", optional),
847: variable("EXTRACT_SUFX", optional),
848: variable("DISTFILES", many),
849: variable("SITES.*", many)),
850: section(optional,
851: variable("PATCH_SITES", optional), // or once?
852: variable("PATCH_SITE_SUBDIR", optional),
853: variable("PATCHFILES", optional), // or once?
854: variable("PATCH_DIST_ARGS", optional),
855: variable("PATCH_DIST_STRIP", optional),
856: variable("PATCH_DIST_CAT", optional)),
857: section(once,
858: variable("MAINTAINER", optional),
859: variable("OWNER", optional),
860: variable("HOMEPAGE", optional),
861: variable("COMMENT", once),
862: variable("LICENSE", once)),
863: section(optional,
864: variable("LICENSE_FILE", optional),
865: variable("RESTRICTED", optional),
866: variable("NO_BIN_ON_CDROM", optional),
867: variable("NO_BIN_ON_FTP", optional),
868: variable("NO_SRC_ON_CDROM", optional),
869: variable("NO_SRC_ON_FTP", optional)),
870: section(optional,
871: variable("BROKEN_EXCEPT_ON_PLATFORM", many),
872: variable("BROKEN_ON_PLATFORM", many),
873: variable("NOT_FOR_PLATFORM", many),
874: variable("ONLY_FOR_PLATFORM", many),
875: variable("NOT_FOR_COMPILER", many),
876: variable("ONLY_FOR_COMPILER", many),
877: variable("NOT_FOR_UNPRIVILEGED", optional),
878: variable("ONLY_FOR_UNPRIVILEGED", optional)),
879: section(optional,
880: variable("BUILD_DEPENDS", many),
881: variable("TOOL_DEPENDS", many),
1.41 rillig 882: variable("DEPENDS", many))}
1.25 rillig 883:
1.45 rillig 884: relevantLines := (func() []MkLine {
885: firstRelevant := -1
886: lastRelevant := -1
1.41 rillig 887:
1.25 rillig 888: relevantVars := make(map[string]bool)
889: for _, section := range sections {
890: for _, variable := range section.vars {
891: relevantVars[variable.varname] = true
892: }
893: }
1.1 rillig 894:
1.25 rillig 895: firstIrrelevant := -1
896: for i, mkline := range mklines.mklines {
897: switch {
898: case mkline.IsVarassign(), mkline.IsCommentedVarassign():
899: varcanon := mkline.Varcanon()
900: if relevantVars[varcanon] {
901: if firstRelevant == -1 {
902: firstRelevant = i
903: }
904: if firstIrrelevant != -1 {
905: if trace.Tracing {
906: trace.Stepf("Skipping varorder because of line %s.",
907: mklines.mklines[firstIrrelevant].Linenos())
908: }
1.45 rillig 909: return nil
1.25 rillig 910: }
911: lastRelevant = i
912: } else {
913: if firstIrrelevant == -1 {
914: firstIrrelevant = i
915: }
916: }
1.1 rillig 917:
1.25 rillig 918: case mkline.IsComment(), mkline.IsEmpty():
1.1 rillig 919: break
1.25 rillig 920:
921: default:
922: if firstIrrelevant == -1 {
923: firstIrrelevant = i
924: }
1.1 rillig 925: }
926: }
927:
1.28 rillig 928: if firstRelevant == -1 {
1.45 rillig 929: return nil
1.28 rillig 930: }
1.45 rillig 931: return mklines.mklines[firstRelevant : lastRelevant+1]
932: })()
933:
934: skip := func() bool {
935: interesting := relevantLines
1.1 rillig 936:
1.25 rillig 937: varcanon := func() string {
1.38 rillig 938: for len(interesting) > 0 && interesting[0].IsComment() {
1.25 rillig 939: interesting = interesting[1:]
940: }
1.38 rillig 941: if len(interesting) > 0 && (interesting[0].IsVarassign() || interesting[0].IsCommentedVarassign()) {
1.25 rillig 942: return interesting[0].Varcanon()
943: }
944: return ""
945: }
1.1 rillig 946:
1.25 rillig 947: for _, section := range sections {
948: for _, variable := range section.vars {
949: switch variable.repetition {
950: case optional:
951: if varcanon() == variable.varname {
952: interesting = interesting[1:]
953: }
954: case once:
955: if varcanon() == variable.varname {
956: interesting = interesting[1:]
957: } else if section.repetition == once {
958: if variable.varname != "LICENSE" {
959: if trace.Tracing {
960: trace.Stepf("Wrong varorder because %s is missing.", variable.varname)
961: }
962: return false
963: }
964: }
965: case many:
966: for varcanon() == variable.varname {
967: interesting = interesting[1:]
968: }
1.1 rillig 969: }
970: }
971:
1.38 rillig 972: for len(interesting) > 0 && (interesting[0].IsEmpty() || interesting[0].IsComment()) {
1.25 rillig 973: interesting = interesting[1:]
1.1 rillig 974: }
1.25 rillig 975: }
1.1 rillig 976:
1.25 rillig 977: return len(interesting) == 0
978: }
1.1 rillig 979:
1.45 rillig 980: if len(relevantLines) == 0 || skip() {
1.25 rillig 981: return
982: }
1.1 rillig 983:
1.25 rillig 984: var canonical []string
985: for _, section := range sections {
986: for _, variable := range section.vars {
987: found := false
1.45 rillig 988: for _, mkline := range relevantLines {
1.25 rillig 989: if mkline.IsVarassign() || mkline.IsCommentedVarassign() {
990: if mkline.Varcanon() == variable.varname {
991: canonical = append(canonical, mkline.Varname())
992: found = true
1.21 rillig 993: }
1.1 rillig 994: }
995: }
1.25 rillig 996: if !found && section.repetition == once && variable.repetition == once {
997: canonical = append(canonical, variable.varname)
1.1 rillig 998: }
999: }
1.38 rillig 1000: if len(canonical) > 0 && canonical[len(canonical)-1] != "empty line" {
1.25 rillig 1001: canonical = append(canonical, "empty line")
1002: }
1003: }
1.38 rillig 1004: if len(canonical) > 0 && canonical[len(canonical)-1] == "empty line" {
1.25 rillig 1005: canonical = canonical[:len(canonical)-1]
1.1 rillig 1006: }
1.25 rillig 1007:
1.41 rillig 1008: // TODO: This leads to very long and complicated warnings.
1009: // Those parts that are correct should not be mentioned,
1010: // except if they are helpful for locating the mistakes.
1.45 rillig 1011: mkline := relevantLines[0]
1.25 rillig 1012: mkline.Warnf("The canonical order of the variables is %s.", strings.Join(canonical, ", "))
1.50 rillig 1013: mkline.Explain(
1.25 rillig 1014: "In simple package Makefiles, some common variables should be",
1015: "arranged in a specific order.",
1016: "",
1.39 rillig 1017: "See doc/Makefile-example for an example Makefile.",
1018: seeGuide("Package components, Makefile", "components.Makefile"))
1.1 rillig 1019: }
1.3 rillig 1020:
1.53 rillig 1021: func (pkg *Package) checkFileMakefileExt(filename string) {
1022: base := path.Base(filename)
1023: if !hasPrefix(base, "Makefile.") || base == "Makefile.common" {
1024: return
1025: }
1026: ext := strings.TrimPrefix(base, "Makefile.")
1027:
1028: line := NewLineWhole(filename)
1029: line.Notef("Consider renaming %q to %q.", base, ext+".mk")
1030: line.Explain(
1031: "The main definition of a pkgsrc package should be in the Makefile.",
1032: "Common definitions for a few very closely related packages can be",
1033: "placed in a Makefile.common, these may cover various topics.",
1034: "",
1035: "All other definitions should be grouped by topics and implemented",
1036: "in separate files named *.mk after their topics. Typical examples",
1037: "are extension.mk, module.mk, version.mk.",
1038: "",
1039: "These topic files should be documented properly so that their",
1040: sprintf("content can be queried using %q.", makeHelp("help")))
1041: }
1042:
1.41 rillig 1043: // checkLocallyModified checks files that are about to be committed.
1044: // Depending on whether the package has a MAINTAINER or an OWNER,
1045: // the wording differs.
1046: //
1047: // Pkglint assumes that the local username is the same as the NetBSD
1048: // username, which fits most scenarios.
1.39 rillig 1049: func (pkg *Package) checkLocallyModified(filename string) {
1.16 rillig 1050: if trace.Tracing {
1.39 rillig 1051: defer trace.Call(filename)()
1.9 rillig 1052: }
1053:
1.45 rillig 1054: owner := pkg.vars.LastValue("OWNER")
1055: maintainer := pkg.vars.LastValue("MAINTAINER")
1.36 rillig 1056: if maintainer == "pkgsrc-users@NetBSD.org" {
1.35 rillig 1057: maintainer = ""
1.9 rillig 1058: }
1059: if owner == "" && maintainer == "" {
1060: return
1061: }
1062:
1.41 rillig 1063: username := G.Username
1.16 rillig 1064: if trace.Tracing {
1065: trace.Stepf("user=%q owner=%q maintainer=%q", username, owner, maintainer)
1.9 rillig 1066: }
1067:
1068: if username == strings.Split(owner, "@")[0] || username == strings.Split(maintainer, "@")[0] {
1069: return
1070: }
1071:
1.50 rillig 1072: if !isLocallyModified(filename) || !fileExists(filename) {
1.41 rillig 1073: return
1074: }
1075:
1076: if owner != "" {
1.50 rillig 1077: line := NewLineWhole(filename)
1078: line.Warnf("Don't commit changes to this file without asking the OWNER, %s.", owner)
1079: line.Explain(
1.41 rillig 1080: seeGuide("Package components, Makefile", "components.Makefile"))
1081: }
1082:
1083: if maintainer != "" {
1.50 rillig 1084: line := NewLineWhole(filename)
1085: line.Notef("Please only commit changes that %s would approve.", maintainer)
1086: line.Explain(
1.41 rillig 1087: "See the pkgsrc guide, section \"Package components\",",
1088: "keyword \"maintainer\", for more information.")
1.9 rillig 1089: }
1090: }
1.12 rillig 1091:
1.40 rillig 1092: func (pkg *Package) checkIncludeConditionally(mkline MkLine, indentation *Indentation) {
1.34 rillig 1093: conditionalVars := mkline.ConditionalVars()
1.39 rillig 1094: if len(conditionalVars) == 0 {
1.34 rillig 1095: conditionalVars = indentation.Varnames()
1096: mkline.SetConditionalVars(conditionalVars)
1.12 rillig 1097: }
1098:
1.39 rillig 1099: if path.Dir(abspath(mkline.Filename)) == abspath(pkg.File(".")) {
1100: includedFile := mkline.IncludedFile()
1.12 rillig 1101:
1102: if indentation.IsConditional() {
1.39 rillig 1103: pkg.conditionalIncludes[includedFile] = mkline
1104: if other := pkg.unconditionalIncludes[includedFile]; other != nil {
1.41 rillig 1105: mkline.Warnf(
1106: "%q is included conditionally here (depending on %s) "+
1107: "and unconditionally in %s.",
1.39 rillig 1108: cleanpath(includedFile), strings.Join(mkline.ConditionalVars(), ", "), mkline.RefTo(other))
1.12 rillig 1109: }
1.41 rillig 1110:
1.12 rillig 1111: } else {
1.39 rillig 1112: pkg.unconditionalIncludes[includedFile] = mkline
1113: if other := pkg.conditionalIncludes[includedFile]; other != nil {
1.41 rillig 1114: mkline.Warnf(
1115: "%q is included unconditionally here "+
1116: "and conditionally in %s (depending on %s).",
1.39 rillig 1117: cleanpath(includedFile), mkline.RefTo(other), strings.Join(other.ConditionalVars(), ", "))
1.12 rillig 1118: }
1119: }
1.39 rillig 1120:
1121: // TODO: Check whether the conditional variables are the same on both places.
1.46 rillig 1122: // Ideally they should match, but there may be some differences in internal
1123: // variables, which need to be filtered out before comparing them, like it is
1124: // already done with *_MK variables.
1.12 rillig 1125: }
1126: }
1.22 rillig 1127:
1128: func (pkg *Package) loadPlistDirs(plistFilename string) {
1.34 rillig 1129: lines := Load(plistFilename, MustSucceed)
1.38 rillig 1130: for _, line := range lines.Lines {
1.22 rillig 1131: text := line.Text
1.38 rillig 1132: pkg.Plist.Files[text] = true // XXX: ignores PLIST conditions for now
1.22 rillig 1133: // Keep in sync with PlistChecker.collectFilesAndDirs
1134: if !contains(text, "$") && !contains(text, "@") {
1135: for dir := path.Dir(text); dir != "."; dir = path.Dir(dir) {
1.38 rillig 1136: pkg.Plist.Dirs[dir] = true
1.22 rillig 1137: }
1138: }
1139: }
1140: }
1.38 rillig 1141:
1.45 rillig 1142: func (pkg *Package) AutofixDistinfo(oldSha1, newSha1 string) {
1143: distinfoFilename := pkg.File(pkg.DistinfoFile)
1144: if lines := Load(distinfoFilename, NotEmpty|LogErrors); lines != nil {
1145: for _, line := range lines.Lines {
1146: fix := line.Autofix()
1147: fix.Warnf(SilentAutofixFormat)
1148: fix.Replace(oldSha1, newSha1)
1149: fix.Apply()
1150: }
1151: lines.SaveAutofixChanges()
1152: }
1153: }
1154:
1.50 rillig 1155: // checkUseLanguagesCompilerMk checks that after including mk/compiler.mk
1156: // or mk/endian.mk for the first time, there are no more changes to
1157: // USE_LANGUAGES, as these would be ignored by the pkgsrc infrastructure.
1158: func (pkg *Package) checkUseLanguagesCompilerMk(mklines MkLines) {
1159:
1160: var seen Once
1161:
1162: handleVarassign := func(mkline MkLine) {
1163: if mkline.Varname() != "USE_LANGUAGES" {
1164: return
1165: }
1166:
1167: if !seen.Seen("../../mk/compiler.mk") && !seen.Seen("../../mk/endian.mk") {
1168: return
1169: }
1170:
1171: if mkline.Basename == "compiler.mk" {
1172: if relpath(pkg.dir, mkline.Filename) == "../../mk/compiler.mk" {
1173: return
1174: }
1175: }
1176:
1177: mkline.Warnf("Modifying USE_LANGUAGES after including ../../mk/compiler.mk has no effect.")
1178: mkline.Explain(
1179: "The file compiler.mk guards itself against multiple inclusion.")
1180: }
1181:
1182: handleInclude := func(mkline MkLine) {
1183: dirname, _ := path.Split(mkline.Filename)
1184: dirname = cleanpath(dirname)
1185: fullIncluded := dirname + "/" + mkline.IncludedFile()
1186: relIncludedFile := relpath(pkg.dir, fullIncluded)
1187:
1188: seen.FirstTime(relIncludedFile)
1189: }
1190:
1191: mklines.ForEach(func(mkline MkLine) {
1192: switch {
1193: case mkline.IsVarassign():
1194: handleVarassign(mkline)
1195:
1196: case mkline.IsInclude():
1197: handleInclude(mkline)
1198: }
1199: })
1200: }
1201:
1.38 rillig 1202: type PlistContent struct {
1203: Dirs map[string]bool
1204: Files map[string]bool
1205: }
1206:
1207: func NewPlistContent() PlistContent {
1208: return PlistContent{
1209: make(map[string]bool),
1210: make(map[string]bool)}
1211: }
CVSweb <webmaster@jp.NetBSD.org>