pkgtools/pkglint: update to 20.1.11 Changes since 20.1.10: PKG_SYSCONFDIR and VARBASE must not appear in INSTALLATION_DIRS. Patch files in which the line number have been edited manually are marked with notes.diff -r1.649 -r1.650 pkgsrc/pkgtools/pkglint/Makefile
(rillig)
@@ -1,16 +1,16 @@ | @@ -1,16 +1,16 @@ | |||
1 | # $NetBSD: Makefile,v 1.649 2020/05/24 19:12:29 rillig Exp $ | 1 | # $NetBSD: Makefile,v 1.650 2020/05/29 20:13:17 rillig Exp $ | |
2 | 2 | |||
3 | PKGNAME= pkglint-20.1.10 | 3 | PKGNAME= pkglint-20.1.11 | |
4 | CATEGORIES= pkgtools | 4 | CATEGORIES= pkgtools | |
5 | DISTNAME= tools | 5 | DISTNAME= tools | |
6 | MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/} | 6 | MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/} | |
7 | GITHUB_PROJECT= tools | 7 | GITHUB_PROJECT= tools | |
8 | GITHUB_TAG= 92d8274bd7b8a4c65f24bafe401a029e58392704 | 8 | GITHUB_TAG= 92d8274bd7b8a4c65f24bafe401a029e58392704 | |
9 | 9 | |||
10 | MAINTAINER= rillig@NetBSD.org | 10 | MAINTAINER= rillig@NetBSD.org | |
11 | HOMEPAGE= https://github.com/rillig/pkglint | 11 | HOMEPAGE= https://github.com/rillig/pkglint | |
12 | COMMENT= Verifier for NetBSD packages | 12 | COMMENT= Verifier for NetBSD packages | |
13 | LICENSE= 2-clause-bsd | 13 | LICENSE= 2-clause-bsd | |
14 | CONFLICTS+= pkglint4-[0-9]* | 14 | CONFLICTS+= pkglint4-[0-9]* | |
15 | 15 | |||
16 | USE_TOOLS+= pax | 16 | USE_TOOLS+= pax |
@@ -811,77 +811,74 @@ func (mkline *MkLine) VariableNeedsQuoti | @@ -811,77 +811,74 @@ func (mkline *MkLine) VariableNeedsQuoti | |||
811 | // Good: LDADD+= ${LIBS:S,^,-l,} | 811 | // Good: LDADD+= ${LIBS:S,^,-l,} | |
812 | if wantList { | 812 | if wantList { | |
813 | return yes | 813 | return yes | |
814 | } | 814 | } | |
815 | 815 | |||
816 | if trace.Tracing { | 816 | if trace.Tracing { | |
817 | trace.Step1("Don't know whether :Q is needed for %q", varuse.varname) | 817 | trace.Step1("Don't know whether :Q is needed for %q", varuse.varname) | |
818 | } | 818 | } | |
819 | return unknown | 819 | return unknown | |
820 | } | 820 | } | |
821 | 821 | |||
822 | // ForEachUsed calls the action for each variable that is used in the line. | 822 | // ForEachUsed calls the action for each variable that is used in the line. | |
823 | func (mkline *MkLine) ForEachUsed(action func(varUse *MkVarUse, time VucTime)) { | 823 | func (mkline *MkLine) ForEachUsed(action func(varUse *MkVarUse, time VucTime)) { | |
824 | ||||
825 | var searchIn func(text string, time VucTime) // mutually recursive with searchInVarUse | |||
826 | ||||
827 | searchInVarUse := func(varuse *MkVarUse, time VucTime) { | |||
828 | varname := varuse.varname | |||
829 | if !varuse.IsExpression() { | |||
830 | action(varuse, time) | |||
831 | } | |||
832 | searchIn(varname, time) | |||
833 | for _, mod := range varuse.modifiers { | |||
834 | searchIn(mod.Text, time) | |||
835 | } | |||
836 | } | |||
837 | ||||
838 | searchIn = func(text string, time VucTime) { | |||
839 | if !contains(text, "$") { | |||
840 | return | |||
841 | } | |||
842 | ||||
843 | tokens, _ := NewMkLexer(text, nil).MkTokens() | |||
844 | for _, token := range tokens { | |||
845 | if token.Varuse != nil { | |||
846 | searchInVarUse(token.Varuse, time) | |||
847 | } | |||
848 | } | |||
849 | } | |||
850 | ||||
851 | switch { | 824 | switch { | |
852 | 825 | |||
853 | case mkline.IsVarassign(): | 826 | case mkline.IsVarassign(): | |
854 | searchIn(mkline.Varname(), VucLoadTime) | 827 | mkline.ForEachUsedText(mkline.Varname(), VucLoadTime, action) | |
855 | searchIn(mkline.Value(), mkline.Op().Time()) | 828 | mkline.ForEachUsedText(mkline.Value(), mkline.Op().Time(), action) | |
856 | 829 | |||
857 | case mkline.IsDirective() && mkline.Directive() == "for": | 830 | case mkline.IsDirective() && mkline.Directive() == "for": | |
858 | searchIn(mkline.Args(), VucLoadTime) | 831 | mkline.ForEachUsedText(mkline.Args(), VucLoadTime, action) | |
859 | 832 | |||
860 | case mkline.IsDirective() && (mkline.Directive() == "if" || mkline.Directive() == "elif") && mkline.Cond() != nil: | 833 | case mkline.IsDirective() && (mkline.Directive() == "if" || mkline.Directive() == "elif") && mkline.Cond() != nil: | |
861 | mkline.Cond().Walk(&MkCondCallback{ | 834 | mkline.Cond().Walk(&MkCondCallback{ | |
862 | VarUse: func(varuse *MkVarUse) { | 835 | VarUse: func(varuse *MkVarUse) { | |
863 | searchInVarUse(varuse, VucLoadTime) | 836 | mkline.ForEachUsedVarUse(varuse, VucLoadTime, action) | |
864 | }}) | 837 | }}) | |
865 | 838 | |||
866 | case mkline.IsShellCommand(): | 839 | case mkline.IsShellCommand(): | |
867 | searchIn(mkline.ShellCommand(), VucRunTime) | 840 | mkline.ForEachUsedText(mkline.ShellCommand(), VucRunTime, action) | |
868 | 841 | |||
869 | case mkline.IsDependency(): | 842 | case mkline.IsDependency(): | |
870 | searchIn(mkline.Targets(), VucLoadTime) | 843 | mkline.ForEachUsedText(mkline.Targets(), VucLoadTime, action) | |
871 | searchIn(mkline.Sources(), VucLoadTime) | 844 | mkline.ForEachUsedText(mkline.Sources(), VucLoadTime, action) | |
872 | 845 | |||
873 | case mkline.IsInclude(): | 846 | case mkline.IsInclude(): | |
874 | searchIn(mkline.IncludedFile().String(), VucLoadTime) | 847 | mkline.ForEachUsedText(mkline.IncludedFile().String(), VucLoadTime, action) | |
848 | } | |||
849 | } | |||
850 | ||||
851 | func (mkline *MkLine) ForEachUsedText(text string, time VucTime, action func(varUse *MkVarUse, time VucTime)) { | |||
852 | if !contains(text, "$") { | |||
853 | return | |||
854 | } | |||
855 | ||||
856 | tokens, _ := NewMkLexer(text, nil).MkTokens() | |||
857 | for _, token := range tokens { | |||
858 | if token.Varuse != nil { | |||
859 | mkline.ForEachUsedVarUse(token.Varuse, time, action) | |||
860 | } | |||
861 | } | |||
862 | } | |||
863 | ||||
864 | func (mkline *MkLine) ForEachUsedVarUse(varuse *MkVarUse, time VucTime, action func(varUse *MkVarUse, time VucTime)) { | |||
865 | varname := varuse.varname | |||
866 | if !varuse.IsExpression() { | |||
867 | action(varuse, time) | |||
868 | } | |||
869 | mkline.ForEachUsedText(varname, time, action) | |||
870 | for _, mod := range varuse.modifiers { | |||
871 | mkline.ForEachUsedText(mod.Text, time, action) | |||
875 | } | 872 | } | |
876 | } | 873 | } | |
877 | 874 | |||
878 | // UnquoteShell removes one level of double and single quotes, | 875 | // UnquoteShell removes one level of double and single quotes, | |
879 | // like in the shell. | 876 | // like in the shell. | |
880 | // | 877 | // | |
881 | // See ValueFields. | 878 | // See ValueFields. | |
882 | func (mkline *MkLine) UnquoteShell(str string, warn bool) string { | 879 | func (mkline *MkLine) UnquoteShell(str string, warn bool) string { | |
883 | sb := NewLazyStringBuilder(str) | 880 | sb := NewLazyStringBuilder(str) | |
884 | lexer := NewMkTokensLexer(mkline.Tokenize(str, false)) | 881 | lexer := NewMkTokensLexer(mkline.Tokenize(str, false)) | |
885 | 882 | |||
886 | plain := func() { | 883 | plain := func() { | |
887 | varUse := lexer.NextVarUse() | 884 | varUse := lexer.NextVarUse() |
@@ -91,37 +91,56 @@ func (ck *PatchChecker) Check(pkg *Packa | @@ -91,37 +91,56 @@ func (ck *PatchChecker) Check(pkg *Packa | |||
91 | CheckLinesTrailingEmptyLines(ck.lines) | 91 | CheckLinesTrailingEmptyLines(ck.lines) | |
92 | sha1Before := computePatchSha1Hex(ck.lines) | 92 | sha1Before := computePatchSha1Hex(ck.lines) | |
93 | if SaveAutofixChanges(ck.lines) && pkg != nil { | 93 | if SaveAutofixChanges(ck.lines) && pkg != nil { | |
94 | linesAfter := Load(ck.lines.Filename, 0) | 94 | linesAfter := Load(ck.lines.Filename, 0) | |
95 | sha1After := computePatchSha1Hex(linesAfter) | 95 | sha1After := computePatchSha1Hex(linesAfter) | |
96 | pkg.AutofixDistinfo(sha1Before, sha1After) | 96 | pkg.AutofixDistinfo(sha1Before, sha1After) | |
97 | } | 97 | } | |
98 | } | 98 | } | |
99 | 99 | |||
100 | // See https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html | 100 | // See https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html | |
101 | func (ck *PatchChecker) checkUnifiedDiff(patchedFile Path) { | 101 | func (ck *PatchChecker) checkUnifiedDiff(patchedFile Path) { | |
102 | isConfigure := ck.isConfigure(patchedFile) | 102 | isConfigure := ck.isConfigure(patchedFile) | |
103 | 103 | |||
104 | linesDiff := 0 | |||
104 | hasHunks := false | 105 | hasHunks := false | |
105 | for { | 106 | for { | |
106 | m := ck.llex.NextRegexp(rePatchUniHunk) | 107 | m := ck.llex.NextRegexp(rePatchUniHunk) | |
107 | if m == nil { | 108 | if m == nil { | |
108 | break | 109 | break | |
109 | } | 110 | } | |
110 | 111 | |||
111 | text := m[0] | 112 | text := m[0] | |
112 | hasHunks = true | 113 | hasHunks = true | |
114 | linenoDel := toInt(m[1], 0) | |||
113 | linesToDel := toInt(m[2], 1) | 115 | linesToDel := toInt(m[2], 1) | |
116 | linenoAdd := toInt(m[3], 0) | |||
114 | linesToAdd := toInt(m[4], 1) | 117 | linesToAdd := toInt(m[4], 1) | |
118 | if linenoDel > 0 && linenoAdd > 0 && linenoDel+linesDiff != linenoAdd { | |||
119 | line := ck.llex.PreviousLine() | |||
120 | line.Notef("The difference between the line numbers %d and %d should be %d, not %d.", | |||
121 | linenoDel, linenoAdd, linesDiff, linenoAdd-linenoDel) | |||
122 | line.Explain( | |||
123 | "This only happens when patches are edited manually.", | |||
124 | "", | |||
125 | "To fix this, either regenerate the line numbers by first running", | |||
126 | bmake("patch"), | |||
127 | "and then \"mkpatches\", or edit the line numbers by hand.", | |||
128 | "", | |||
129 | "While here, it's a good idea to make the patch apply really cleanly,", | |||
130 | "by ensuring that the output from the patch command does not contain", | |||
131 | "the word \"offset\", like in \"Hunk #11 succeeded at 2598 (offset 10 lines).") | |||
132 | } | |||
133 | linesDiff += linesToAdd - linesToDel | |||
115 | 134 | |||
116 | ck.checktextUniHunkCr() | 135 | ck.checktextUniHunkCr() | |
117 | ck.checktextCvsID(text) | 136 | ck.checktextCvsID(text) | |
118 | 137 | |||
119 | for !ck.llex.EOF() && (linesToDel > 0 || linesToAdd > 0 || hasPrefix(ck.llex.CurrentLine().Text, "\\")) { | 138 | for !ck.llex.EOF() && (linesToDel > 0 || linesToAdd > 0 || hasPrefix(ck.llex.CurrentLine().Text, "\\")) { | |
120 | line := ck.llex.CurrentLine() | 139 | line := ck.llex.CurrentLine() | |
121 | ck.llex.Skip() | 140 | ck.llex.Skip() | |
122 | 141 | |||
123 | text := line.Text | 142 | text := line.Text | |
124 | switch { | 143 | switch { | |
125 | 144 | |||
126 | case text == "": | 145 | case text == "": | |
127 | // There should be a space here, but that was a trailing space and | 146 | // There should be a space here, but that was a trailing space and |
@@ -609,45 +609,69 @@ func (s *Suite) Test_PatchChecker_Check_ | @@ -609,45 +609,69 @@ func (s *Suite) Test_PatchChecker_Check_ | |||
609 | "@@ -1,1 +1,1 @@", | 609 | "@@ -1,1 +1,1 @@", | |
610 | "- prefix := @prefix@", | 610 | "- prefix := @prefix@", | |
611 | "+ prefix := /usr/pkg") | 611 | "+ prefix := /usr/pkg") | |
612 | 612 | |||
613 | CheckLinesPatch(lines, nil) | 613 | CheckLinesPatch(lines, nil) | |
614 | 614 | |||
615 | t.CheckOutputLines( | 615 | t.CheckOutputLines( | |
616 | "ERROR: ~/patch-aa:9: Patches must not hard-code the pkgsrc PREFIX.") | 616 | "ERROR: ~/patch-aa:9: Patches must not hard-code the pkgsrc PREFIX.") | |
617 | } | 617 | } | |
618 | 618 | |||
619 | func (s *Suite) Test_PatchChecker_checkUnifiedDiff__lines_at_end(c *check.C) { | 619 | func (s *Suite) Test_PatchChecker_checkUnifiedDiff__lines_at_end(c *check.C) { | |
620 | t := s.Init(c) | 620 | t := s.Init(c) | |
621 | 621 | |||
622 | lines := t.SetUpFileLines("patch-aa", | 622 | lines := t.NewLines("patch-aa", | |
623 | CvsID, | 623 | CvsID, | |
624 | "", | 624 | "", | |
625 | "Documentation", | 625 | "Documentation", | |
626 | "", | 626 | "", | |
627 | "--- old", | 627 | "--- old", | |
628 | "+++ new", | 628 | "+++ new", | |
629 | "@@ -1,1 +1,1 @@", | 629 | "@@ -1,1 +1,1 @@", | |
630 | "- old", | 630 | "- old", | |
631 | "+ new", | 631 | "+ new", | |
632 | "", | 632 | "", | |
633 | "This line is not part of the patch. Since it is separated from", | 633 | "This line is not part of the patch. Since it is separated from", | |
634 | "the patch by an empty line, there is no reason for a warning.") | 634 | "the patch by an empty line, there is no reason for a warning.") | |
635 | 635 | |||
636 | CheckLinesPatch(lines, nil) | 636 | CheckLinesPatch(lines, nil) | |
637 | 637 | |||
638 | t.CheckOutputEmpty() | 638 | t.CheckOutputEmpty() | |
639 | } | 639 | } | |
640 | 640 | |||
641 | func (s *Suite) Test_PatchChecker_checkUnifiedDiff__line_number_mismatch(c *check.C) { | |||
642 | t := s.Init(c) | |||
643 | ||||
644 | lines := t.NewLines("patch-aa", | |||
645 | CvsID, | |||
646 | "", | |||
647 | "Documentation", | |||
648 | "", | |||
649 | "--- old", | |||
650 | "+++ new", | |||
651 | "@@ -2,1 +1,1 @@", | |||
652 | "- old", | |||
653 | "+ new", | |||
654 | "@@ -5,1 +7,1 @@", | |||
655 | "- old", | |||
656 | "+ new") | |||
657 | ||||
658 | CheckLinesPatch(lines, nil) | |||
659 | ||||
660 | t.CheckOutputLines( | |||
661 | "NOTE: patch-aa:7: The difference between the line numbers 2 and 1 should be 0, not -1.", | |||
662 | "NOTE: patch-aa:10: The difference between the line numbers 5 and 7 should be 0, not 2.") | |||
663 | } | |||
664 | ||||
641 | func (s *Suite) Test_PatchChecker_checkBeginDiff__multiple_patches_without_documentation(c *check.C) { | 665 | func (s *Suite) Test_PatchChecker_checkBeginDiff__multiple_patches_without_documentation(c *check.C) { | |
642 | t := s.Init(c) | 666 | t := s.Init(c) | |
643 | 667 | |||
644 | lines := t.SetUpFileLines("patch-aa", | 668 | lines := t.SetUpFileLines("patch-aa", | |
645 | CvsID, | 669 | CvsID, | |
646 | "", | 670 | "", | |
647 | "--- old", | 671 | "--- old", | |
648 | "+++ new", | 672 | "+++ new", | |
649 | "@@ -1,1 +1,1 @@", | 673 | "@@ -1,1 +1,1 @@", | |
650 | "- old", | 674 | "- old", | |
651 | "+ new", | 675 | "+ new", | |
652 | "", | 676 | "", | |
653 | "--- old", | 677 | "--- old", |
@@ -1132,26 +1132,34 @@ func (cv *VartypeCheck) PlistIdentifier( | @@ -1132,26 +1132,34 @@ func (cv *VartypeCheck) PlistIdentifier( | |||
1132 | "there should be a corresponding ${PLIST.identifier}", | 1132 | "there should be a corresponding ${PLIST.identifier}", | |
1133 | "in any of the PLIST files of the package.") | 1133 | "in any of the PLIST files of the package.") | |
1134 | } | 1134 | } | |
1135 | } | 1135 | } | |
1136 | 1136 | |||
1137 | // PrefixPathname checks for a pathname relative to ${PREFIX}. | 1137 | // PrefixPathname checks for a pathname relative to ${PREFIX}. | |
1138 | func (cv *VartypeCheck) PrefixPathname() { | 1138 | func (cv *VartypeCheck) PrefixPathname() { | |
1139 | if NewPath(cv.Value).IsAbs() { | 1139 | if NewPath(cv.Value).IsAbs() { | |
1140 | cv.Errorf("The pathname %q in %s must be relative to ${PREFIX}.", | 1140 | cv.Errorf("The pathname %q in %s must be relative to ${PREFIX}.", | |
1141 | cv.Value, cv.Varname) | 1141 | cv.Value, cv.Varname) | |
1142 | return | 1142 | return | |
1143 | } | 1143 | } | |
1144 | 1144 | |||
1145 | cv.MkLine.ForEachUsedText(cv.Value, VucRunTime, func(varUse *MkVarUse, time VucTime) { | |||
1146 | varname := varUse.varname | |||
1147 | if varname == "PKG_SYSCONFDIR" || varname == "VARBASE" { | |||
1148 | cv.Errorf("%s must not be used in %s since it is not relative to PREFIX.", | |||
1149 | varname, cv.Varname) | |||
1150 | } | |||
1151 | }) | |||
1152 | ||||
1145 | if m, manSubdir := match1(cv.Value, `^man/(.+)`); m { | 1153 | if m, manSubdir := match1(cv.Value, `^man/(.+)`); m { | |
1146 | from := "${PKGMANDIR}/" + manSubdir | 1154 | from := "${PKGMANDIR}/" + manSubdir | |
1147 | fix := cv.Autofix() | 1155 | fix := cv.Autofix() | |
1148 | fix.Warnf("Please use %q instead of %q.", from, cv.Value) | 1156 | fix.Warnf("Please use %q instead of %q.", from, cv.Value) | |
1149 | fix.Replace(cv.Value, from) | 1157 | fix.Replace(cv.Value, from) | |
1150 | fix.Apply() | 1158 | fix.Apply() | |
1151 | } | 1159 | } | |
1152 | } | 1160 | } | |
1153 | 1161 | |||
1154 | func (cv *VartypeCheck) PythonDependency() { | 1162 | func (cv *VartypeCheck) PythonDependency() { | |
1155 | if cv.Value != cv.ValueNoVar { | 1163 | if cv.Value != cv.ValueNoVar { | |
1156 | cv.Warnf("Python dependencies should not contain variables.") | 1164 | cv.Warnf("Python dependencies should not contain variables.") | |
1157 | } else if !matches(cv.ValueNoVar, `^[+\-.0-9A-Z_a-z]+(?:|:link|:build|:test|:tool)$`) { | 1165 | } else if !matches(cv.ValueNoVar, `^[+\-.0-9A-Z_a-z]+(?:|:link|:build|:test|:tool)$`) { |
@@ -1610,26 +1610,36 @@ func (s *Suite) Test_VartypeCheck_Prefix | @@ -1610,26 +1610,36 @@ func (s *Suite) Test_VartypeCheck_Prefix | |||
1610 | vt := NewVartypeCheckTester(s.Init(c), BtPrefixPathname) | 1610 | vt := NewVartypeCheckTester(s.Init(c), BtPrefixPathname) | |
1611 | 1611 | |||
1612 | vt.Varname("PKGMANDIR") | 1612 | vt.Varname("PKGMANDIR") | |
1613 | vt.Values( | 1613 | vt.Values( | |
1614 | "man/man1", | 1614 | "man/man1", | |
1615 | "share/locale", | 1615 | "share/locale", | |
1616 | "/absolute") | 1616 | "/absolute") | |
1617 | 1617 | |||
1618 | vt.Output( | 1618 | vt.Output( | |
1619 | "WARN: filename.mk:1: "+ | 1619 | "WARN: filename.mk:1: "+ | |
1620 | "Please use \"${PKGMANDIR}/man1\" instead of \"man/man1\".", | 1620 | "Please use \"${PKGMANDIR}/man1\" instead of \"man/man1\".", | |
1621 | "ERROR: filename.mk:3: The pathname \"/absolute\" in PKGMANDIR "+ | 1621 | "ERROR: filename.mk:3: The pathname \"/absolute\" in PKGMANDIR "+ | |
1622 | "must be relative to ${PREFIX}.") | 1622 | "must be relative to ${PREFIX}.") | |
1623 | ||||
1624 | vt.Varname("INSTALLATION_DIRS") | |||
1625 | vt.Values( | |||
1626 | "bin ${PKG_SYSCONFDIR} ${VARBASE}") | |||
1627 | ||||
1628 | vt.Output( | |||
1629 | "ERROR: filename.mk:11: PKG_SYSCONFDIR must not be used in INSTALLATION_DIRS "+ | |||
1630 | "since it is not relative to PREFIX.", | |||
1631 | "ERROR: filename.mk:11: VARBASE must not be used in INSTALLATION_DIRS "+ | |||
1632 | "since it is not relative to PREFIX.") | |||
1623 | } | 1633 | } | |
1624 | 1634 | |||
1625 | func (s *Suite) Test_VartypeCheck_PythonDependency(c *check.C) { | 1635 | func (s *Suite) Test_VartypeCheck_PythonDependency(c *check.C) { | |
1626 | vt := NewVartypeCheckTester(s.Init(c), BtPythonDependency) | 1636 | vt := NewVartypeCheckTester(s.Init(c), BtPythonDependency) | |
1627 | 1637 | |||
1628 | vt.Varname("PYTHON_VERSIONED_DEPENDENCIES") | 1638 | vt.Varname("PYTHON_VERSIONED_DEPENDENCIES") | |
1629 | vt.Values( | 1639 | vt.Values( | |
1630 | "cairo", | 1640 | "cairo", | |
1631 | "${PYDEP}", | 1641 | "${PYDEP}", | |
1632 | "cairo,X") | 1642 | "cairo,X") | |
1633 | 1643 | |||
1634 | vt.Output( | 1644 | vt.Output( | |
1635 | "WARN: filename.mk:2: Python dependencies should not contain variables.", | 1645 | "WARN: filename.mk:2: Python dependencies should not contain variables.", |