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

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>