Skip to content

Commit

Permalink
Updated pkglint to 5.4.7.
Browse files Browse the repository at this point in the history
Changes since 5.4.6:

* Allow conditionals of the form "${var1}" == "${var2}"
* Check for indentation of .include directives
* Check arbitrarily complex license conditions
* General code cleanup
  • Loading branch information
rillig committed Jul 10, 2016
1 parent f75b987 commit 499a4f8
Show file tree
Hide file tree
Showing 30 changed files with 1,363 additions and 1,268 deletions.
4 changes: 2 additions & 2 deletions pkgtools/pkglint/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# $NetBSD: Makefile,v 1.491 2016/07/10 11:37:27 rillig Exp $
# $NetBSD: Makefile,v 1.492 2016/07/10 21:24:47 rillig Exp $

PKGNAME= pkglint-5.4.6
PKGNAME= pkglint-5.4.7
DISTFILES= # none
CATEGORIES= pkgtools

Expand Down
2 changes: 1 addition & 1 deletion pkgtools/pkglint/files/globaldata.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (gd *GlobalData) loadTools() {
fname := G.globalData.Pkgsrcdir + "/mk/tools/bsd.tools.mk"
lines := LoadExistingLines(fname, true)
for _, line := range lines {
if m, _, includefile := match2(line.Text, reMkInclude); m {
if m, _, _, includefile := match3(line.Text, reMkInclude); m {
if !contains(includefile, "/") {
toolFiles = append(toolFiles, includefile)
}
Expand Down
4 changes: 2 additions & 2 deletions pkgtools/pkglint/files/globaldata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
func (s *Suite) Test_GlobalData_InitVartypes(c *check.C) {
G.globalData.InitVartypes()

c.Check(G.globalData.vartypes["BSD_MAKE_ENV"].checker.name, equals, "ShellWord")
c.Check(G.globalData.vartypes["USE_BUILTIN.*"].checker.name, equals, "YesNoIndirectly")
c.Check(G.globalData.vartypes["BSD_MAKE_ENV"].basicType.name, equals, "ShellWord")
c.Check(G.globalData.vartypes["USE_BUILTIN.*"].basicType.name, equals, "YesNoIndirectly")
}

func (s *Suite) Test_parselinesSuggestedUpdates(c *check.C) {
Expand Down
35 changes: 35 additions & 0 deletions pkgtools/pkglint/files/license.y
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
%{
package main
%}

%token <Node> ltNAME
%token ltAND ltOR ltOPEN ltCLOSE

%union {
Node *LicenseCondition
}

%type <Node> start list condition

%%

start : list {
liyylex.(*licenseLexer).result = $$
}

list : condition {
$$ = $1
}
list : list ltAND condition {
$$.And = append($$.And, $3)
}
list : list ltOR condition {
$$.Or = append($$.Or, $3)
}

condition : ltNAME {
$$ = $1
}
condition : ltOPEN list ltCLOSE {
$$ = &LicenseCondition{Main: $2}
}
162 changes: 127 additions & 35 deletions pkgtools/pkglint/files/licenses.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,78 @@ package main

import (
"io/ioutil"
"strings"
)

func parseLicenses(licenses string) []string {
noPerl := strings.Replace(licenses, "${PERL5_LICENSE}", "gnu-gpl-v2 OR artistic", -1)
noOps := regcomp(`[()]|AND|OR`).ReplaceAllString(noPerl, "") // cheated
return splitOnSpace(strings.TrimSpace(noOps))
//go:generate go tool yacc -p liyy -o licenseyacc.go -v licenseyacc.log license.y

// LicenseCondition describes a complex license condition.
// It has either `Name` or `Main` set.
type LicenseCondition struct {
Name string
Main *LicenseCondition
And []*LicenseCondition
Or []*LicenseCondition
}

func (lc *LicenseCondition) Walk(callback func(*LicenseCondition)) {
callback(lc)
if lc.Main != nil {
lc.Main.Walk(callback)
}
for _, and := range lc.And {
and.Walk(callback)
}
for _, or := range lc.Or {
or.Walk(callback)
}
}

type licenseLexer struct {
repl *PrefixReplacer
result *LicenseCondition
error string
}

func checktoplevelUnusedLicenses() {
func (lexer *licenseLexer) Lex(llval *liyySymType) int {
repl := lexer.repl
repl.AdvanceHspace()
switch {
case repl.rest == "":
return 0
case repl.AdvanceStr("("):
return ltOPEN
case repl.AdvanceStr(")"):
return ltCLOSE
case repl.AdvanceRegexp(`^[\w-.]+`):
word := repl.m[0]
switch word {
case "AND":
return ltAND
case "OR":
return ltOR
default:
llval.Node = &LicenseCondition{Name: word}
return ltNAME
}
}
return -1
}

func (lexer *licenseLexer) Error(s string) {
lexer.error = s
}

func parseLicenses(licenses string) *LicenseCondition {
expanded := resolveVariableRefs(licenses) // For ${PERL5_LICENSE}
lexer := &licenseLexer{repl: NewPrefixReplacer(expanded)}
result := liyyNewParser().Parse(lexer)
if result == 0 {
return lexer.result
}
return nil
}

func checkToplevelUnusedLicenses() {
if G.UsedLicenses == nil {
return
}
Expand All @@ -29,38 +91,68 @@ func checktoplevelUnusedLicenses() {
}
}

func checklineLicense(line *MkLine, value string) {
licenses := parseLicenses(value)
for _, license := range licenses {
var licenseFile string
if G.Pkg != nil {
if licenseFileValue, ok := G.Pkg.varValue("LICENSE_FILE"); ok {
licenseFile = G.CurrentDir + "/" + resolveVarsInRelativePath(licenseFileValue, false)
}
}
if licenseFile == "" {
licenseFile = G.globalData.Pkgsrcdir + "/licenses/" + license
if G.UsedLicenses != nil {
G.UsedLicenses[license] = true
}
}
type LicenseChecker struct {
MkLine *MkLine
}

if !fileExists(licenseFile) {
line.Warn1("License file %s does not exist.", cleanpath(licenseFile))
func (lc *LicenseChecker) Check(value string, op MkOperator) {
licenses := parseLicenses(ifelseStr(op == opAssignAppend, "append-placeholder ", "") + value)

if licenses == nil {
if op == opAssign {
lc.MkLine.Line.Error1("Parse error for license condition %q.", value)
} else {
lc.MkLine.Line.Error1("Parse error for appended license condition %q.", value)
}
return
}

licenses.Walk(lc.checkNode)
}

switch license {
case "fee-based-commercial-use",
"no-commercial-use",
"no-profit",
"no-redistribution",
"shareware":
line.Warn1("License %q is deprecated.", license)
Explain(
"Instead of using these deprecated licenses, extract the actual",
"license from the package into the pkgsrc/licenses/ directory",
"and define LICENSE to that file name. See the pkgsrc guide,",
"keyword LICENSE, for more information.")
func (lc *LicenseChecker) checkNode(cond *LicenseCondition) {
license := cond.Name
if license == "" || license == "append-placeholder" {
return
}

var licenseFile string
if G.Pkg != nil {
if licenseFileValue, ok := G.Pkg.varValue("LICENSE_FILE"); ok {
licenseFile = G.CurrentDir + "/" + resolveVarsInRelativePath(licenseFileValue, false)
}
}
if licenseFile == "" {
licenseFile = G.globalData.Pkgsrcdir + "/licenses/" + license
if G.UsedLicenses != nil {
G.UsedLicenses[license] = true
}
}

if !fileExists(licenseFile) {
lc.MkLine.Warn1("License file %s does not exist.", cleanpath(licenseFile))
}

switch license {
case "fee-based-commercial-use",
"no-commercial-use",
"no-profit",
"no-redistribution",
"shareware":
lc.MkLine.Error1("License %q must not be used.", license)
Explain(
"Instead of using these deprecated licenses, extract the actual",
"license from the package into the pkgsrc/licenses/ directory",
"and define LICENSE to that file name. See the pkgsrc guide,",
"keyword LICENSE, for more information.")
}

if len(cond.And) > 0 && len(cond.Or) > 0 {
lc.MkLine.Line.Error0("AND and OR operators in license conditions can only be combined using parentheses.")
Explain(
"Examples for valid license conditions are:",
"",
"\tlicense1 AND license2 AND (license3 OR license4)",
"\t(((license1 OR license2) AND (license3 OR license4)))")
}
}
27 changes: 20 additions & 7 deletions pkgtools/pkglint/files/licenses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
)

func (s *Suite) Test_parseLicenses(c *check.C) {
c.Check(parseLicenses("gnu-gpl-v2"), check.DeepEquals, []string{"gnu-gpl-v2"})
c.Check(parseLicenses("AND artistic"), check.DeepEquals, []string{"artistic"})
c.Check(parseLicenses("gnu-gpl-v2"), check.DeepEquals, &LicenseCondition{Name: "gnu-gpl-v2"})
c.Check(parseLicenses("AND artistic"), check.IsNil)
}

func (s *Suite) Test_checklineLicense(c *check.C) {
Expand All @@ -15,19 +15,32 @@ func (s *Suite) Test_checklineLicense(c *check.C) {
G.globalData.Pkgsrcdir = s.tmpdir
G.CurrentDir = s.tmpdir

checklineLicense(mkline, "gpl-v2")
licenseChecker := &LicenseChecker{mkline}
licenseChecker.Check("gpl-v2", opAssign)

c.Check(s.Output(), equals, "WARN: Makefile:7: License file ~/licenses/gpl-v2 does not exist.\n")

checklineLicense(mkline, "no-profit shareware")
licenseChecker.Check("no-profit shareware", opAssign)

c.Check(s.Output(), equals, "ERROR: Makefile:7: Parse error for license condition \"no-profit shareware\".\n")

licenseChecker.Check("no-profit AND shareware", opAssign)

c.Check(s.Output(), equals, ""+
"WARN: Makefile:7: License file ~/licenses/no-profit does not exist.\n"+
"WARN: Makefile:7: License \"no-profit\" is deprecated.\n"+
"ERROR: Makefile:7: License \"no-profit\" must not be used.\n"+
"WARN: Makefile:7: License file ~/licenses/shareware does not exist.\n"+
"WARN: Makefile:7: License \"shareware\" is deprecated.\n")
"ERROR: Makefile:7: License \"shareware\" must not be used.\n")

licenseChecker.Check("gnu-gpl-v2", opAssign)

c.Check(s.Output(), equals, "")

licenseChecker.Check("gnu-gpl-v2 AND gnu-gpl-v2 OR gnu-gpl-v2", opAssign)

c.Check(s.Output(), equals, "ERROR: Makefile:7: AND and OR operators in license conditions can only be combined using parentheses.\n")

checklineLicense(mkline, "gnu-gpl-v2")
licenseChecker.Check("(gnu-gpl-v2 OR gnu-gpl-v2) AND gnu-gpl-v2", opAssign)

c.Check(s.Output(), equals, "")
}
2 changes: 1 addition & 1 deletion pkgtools/pkglint/files/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (pkglint *Pkglint) Main(args ...string) (exitcode int) {
CheckDirent(item)
}

checktoplevelUnusedLicenses()
checkToplevelUnusedLicenses()
pkglint.PrintSummary()
if G.opts.Profiling {
G.loghisto.PrintStats("loghisto", G.logOut, 0)
Expand Down
Loading

0 comments on commit 499a4f8

Please sign in to comment.