Annotation of pkgsrc/pkgtools/pkglint/files/buildlink3.go, Revision 1.35
1.16 rillig 1: package pkglint
1.1 rillig 2:
3: import (
1.8 rillig 4: "netbsd.org/pkglint/pkgver"
1.1 rillig 5: "strings"
6: )
7:
1.14 rillig 8: type Buildlink3Checker struct {
1.23 rillig 9: mklines *MkLines
1.14 rillig 10: pkgbase string
1.23 rillig 11: pkgbaseLine *MkLine
12: abiLine, apiLine *MkLine
1.14 rillig 13: abi, api *DependencyPattern
14: }
15:
1.34 rillig 16: func CheckLinesBuildlink3Mk(mklines *MkLines) {
17: (&Buildlink3Checker{mklines: mklines}).Check()
1.14 rillig 18: }
19:
20: func (ck *Buildlink3Checker) Check() {
21: mklines := ck.mklines
1.9 rillig 22: if trace.Tracing {
1.26 rillig 23: defer trace.Call(mklines.lines.Filename)()
1.2 rillig 24: }
1.1 rillig 25:
1.2 rillig 26: mklines.Check()
1.1 rillig 27:
1.19 rillig 28: llex := NewMkLinesLexer(mklines)
1.1 rillig 29:
1.23 rillig 30: for llex.SkipIf((*MkLine).IsComment) {
1.19 rillig 31: line := llex.PreviousLine()
1.2 rillig 32: // See pkgtools/createbuildlink/files/createbuildlink
1.11 rillig 33: if hasPrefix(line.Text, "# XXX This file was created automatically") {
1.7 rillig 34: line.Errorf("This comment indicates unfinished work (url2pkg).")
1.1 rillig 35: }
36: }
37:
1.19 rillig 38: llex.SkipEmptyOrNote()
1.1 rillig 39:
1.19 rillig 40: if llex.SkipRegexp(`^BUILDLINK_DEPMETHOD\.([^\t ]+)\?=.*$`) {
41: llex.PreviousLine().Warnf("This line belongs inside the .ifdef block.")
1.26 rillig 42: for llex.SkipText("") {
1.1 rillig 43: }
44: }
45:
1.19 rillig 46: if !ck.checkFirstParagraph(llex) {
1.14 rillig 47: return
48: }
1.19 rillig 49: if !ck.checkSecondParagraph(llex) {
1.14 rillig 50: return
51: }
1.19 rillig 52: if !ck.checkMainPart(llex) {
1.14 rillig 53: return
54: }
55:
56: // Fourth paragraph: Cleanup, corresponding to the first paragraph.
1.19 rillig 57: if !llex.SkipContainsOrWarn("BUILDLINK_TREE+=\t-" + ck.pkgbase) {
1.14 rillig 58: return
59: }
60:
1.19 rillig 61: if !llex.EOF() {
62: llex.CurrentLine().Warnf("The file should end here.")
1.14 rillig 63: }
64:
1.30 rillig 65: pkg := ck.mklines.pkg
66: if pkg != nil {
67: pkg.checkLinesBuildlink3Inclusion(mklines)
1.14 rillig 68: }
69:
70: mklines.SaveAutofixChanges()
71: }
72:
1.19 rillig 73: func (ck *Buildlink3Checker) checkFirstParagraph(mlex *MkLinesLexer) bool {
1.1 rillig 74:
75: // First paragraph: Introduction of the package identifier
1.19 rillig 76: m := mlex.NextRegexp(`^BUILDLINK_TREE\+=[\t ]*([^\t ]+)$`)
77: if m == nil {
78: mlex.CurrentLine().Warnf("Expected a BUILDLINK_TREE line.")
1.14 rillig 79: return false
1.1 rillig 80: }
1.14 rillig 81:
1.19 rillig 82: pkgbase := m[1]
83: pkgbaseLine := mlex.PreviousMkLine()
1.14 rillig 84:
1.30 rillig 85: if containsVarUse(pkgbase) {
86: ck.checkVaruseInPkgbase(pkgbaseLine)
1.2 rillig 87: }
1.22 rillig 88:
89: ck.checkUniquePkgbase(pkgbase, pkgbaseLine)
90:
1.19 rillig 91: mlex.SkipEmptyOrNote()
1.14 rillig 92: ck.pkgbase = pkgbase
1.34 rillig 93: if pkg := ck.mklines.pkg; pkg != nil {
94: pkg.buildlinkID = ck.pkgbase
95: }
1.14 rillig 96: ck.pkgbaseLine = pkgbaseLine
97: return true
98: }
1.2 rillig 99:
1.23 rillig 100: func (ck *Buildlink3Checker) checkUniquePkgbase(pkgbase string, mkline *MkLine) {
1.22 rillig 101: prev := G.InterPackage.Bl3(pkgbase, &mkline.Location)
102: if prev == nil {
103: return
104: }
105:
1.32 rillig 106: dirname := G.Pkgsrc.Rel(mkline.Filename().Dir()).Base()
1.27 rillig 107: base, name := trimCommon(pkgbase, dirname)
1.22 rillig 108: if base == "" && matches(name, `^(\d*|-cvs|-fossil|-git|-hg|-svn|-devel|-snapshot)$`) {
109: return
110: }
111:
112: mkline.Errorf("Duplicate package identifier %q already appeared in %s.",
1.28 rillig 113: pkgbase, mkline.RelLocation(*prev))
1.22 rillig 114: mkline.Explain(
115: "Each buildlink3.mk file must have a unique identifier.",
116: "These identifiers are used for multiple-inclusion guards,",
117: "and using the same identifier for different packages",
118: "(often by copy-and-paste) may change the dependencies",
119: "of a package in subtle and unexpected ways.")
120: }
121:
1.14 rillig 122: // checkSecondParagraph checks the multiple inclusion protection and
123: // introduces the uppercase package identifier.
1.19 rillig 124: func (ck *Buildlink3Checker) checkSecondParagraph(mlex *MkLinesLexer) bool {
1.14 rillig 125: pkgbase := ck.pkgbase
1.19 rillig 126: m := mlex.NextRegexp(`^\.if !defined\(([^\t ]+)_BUILDLINK3_MK\)$`)
127: if m == nil {
1.14 rillig 128: return false
1.1 rillig 129: }
1.19 rillig 130: pkgupperLine, pkgupper := mlex.PreviousMkLine(), m[1]
1.2 rillig 131:
1.19 rillig 132: if !mlex.SkipContainsOrWarn(pkgupper + "_BUILDLINK3_MK:=") {
1.14 rillig 133: return false
1.1 rillig 134: }
1.19 rillig 135: mlex.SkipEmptyOrNote()
1.1 rillig 136:
1.2 rillig 137: // See pkgtools/createbuildlink/files/createbuildlink, keyword PKGUPPER
138: ucPkgbase := strings.ToUpper(strings.Replace(pkgbase, "-", "_", -1))
1.30 rillig 139: if ucPkgbase != pkgupper && !containsVarUse(pkgbase) {
1.3 rillig 140: pkgupperLine.Errorf("Package name mismatch between multiple-inclusion guard %q (expected %q) and package name %q (from %s).",
1.28 rillig 141: pkgupper, ucPkgbase, pkgbase, pkgupperLine.RelMkLine(ck.pkgbaseLine))
1.25 rillig 142: }
143: ck.checkPkgbaseMismatch(pkgbase)
144:
145: return true
146: }
147:
148: func (ck *Buildlink3Checker) checkPkgbaseMismatch(bl3base string) {
1.30 rillig 149: pkg := ck.mklines.pkg
150: if pkg == nil {
1.25 rillig 151: return
1.2 rillig 152: }
1.25 rillig 153:
1.30 rillig 154: mkbase := pkg.EffectivePkgbase
1.25 rillig 155: if mkbase == "" || mkbase == bl3base || strings.TrimPrefix(mkbase, "lib") == bl3base {
156: return
157: }
158:
159: if hasPrefix(mkbase, bl3base) && matches(mkbase[len(bl3base):], `^\d+$`) {
160: return
1.1 rillig 161: }
162:
1.25 rillig 163: ck.pkgbaseLine.Errorf("Package name mismatch between %q in this file and %q from %s.",
1.30 rillig 164: bl3base, mkbase, ck.pkgbaseLine.RelMkLine(pkg.EffectivePkgnameLine))
1.14 rillig 165: }
1.1 rillig 166:
1.14 rillig 167: // Third paragraph: Package information.
1.19 rillig 168: func (ck *Buildlink3Checker) checkMainPart(mlex *MkLinesLexer) bool {
1.14 rillig 169: pkgbase := ck.pkgbase
170:
171: // The first .if is from the second paragraph.
172: indentLevel := 1
173:
1.19 rillig 174: for !mlex.EOF() && indentLevel > 0 {
175: mkline := mlex.CurrentMkLine()
176: mlex.Skip()
1.14 rillig 177:
178: switch {
179: case mkline.IsVarassign():
1.30 rillig 180: ck.checkVarassign(mkline, pkgbase)
1.1 rillig 181:
1.14 rillig 182: case mkline.IsDirective() && mkline.Directive() == "if":
183: indentLevel++
1.1 rillig 184:
1.14 rillig 185: case mkline.IsDirective() && mkline.Directive() == "endif":
186: indentLevel--
187: }
1.33 rillig 188:
189: mkline.ForEachUsed(func(varUse *MkVarUse, time VucTime) {
190: ck.checkVarUse(varUse, mkline)
191: })
1.14 rillig 192: }
1.1 rillig 193:
1.14 rillig 194: if indentLevel > 0 {
195: return false
196: }
1.1 rillig 197:
1.14 rillig 198: if ck.apiLine == nil {
1.19 rillig 199: mlex.CurrentLine().Warnf("Definition of BUILDLINK_API_DEPENDS is missing.")
1.14 rillig 200: }
1.19 rillig 201: mlex.SkipEmptyOrNote()
1.14 rillig 202: return true
203: }
1.1 rillig 204:
1.33 rillig 205: func (ck *Buildlink3Checker) checkVarUse(varUse *MkVarUse, mkline *MkLine) {
206: varname := varUse.varname
207: if varname == "PKG_OPTIONS" {
208: mkline.Errorf("PKG_OPTIONS is not available in buildlink3.mk files.")
209: mkline.Explain(
210: "The buildlink3.mk file of a package is only ever included",
211: "by other packages, never by the package itself.",
212: "Therefore it does not make sense to use the variable PKG_OPTIONS",
213: "in this place since it contains the package options of a random",
214: "package that happens to include this file.",
215: "",
216: "To access the options of this package, see mk/pkg-build-options.mk.")
217: }
218:
219: if varnameBase(varname) == "PKG_BUILD_OPTIONS" {
220: param := varnameParam(varname)
221: if param != "" && param != ck.pkgbase {
222: mkline.Warnf("Wrong PKG_BUILD_OPTIONS, expected %q instead of %q.",
223: ck.pkgbase, param)
224: mkline.Explain(
225: "The variable parameter for PKG_BUILD_OPTIONS must correspond",
226: "to the value of \"pkgbase\" above.")
227: }
228: }
229: }
230:
1.30 rillig 231: func (ck *Buildlink3Checker) checkVarassign(mkline *MkLine, pkgbase string) {
1.14 rillig 232: varname, value := mkline.Varname(), mkline.Value()
233: doCheck := false
1.1 rillig 234:
1.14 rillig 235: if varname == "BUILDLINK_ABI_DEPENDS."+pkgbase {
236: ck.abiLine = mkline
1.24 rillig 237: parser := NewMkParser(nil, value)
1.31 rillig 238: if dp := parser.DependencyPattern(); dp != nil && parser.EOF() {
1.14 rillig 239: ck.abi = dp
240: }
241: doCheck = true
242: }
1.1 rillig 243:
1.14 rillig 244: if varname == "BUILDLINK_API_DEPENDS."+pkgbase {
245: ck.apiLine = mkline
1.24 rillig 246: parser := NewMkParser(nil, value)
1.31 rillig 247: if dp := parser.DependencyPattern(); dp != nil && parser.EOF() {
1.14 rillig 248: ck.api = dp
249: }
250: doCheck = true
251: }
1.1 rillig 252:
1.14 rillig 253: if doCheck && ck.abi != nil && ck.api != nil && ck.abi.Pkgbase != ck.api.Pkgbase {
254: if !hasPrefix(ck.api.Pkgbase, "{") {
255: ck.abiLine.Warnf("Package name mismatch between ABI %q and API %q (from %s).",
1.28 rillig 256: ck.abi.Pkgbase, ck.api.Pkgbase, ck.abiLine.RelMkLine(ck.apiLine))
1.14 rillig 257: }
258: }
1.1 rillig 259:
1.14 rillig 260: if doCheck {
1.30 rillig 261: if ck.abi != nil && ck.abi.Lower != "" && !containsVarUse(ck.abi.Lower) {
262: if ck.api != nil && ck.api.Lower != "" && !containsVarUse(ck.api.Lower) {
1.14 rillig 263: if pkgver.Compare(ck.abi.Lower, ck.api.Lower) < 0 {
264: ck.abiLine.Warnf("ABI version %q should be at least API version %q (see %s).",
1.28 rillig 265: ck.abi.Lower, ck.api.Lower, ck.abiLine.RelMkLine(ck.apiLine))
1.14 rillig 266: }
1.2 rillig 267: }
1.1 rillig 268: }
269: }
270:
1.14 rillig 271: if varparam := mkline.Varparam(); varparam != "" && varparam != pkgbase {
272: if hasPrefix(varname, "BUILDLINK_") && mkline.Varcanon() != "BUILDLINK_API_DEPENDS.*" {
273: mkline.Warnf("Only buildlink variables for %q, not %q may be set in this file.", pkgbase, varparam)
274: }
1.2 rillig 275: }
1.14 rillig 276: }
1.2 rillig 277:
1.30 rillig 278: func (ck *Buildlink3Checker) checkVaruseInPkgbase(pkgbaseLine *MkLine) {
1.20 rillig 279: tokens, _ := pkgbaseLine.ValueTokens()
280: for _, token := range tokens {
1.19 rillig 281: if token.Varuse == nil {
282: continue
283: }
284:
285: replacement := ""
286: switch token.Varuse.varname {
287: case "PYPKGPREFIX":
288: replacement = "py"
289: case "RUBY_BASE", "RUBY_PKGPREFIX":
290: replacement = "ruby"
291: case "PHP_PKG_PREFIX":
292: replacement = "php"
293: }
294:
295: if replacement != "" {
296: pkgbaseLine.Warnf("Please use %q instead of %q (also in other variables in this file).",
297: replacement, token.Text)
298: } else {
299: pkgbaseLine.Warnf(
300: "Please replace %q with a simple string (also in other variables in this file).",
301: token.Text)
1.14 rillig 302: }
1.1 rillig 303:
1.21 rillig 304: pkgbaseLine.Explain(
1.14 rillig 305: "The identifiers in the BUILDLINK_TREE variable should be plain",
306: "strings that do not refer to any variable.",
307: "",
308: "Even for packages that depend on a specific version of a",
309: "programming language, the plain name is enough since",
310: "the version number of the programming language is stored elsewhere.",
311: "Furthermore, these package identifiers are only used at build time,",
312: "after the specific version has been decided.")
1.1 rillig 313: }
314: }
1.34 rillig 315:
316: type Buildlink3Data struct {
317: id Buildlink3ID
318: pkgsrcdir PackagePath
319: apiDepends *DependencyPattern
320: apiDependsLine *MkLine
321: abiDepends *DependencyPattern
322: abiDependsLine *MkLine
323: }
324:
325: // Buildlink3ID is the identifier that is used in the BUILDLINK_TREE
326: // for referring to a dependent package.
327: //
328: // It almost uniquely identifies a package.
329: // Packages that are alternatives to each other may use the same identifier.
330: type Buildlink3ID string
331:
332: func LoadBuildlink3Data(mklines *MkLines) *Buildlink3Data {
333: var data Buildlink3Data
334: mklines.ForEach(func(mkline *MkLine) {
335: if mkline.IsVarassign() {
336: varname := mkline.Varname()
337: varbase := varnameBase(varname)
338: varid := Buildlink3ID(varnameParam(varname))
339:
340: if varname == "BUILDLINK_TREE" {
341: value := mkline.Value()
342: if !hasPrefix(value, "-") {
343: data.id = Buildlink3ID(mkline.Value())
344: }
345: }
346:
347: if varbase == "BUILDLINK_API_DEPENDS" && varid == data.id {
348: p := NewMkParser(nil, mkline.Value())
349: dep := p.DependencyPattern()
350: if dep != nil && p.EOF() {
351: data.apiDepends = dep
352: data.apiDependsLine = mkline
353: }
354: }
355:
356: if varbase == "BUILDLINK_ABI_DEPENDS" && varid == data.id {
357: p := NewMkParser(nil, mkline.Value())
358: dep := p.DependencyPattern()
359: if dep != nil && p.EOF() {
360: data.abiDepends = dep
361: data.abiDependsLine = mkline
362: }
363: }
364:
365: if varbase == "BUILDLINK_PKGSRCDIR" && varid == data.id {
366: data.pkgsrcdir = NewPackagePathString(mkline.Value())
367: }
368: }
369: })
370: if data.id != "" && !data.pkgsrcdir.IsEmpty() && data.apiDepends != nil && data.abiDepends != nil {
371: return &data
372: }
373: return nil
374: }
CVSweb <webmaster@jp.NetBSD.org>