summaryrefslogtreecommitdiff
path: root/pkgtools
diff options
context:
space:
mode:
authorrillig <rillig@pkgsrc.org>2019-11-01 19:56:52 +0000
committerrillig <rillig@pkgsrc.org>2019-11-01 19:56:52 +0000
commit1780979c3ee38c9797427d7dfa96fed153e37349 (patch)
tree1a2095a273bb1874a64c21385c0ee256592f89f9 /pkgtools
parent032a3d5a8a86713c153ade17bf7173829d4d0f20 (diff)
downloadpkgsrc-1780979c3ee38c9797427d7dfa96fed153e37349.tar.gz
pkgtools/pkglint: update to 19.3.4
Changes since 19.3.3: In cases where the conditions for including buildlink3.mk files differ between the package itself and its own buildlink3.mk file, explain how to determine PKG_OPTIONS for dependencies. Don't issue wrong warnings in options.mk files when the options are handled in a .for loop.
Diffstat (limited to 'pkgtools')
-rw-r--r--pkgtools/pkglint/Makefile4
-rw-r--r--pkgtools/pkglint/files/category.go2
-rw-r--r--pkgtools/pkglint/files/category_test.go4
-rw-r--r--pkgtools/pkglint/files/check_test.go3
-rw-r--r--pkgtools/pkglint/files/mklinechecker.go11
-rw-r--r--pkgtools/pkglint/files/mktypes.go8
-rw-r--r--pkgtools/pkglint/files/mktypes_test.go41
-rwxr-xr-xpkgtools/pkglint/files/options.go80
-rwxr-xr-xpkgtools/pkglint/files/options_test.go98
-rw-r--r--pkgtools/pkglint/files/package.go24
-rw-r--r--pkgtools/pkglint/files/package_test.go105
-rw-r--r--pkgtools/pkglint/files/pkglint.go27
-rw-r--r--pkgtools/pkglint/files/toplevel.go2
-rw-r--r--pkgtools/pkglint/files/util.go31
-rw-r--r--pkgtools/pkglint/files/vardefs.go46
15 files changed, 388 insertions, 98 deletions
diff --git a/pkgtools/pkglint/Makefile b/pkgtools/pkglint/Makefile
index e6a7c80c24a..f34bbdb4784 100644
--- a/pkgtools/pkglint/Makefile
+++ b/pkgtools/pkglint/Makefile
@@ -1,6 +1,6 @@
-# $NetBSD: Makefile,v 1.603 2019/10/26 11:43:36 rillig Exp $
+# $NetBSD: Makefile,v 1.604 2019/11/01 19:56:52 rillig Exp $
-PKGNAME= pkglint-19.3.3
+PKGNAME= pkglint-19.3.4
CATEGORIES= pkgtools
DISTNAME= tools
MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/}
diff --git a/pkgtools/pkglint/files/category.go b/pkgtools/pkglint/files/category.go
index f068419edc5..5d1a66d5cf5 100644
--- a/pkgtools/pkglint/files/category.go
+++ b/pkgtools/pkglint/files/category.go
@@ -161,6 +161,6 @@ func CheckdirCategory(dir string) {
recurseInto = append(recurseInto, joinPath(dir, msub.name))
}
}
- G.Todo = append(recurseInto, G.Todo...)
+ G.Todo.PushFront(recurseInto...)
}
}
diff --git a/pkgtools/pkglint/files/category_test.go b/pkgtools/pkglint/files/category_test.go
index e279d41fee0..6d6a8a10499 100644
--- a/pkgtools/pkglint/files/category_test.go
+++ b/pkgtools/pkglint/files/category_test.go
@@ -208,12 +208,12 @@ func (s *Suite) Test_CheckdirCategory__recursive(c *check.C) {
// It is only removed in Pkglint.Main, therefore it stays there even
// after the call to CheckdirCategory. This is a bit unrealistic,
// but close enough for this test.
- t.CheckDeepEquals(G.Todo, []string{"."})
+ t.CheckDeepEquals(G.Todo.entries, []string{"."})
CheckdirCategory(".")
t.CheckOutputEmpty()
- t.CheckDeepEquals(G.Todo, []string{"./package", "."})
+ t.CheckDeepEquals(G.Todo.entries, []string{"./package", "."})
}
// Ensures that a directory in the file system can be added at the very
diff --git a/pkgtools/pkglint/files/check_test.go b/pkgtools/pkglint/files/check_test.go
index 1dabb5237d2..289b86d8110 100644
--- a/pkgtools/pkglint/files/check_test.go
+++ b/pkgtools/pkglint/files/check_test.go
@@ -69,6 +69,7 @@ func (s *Suite) SetUpTest(c *check.C) {
t.c = c
t.SetUpCommandLine("-Wall") // To catch duplicate warnings
+ G.Todo.Pop() // The "." was inserted by default.
t.seenSetUpCommandLine = false // This default call doesn't count.
// To improve code coverage and ensure that trace.Result works
@@ -693,6 +694,8 @@ func (t *Tester) FinishSetUp() {
//
// Arguments that name existing files or directories in the temporary test
// directory are transformed to their actual paths.
+//
+// Does not work in combination with SetUpOption.
func (t *Tester) Main(args ...string) int {
if t.seenFinish && !t.seenMain {
t.Errorf("Calling t.FinishSetup() before t.Main() is redundant " +
diff --git a/pkgtools/pkglint/files/mklinechecker.go b/pkgtools/pkglint/files/mklinechecker.go
index 3446a0ea5fd..df63a57fa7c 100644
--- a/pkgtools/pkglint/files/mklinechecker.go
+++ b/pkgtools/pkglint/files/mklinechecker.go
@@ -437,16 +437,9 @@ func (ck MkLineChecker) checkVarassignLeftRationale() {
return
}
- needsRationale := func(mkline *MkLine) bool {
- if !mkline.IsVarassignMaybeCommented() {
- return false
- }
- vartype := G.Pkgsrc.VariableType(ck.MkLines, mkline.Varname())
- return vartype != nil && vartype.NeedsRationale()
- }
-
mkline := ck.MkLine
- if !needsRationale(mkline) {
+ vartype := G.Pkgsrc.VariableType(ck.MkLines, mkline.Varname())
+ if vartype == nil || !vartype.NeedsRationale() {
return
}
diff --git a/pkgtools/pkglint/files/mktypes.go b/pkgtools/pkglint/files/mktypes.go
index 8212d3ff34e..e1bb9d30b8a 100644
--- a/pkgtools/pkglint/files/mktypes.go
+++ b/pkgtools/pkglint/files/mktypes.go
@@ -89,13 +89,15 @@ func (m MkVarUseModifier) Subst(str string) (string, bool) {
// MatchMatch tries to match the modifier to a :M or a :N pattern matching.
// Examples:
-// :Mpattern => true, true, "pattern"
-// :Npattern => true, false, "pattern"
+// :Mpattern => true, true, "pattern", true
+// :M* => true, true, "*", false
+// :M${VAR} => true, true, "${VAR}", false
+// :Npattern => true, false, "pattern", true
// :X => false
func (m MkVarUseModifier) MatchMatch() (ok bool, positive bool, pattern string, exact bool) {
if hasPrefix(m.Text, "M") || hasPrefix(m.Text, "N") {
// See devel/bmake/files/str.c:^Str_Match
- exact := !strings.ContainsAny(m.Text[1:], "*?[\\")
+ exact := !strings.ContainsAny(m.Text[1:], "*?[\\$")
return true, m.Text[0] == 'M', m.Text[1:], exact
}
return false, false, "", false
diff --git a/pkgtools/pkglint/files/mktypes_test.go b/pkgtools/pkglint/files/mktypes_test.go
index 6c18f15907a..fd89c22f40b 100644
--- a/pkgtools/pkglint/files/mktypes_test.go
+++ b/pkgtools/pkglint/files/mktypes_test.go
@@ -39,6 +39,16 @@ func (MkTokenBuilder) VarUse(varname string, modifiers ...string) *MkVarUse {
return &MkVarUse{varname, mods}
}
+// AddCommand adds a command directly to a list of commands,
+// creating all the intermediate nodes for the syntactic representation.
+// As soon as that representation is replaced with a semantic representation,
+// this method should no longer be necessary.
+func (list *MkShList) AddCommand(command *MkShCommand) *MkShList {
+ pipeline := NewMkShPipeline(false, []*MkShCommand{command})
+ andOr := NewMkShAndOr(pipeline)
+ return list.AddAndOr(andOr)
+}
+
func (s *Suite) Test_MkVarUse_Mod(c *check.C) {
t := s.Init(c)
@@ -53,14 +63,29 @@ func (s *Suite) Test_MkVarUse_Mod(c *check.C) {
test("${PATH:ts::Q}", ":ts::Q")
}
-// AddCommand adds a command directly to a list of commands,
-// creating all the intermediate nodes for the syntactic representation.
-// As soon as that representation is replaced with a semantic representation,
-// this method should no longer be necessary.
-func (list *MkShList) AddCommand(command *MkShCommand) *MkShList {
- pipeline := NewMkShPipeline(false, []*MkShCommand{command})
- andOr := NewMkShAndOr(pipeline)
- return list.AddAndOr(andOr)
+func (s *Suite) Test_MkVarUseModifier_MatchMatch(c *check.C) {
+ t := s.Init(c)
+
+ testFail := func(modifier string) {
+ mod := MkVarUseModifier{modifier}
+ ok, _, _, _ := mod.MatchMatch()
+ t.CheckEquals(ok, false)
+ }
+ test := func(modifier string, positive bool, pattern string, exact bool) {
+ mod := MkVarUseModifier{modifier}
+ actualOk, actualPositive, actualPattern, actualExact := mod.MatchMatch()
+ t.CheckDeepEquals(
+ []interface{}{actualOk, actualPositive, actualPattern, actualExact},
+ []interface{}{true, positive, pattern, exact})
+ }
+
+ testFail("")
+ testFail("X")
+
+ test("Mpattern", true, "pattern", true)
+ test("M*", true, "*", false)
+ test("M${VAR}", true, "${VAR}", false)
+ test("Npattern", false, "pattern", true)
}
func (s *Suite) Test_MkVarUseModifier_ChangesWords(c *check.C) {
diff --git a/pkgtools/pkglint/files/options.go b/pkgtools/pkglint/files/options.go
index ea238b348f7..2100fadcbc0 100755
--- a/pkgtools/pkglint/files/options.go
+++ b/pkgtools/pkglint/files/options.go
@@ -1,7 +1,5 @@
package pkglint
-import "path"
-
func CheckLinesOptionsMk(mklines *MkLines) {
ck := OptionsLinesChecker{
mklines,
@@ -42,10 +40,13 @@ func (ck *OptionsLinesChecker) Check() {
mlex.Skip()
}
- for !mlex.EOF() {
- ck.handleLowerLine(mlex.CurrentMkLine())
- mlex.Skip()
- }
+ i := 0
+ mklines.ForEach(func(mkline *MkLine) {
+ if i >= mlex.index {
+ ck.handleLowerLine(mkline)
+ }
+ i++
+ })
ck.checkOptionsMismatch()
@@ -81,7 +82,10 @@ func (ck *OptionsLinesChecker) handleUpperLine(mkline *MkLine) bool {
case mkline.IsVarassign():
switch mkline.Varcanon() {
- case "PKG_SUPPORTED_OPTIONS", "PKG_OPTIONS_GROUP.*", "PKG_OPTIONS_SET.*":
+ case "PKG_SUPPORTED_OPTIONS",
+ "PKG_SUPPORTED_OPTIONS.*",
+ "PKG_OPTIONS_GROUP.*",
+ "PKG_OPTIONS_SET.*":
for _, option := range mkline.ValueFields(mkline.Value()) {
if !containsVarRef(option) {
ck.declaredOptions[option] = mkline
@@ -113,48 +117,64 @@ func (ck *OptionsLinesChecker) handleUpperLine(mkline *MkLine) bool {
}
func (ck *OptionsLinesChecker) handleLowerLine(mkline *MkLine) {
- if mkline.IsDirective() {
- directive := mkline.Directive()
- if directive == "if" || directive == "elif" {
- cond := mkline.Cond()
- if cond != nil {
- ck.handleLowerCondition(mkline, cond)
- }
- }
+ if !mkline.IsDirective() {
+ return
+ }
+
+ directive := mkline.Directive()
+ if directive != "if" && directive != "elif" {
+ return
+ }
+
+ cond := mkline.Cond()
+ if cond == nil {
+ return
}
+
+ ck.handleLowerCondition(mkline, cond)
}
func (ck *OptionsLinesChecker) handleLowerCondition(mkline *MkLine, cond *MkCond) {
- recordUsedOption := func(varuse *MkVarUse) {
- if varuse.varname != "PKG_OPTIONS" || len(varuse.modifiers) != 1 {
+ recordOption := func(option string) {
+ if containsVarRef(option) {
return
}
- m, positive, pattern, exact := varuse.modifiers[0].MatchMatch()
- if !m || !positive || containsVarRef(pattern) {
+ ck.handledOptions[option] = mkline
+ ck.optionsInDeclarationOrder = append(ck.optionsInDeclarationOrder, option)
+ }
+
+ recordVarUse := func(varuse *MkVarUse) {
+ if varuse.varname != "PKG_OPTIONS" || len(varuse.modifiers) != 1 {
return
}
- if exact {
- option := pattern
- ck.handledOptions[option] = mkline
- ck.optionsInDeclarationOrder = append(ck.optionsInDeclarationOrder, option)
+ m, positive, pattern, exact := varuse.modifiers[0].MatchMatch()
+ if !m || !positive {
return
}
- for declaredOption := range ck.declaredOptions {
- matched, err := path.Match(pattern, declaredOption)
- if err == nil && matched {
- ck.handledOptions[declaredOption] = mkline
- ck.optionsInDeclarationOrder = append(ck.optionsInDeclarationOrder, declaredOption)
+ if optionVarUse := ToVarUse(pattern); optionVarUse != nil {
+ for _, option := range ck.mklines.ExpandLoopVar(optionVarUse.varname) {
+ recordOption(option)
+ }
+
+ } else if exact {
+ recordOption(pattern)
+
+ } else {
+ for declaredOption := range ck.declaredOptions {
+ if pathMatches(pattern, declaredOption) {
+ recordOption(declaredOption)
+ }
}
}
}
cond.Walk(&MkCondCallback{
- Empty: recordUsedOption,
- Var: recordUsedOption})
+ Empty: recordVarUse,
+ Var: recordVarUse})
if cond.Empty != nil && cond.Empty.varname == "PKG_OPTIONS" && mkline.HasElseBranch() {
mkline.Notef("The positive branch of the .if/.else should be the one where the option is set.")
diff --git a/pkgtools/pkglint/files/options_test.go b/pkgtools/pkglint/files/options_test.go
index 77dba48ce9d..61986f2af9d 100755
--- a/pkgtools/pkglint/files/options_test.go
+++ b/pkgtools/pkglint/files/options_test.go
@@ -387,3 +387,101 @@ func (s *Suite) Test_CheckLinesOptionsMk__combined_option_handling_coverage(c *c
t.CheckOutputLines(
"WARN: options.mk:4: Option \"opt-variant\" should be handled below in an .if block.")
}
+
+func (s *Suite) Test_CheckLinesOptionsMk__options_in_for_loop(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpOption("idea", "")
+ t.SetUpOption("md2", "")
+ t.SetUpOption("md5", "")
+ t.SetUpOption("other", "")
+ t.CreateFileLines("mk/bsd.options.mk")
+ t.SetUpPackage("category/package",
+ ".include \"options.mk\"")
+ t.CreateFileLines("category/package/options.mk",
+ MkCvsID,
+ "",
+ "PKG_OPTIONS_VAR=\tPKG_OPTIONS.package",
+ "PKG_SUPPORTED_OPTIONS=\tidea md2 md5 other",
+ "",
+ ".include \"../../mk/bsd.options.mk\"",
+ "",
+ ".for alg in idea md2 md5",
+ ". if ${PKG_OPTIONS:M${alg}}",
+ ". endif",
+ ".endfor")
+ t.FinishSetUp()
+ t.Chdir("category/package")
+
+ G.Check(".")
+
+ t.CheckOutputLines(
+ "WARN: options.mk:4: Option \"other\" should be handled below in an .if block.")
+}
+
+func (s *Suite) Test_CheckLinesOptionsMk__indirect(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpOption("generic", "")
+ t.SetUpOption("netbsd", "")
+ t.SetUpOption("os", "")
+ t.CreateFileLines("mk/bsd.options.mk")
+ t.SetUpPackage("category/package",
+ ".include \"options.mk\"")
+ t.CreateFileLines("category/package/options.mk",
+ MkCvsID,
+ "",
+ "PKG_OPTIONS_VAR=\tPKG_OPTIONS.package",
+ "PKG_SUPPORTED_OPTIONS=\tgeneric",
+ "PKG_SUGGESTED_OPTIONS=\tgeneric",
+ "",
+ "PKG_SUPPORTED_OPTIONS.FreeBSD=\tos",
+ "PKG_SUGGESTED_OPTIONS.FreeBSD=\tos",
+ "",
+ "PKG_SUPPORTED_OPTIONS.NetBSD+=\tnetbsd os",
+ "PKG_SUGGESTED_OPTIONS.NetBSD+=\tnetbsd os",
+ "",
+ ".include \"../../mk/bsd.options.mk\"",
+ "",
+ "PLIST_VARS+=\tgeneric netbsd os",
+ "",
+ ".for option in ${PLIST_VARS}",
+ ". if ${PKG_OPTIONS:M${option}}",
+ "CONFIGURE_ARGS+=\t--enable-${option:S/-/_/}",
+ "PLIST.${option}=\tyes",
+ ". endif",
+ ".endfor")
+ t.FinishSetUp()
+ t.Chdir("category/package")
+
+ G.Check(".")
+
+ t.CheckOutputEmpty()
+}
+
+// An unrealistic scenario, but necessary for code coverage.
+func (s *Suite) Test_CheckLinesOptionsMk__partly_indirect(c *check.C) {
+ t := s.Init(c)
+
+ t.CreateFileLines("mk/bsd.options.mk")
+ t.SetUpPackage("category/package",
+ ".include \"options.mk\"")
+ t.CreateFileLines("category/package/options.mk",
+ MkCvsID,
+ "",
+ "PKG_OPTIONS_VAR=\tPKG_OPTIONS.package",
+ "PKG_SUPPORTED_OPTIONS=\tgeneric-${OPSYS}",
+ "",
+ ".include \"../../mk/bsd.options.mk\"",
+ "",
+ ".for option in generic-${OPSYS}",
+ ". if ${PKG_OPTIONS:M${option}}",
+ ". endif",
+ ".endfor")
+ t.FinishSetUp()
+ t.Chdir("category/package")
+
+ G.Check(".")
+
+ t.CheckOutputEmpty()
+}
diff --git a/pkgtools/pkglint/files/package.go b/pkgtools/pkglint/files/package.go
index 5f1e75dc7c3..bdfd012dc4e 100644
--- a/pkgtools/pkglint/files/package.go
+++ b/pkgtools/pkglint/files/package.go
@@ -1272,6 +1272,18 @@ func (pkg *Package) checkIncludeConditionally(mkline *MkLine, indentation *Inden
key := pkg.Rel(mkline.IncludedFileFull())
+ explainPkgOptions := func(uncond *MkLine, cond *MkLine) {
+ if uncond.Basename == "buildlink3.mk" && containsStr(cond.ConditionalVars(), "PKG_OPTIONS") {
+ mkline.Explain(
+ "When including a dependent file, the conditions in the",
+ "buildlink3.mk file should be the same as in options.mk",
+ "or the Makefile.",
+ "",
+ "To find out the PKG_OPTIONS of this package at build time,",
+ "have a look at mk/pkg-build-options.mk.")
+ }
+ }
+
if indentation.IsConditional() {
if other := pkg.unconditionalIncludes[key]; other != nil {
if !pkg.Once.FirstTimeSlice("checkIncludeConditionally", mkline.Location.String(), other.Location.String()) {
@@ -1282,6 +1294,8 @@ func (pkg *Package) checkIncludeConditionally(mkline *MkLine, indentation *Inden
"%q is included conditionally here (depending on %s) "+
"and unconditionally in %s.",
cleanpath(mkline.IncludedFile()), strings.Join(mkline.ConditionalVars(), ", "), mkline.RefTo(other))
+
+ explainPkgOptions(other, mkline)
}
} else {
@@ -1295,15 +1309,7 @@ func (pkg *Package) checkIncludeConditionally(mkline *MkLine, indentation *Inden
"and conditionally in %s (depending on %s).",
cleanpath(mkline.IncludedFile()), mkline.RefTo(other), strings.Join(other.ConditionalVars(), ", "))
- if mkline.Basename == "buildlink3.mk" && containsStr(other.ConditionalVars(), "PKG_OPTIONS") {
- mkline.Explain(
- "When including a dependent file, the conditions in the",
- "buildlink3.mk file should be the same as in options.mk",
- "or the Makefile.",
- "",
- "To find out the PKG_OPTIONS of this package at build time,",
- "have a look at mk/pkg-build-options.mk.")
- }
+ explainPkgOptions(mkline, other)
}
}
diff --git a/pkgtools/pkglint/files/package_test.go b/pkgtools/pkglint/files/package_test.go
index 7873af65b1b..b6a8fd6091e 100644
--- a/pkgtools/pkglint/files/package_test.go
+++ b/pkgtools/pkglint/files/package_test.go
@@ -1288,6 +1288,111 @@ func (s *Suite) Test_Package_checkIncludeConditionally__conditional_and_uncondit
"WARN: options.mk:3: Expected definition of PKG_OPTIONS_VAR.")
}
+func (s *Suite) Test_Package_checkIncludeConditionally__explain_PKG_OPTIONS_in_Makefile(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpCommandLine("-Wall", "--explain")
+ t.SetUpOption("zlib", "use zlib compression")
+
+ t.CreateFileLines("mk/bsd.options.mk",
+ MkCvsID)
+ t.CreateFileLines("devel/zlib/buildlink3.mk",
+ MkCvsID)
+ t.SetUpPackage("category/package",
+ "PKG_OPTIONS_VAR=\tPKG_OPTIONS.package",
+ "PKG_SUPPORTED_OPTIONS=\tzlib",
+ "",
+ ".include \"../../mk/bsd.options.mk\"",
+ "",
+ ".if ${PKG_OPTIONS:Mzlib}",
+ ".include \"../../devel/zlib/buildlink3.mk\"",
+ ".endif")
+ t.CreateFileDummyBuildlink3("category/package/buildlink3.mk",
+ ".include \"../../devel/zlib/buildlink3.mk\"")
+ t.Chdir("category/package")
+ t.FinishSetUp()
+
+ G.checkdirPackage(".")
+
+ t.CheckOutputLines(
+ "WARN: Makefile:26: "+
+ "\"../../devel/zlib/buildlink3.mk\" is included conditionally here "+
+ "(depending on PKG_OPTIONS) and unconditionally in buildlink3.mk:12.",
+ "",
+ "\tWhen including a dependent file, the conditions in the buildlink3.mk",
+ "\tfile should be the same as in options.mk or the Makefile.",
+ "",
+ "\tTo find out the PKG_OPTIONS of this package at build time, have a",
+ "\tlook at mk/pkg-build-options.mk.",
+ "")
+}
+
+func (s *Suite) Test_Package_checkIncludeConditionally__no_explanation(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpCommandLine("-Wall", "--explain")
+ t.CreateFileLines("devel/zlib/buildlink3.mk",
+ MkCvsID)
+ t.SetUpPackage("category/package",
+ ".if ${OPSYS} == Linux",
+ ".include \"../../devel/zlib/buildlink3.mk\"",
+ ".endif")
+ t.CreateFileDummyBuildlink3("category/package/buildlink3.mk",
+ ".include \"../../devel/zlib/buildlink3.mk\"")
+ t.Chdir("category/package")
+ t.FinishSetUp()
+
+ G.checkdirPackage(".")
+
+ t.CheckOutputLines(
+ "WARN: Makefile:21: " +
+ "\"../../devel/zlib/buildlink3.mk\" is included conditionally here " +
+ "(depending on OPSYS) and unconditionally in buildlink3.mk:12.")
+}
+
+func (s *Suite) Test_Package_checkIncludeConditionally__explain_PKG_OPTIONS_in_options_mk(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpCommandLine("-Wall", "--explain")
+ t.SetUpOption("zlib", "use zlib compression")
+
+ t.CreateFileLines("mk/bsd.options.mk",
+ MkCvsID)
+ t.CreateFileLines("devel/zlib/buildlink3.mk",
+ MkCvsID)
+ t.SetUpPackage("category/package",
+ ".include \"options.mk\"")
+ t.CreateFileLines("category/package/options.mk",
+ MkCvsID,
+ "",
+ "PKG_OPTIONS_VAR=\tPKG_OPTIONS.package",
+ "PKG_SUPPORTED_OPTIONS=\tzlib",
+ "",
+ ".include \"../../mk/bsd.options.mk\"",
+ "",
+ ".if ${PKG_OPTIONS:Mzlib}",
+ ".include \"../../devel/zlib/buildlink3.mk\"",
+ ".endif")
+ t.CreateFileDummyBuildlink3("category/package/buildlink3.mk",
+ ".include \"../../devel/zlib/buildlink3.mk\"")
+ t.Chdir("category/package")
+ t.FinishSetUp()
+
+ G.checkdirPackage(".")
+
+ t.CheckOutputLines(
+ "WARN: buildlink3.mk:12: "+
+ "\"../../devel/zlib/buildlink3.mk\" is included unconditionally here "+
+ "and conditionally in options.mk:9 (depending on PKG_OPTIONS).",
+ "",
+ "\tWhen including a dependent file, the conditions in the buildlink3.mk",
+ "\tfile should be the same as in options.mk or the Makefile.",
+ "",
+ "\tTo find out the PKG_OPTIONS of this package at build time, have a",
+ "\tlook at mk/pkg-build-options.mk.",
+ "")
+}
+
func (s *Suite) Test_Package_checkIncludeConditionally__unconditionally_first(c *check.C) {
t := s.Init(c)
diff --git a/pkgtools/pkglint/files/pkglint.go b/pkgtools/pkglint/files/pkglint.go
index 252d476137b..05efef29622 100644
--- a/pkgtools/pkglint/files/pkglint.go
+++ b/pkgtools/pkglint/files/pkglint.go
@@ -26,12 +26,12 @@ type Pkglint struct {
Pkgsrc Pkgsrc // Global data, mostly extracted from mk/*.
Pkg *Package // The package that is currently checked, or nil.
- Todo []string // The files or directories that still need to be checked.
- Wip bool // Is the currently checked file or package from pkgsrc-wip?
- Infrastructure bool // Is the currently checked file from the pkgsrc infrastructure?
- Testing bool // Is pkglint in self-testing mode (only during development)?
- Experimental bool // For experimental features, only enabled individually in tests
- Username string // For checking against OWNER and MAINTAINER
+ Todo StringQueue // The files or directories that still need to be checked.
+ Wip bool // Is the currently checked file or package from pkgsrc-wip?
+ Infrastructure bool // Is the currently checked file from the pkgsrc infrastructure?
+ Testing bool // Is pkglint in self-testing mode (only during development)?
+ Experimental bool // For experimental features, only enabled individually in tests
+ Username string // For checking against OWNER and MAINTAINER
cvsEntriesDir string // Cached to avoid I/O
cvsEntries map[string]CvsEntry
@@ -190,10 +190,8 @@ func (pkglint *Pkglint) Main(stdout io.Writer, stderr io.Writer, args []string)
pkglint.prepareMainLoop()
- for len(pkglint.Todo) > 0 {
- item := pkglint.Todo[0]
- pkglint.Todo = pkglint.Todo[1:]
- pkglint.Check(item)
+ for !pkglint.Todo.Empty() {
+ pkglint.Check(pkglint.Todo.Pop())
}
pkglint.Pkgsrc.checkToplevelUnusedLicenses()
@@ -258,7 +256,7 @@ func (pkglint *Pkglint) setUpProfiling() func() {
}
func (pkglint *Pkglint) prepareMainLoop() {
- firstDir := pkglint.Todo[0]
+ firstDir := pkglint.Todo.Front()
if fileExists(firstDir) {
firstDir = path.Dir(firstDir)
}
@@ -333,12 +331,11 @@ func (pkglint *Pkglint) ParseCommandLine(args []string) int {
return 0
}
- pkglint.Todo = nil
for _, arg := range pkglint.Opts.args {
- pkglint.Todo = append(pkglint.Todo, filepath.ToSlash(arg))
+ pkglint.Todo.Push(filepath.ToSlash(arg))
}
- if len(pkglint.Todo) == 0 {
- pkglint.Todo = []string{"."}
+ if pkglint.Todo.Empty() {
+ pkglint.Todo.Push(".")
}
return -1
diff --git a/pkgtools/pkglint/files/toplevel.go b/pkgtools/pkglint/files/toplevel.go
index 71af8aa6c87..a11d1dff856 100644
--- a/pkgtools/pkglint/files/toplevel.go
+++ b/pkgtools/pkglint/files/toplevel.go
@@ -31,7 +31,7 @@ func CheckdirToplevel(dir string) {
if G.Opts.CheckGlobal {
G.InterPackage.Enable()
}
- G.Todo = append(append([]string(nil), ctx.subdirs...), G.Todo...)
+ G.Todo.PushFront(ctx.subdirs...)
}
}
diff --git a/pkgtools/pkglint/files/util.go b/pkgtools/pkglint/files/util.go
index b2634f7b6fb..70594ac6751 100644
--- a/pkgtools/pkglint/files/util.go
+++ b/pkgtools/pkglint/files/util.go
@@ -1384,3 +1384,34 @@ func shquote(s string) string {
}
return "'" + strings.Replace(s, "'", "'\\''", -1) + "'"
}
+
+func pathMatches(pattern, s string) bool {
+ matched, err := path.Match(pattern, s)
+ return err == nil && matched
+}
+
+type StringQueue struct {
+ entries []string
+}
+
+func (q *StringQueue) PushFront(entries ...string) {
+ q.entries = append(append([]string(nil), entries...), q.entries...)
+}
+
+func (q *StringQueue) Push(entries ...string) {
+ q.entries = append(q.entries, entries...)
+}
+
+func (q *StringQueue) Empty() bool {
+ return len(q.entries) == 0
+}
+
+func (q *StringQueue) Front() string {
+ return q.entries[0]
+}
+
+func (q *StringQueue) Pop() string {
+ front := q.entries[0]
+ q.entries = q.entries[1:]
+ return front
+}
diff --git a/pkgtools/pkglint/files/vardefs.go b/pkgtools/pkglint/files/vardefs.go
index 4e1aa236d28..4c98824323e 100644
--- a/pkgtools/pkglint/files/vardefs.go
+++ b/pkgtools/pkglint/files/vardefs.go
@@ -156,6 +156,17 @@ func (reg *VarTypeRegistry) pkglistrat(varname string, basicType *BasicType) {
"Makefile, Makefile.*, *.mk: default, set, append, use")
}
+// A package-defined load-time list may be used or defined or appended to in
+// all Makefiles except buildlink3.mk and builtin.mk. Simple assignment
+// (instead of appending) is also allowed. If this leads to an unconditional
+// assignment overriding a previous value, the redundancy check will catch it.
+func (reg *VarTypeRegistry) pkgloadlist(varname string, basicType *BasicType) {
+ reg.acllist(varname, basicType,
+ List|PackageSettable,
+ "buildlink3.mk, builtin.mk: none",
+ "Makefile, Makefile.*, *.mk: default, set, append, use, use-loadtime")
+}
+
// pkgappend declares a variable that may use the += operator,
// even though it is not a list where each item can be interpreted
// on its own.
@@ -328,7 +339,7 @@ func (reg *VarTypeRegistry) compilerLanguages(src *Pkgsrc) *BasicType {
VarUse: func(varuse *MkVarUse) {
if varuse.varname == "USE_LANGUAGES" && len(varuse.modifiers) == 1 {
ok, _, pattern, exact := varuse.modifiers[0].MatchMatch()
- if ok && exact && !containsVarRef(pattern) {
+ if ok && exact {
languages[intern(pattern)] = true
}
}
@@ -1409,28 +1420,27 @@ func (reg *VarTypeRegistry) Init(src *Pkgsrc) {
// define them in the Makefile or Makefile.common.
reg.sysloadlist("PKG_OPTIONS", BtOption)
reg.usrlist("PKG_OPTIONS.*", BtOption)
- opt := reg.pkg
- optlist := reg.pkglist
- optlist("PKG_LEGACY_OPTIONS", BtOption)
- optlist("PKG_OPTIONS_DEPRECATED_WARNINGS", BtShellWord)
- optlist("PKG_OPTIONS_GROUP.*", BtOption)
- optlist("PKG_OPTIONS_LEGACY_OPTS", BtUnknown)
- optlist("PKG_OPTIONS_LEGACY_VARS", BtUnknown)
- optlist("PKG_OPTIONS_NONEMPTY_SETS", BtIdentifier)
- optlist("PKG_OPTIONS_OPTIONAL_GROUPS", BtIdentifier)
- optlist("PKG_OPTIONS_REQUIRED_GROUPS", BtIdentifier)
- optlist("PKG_OPTIONS_SET.*", BtOption)
- opt("PKG_OPTIONS_VAR", BtPkgOptionsVar)
- reg.pkglist("PKG_SKIP_REASON", BtShellWord)
- optlist("PKG_SUGGESTED_OPTIONS", BtOption)
- optlist("PKG_SUGGESTED_OPTIONS.*", BtOption)
- optlist("PKG_SUPPORTED_OPTIONS", BtOption)
+ reg.pkgloadlist("PKG_LEGACY_OPTIONS", BtOption)
+ reg.pkgloadlist("PKG_OPTIONS_DEPRECATED_WARNINGS", BtShellWord)
+ reg.pkgloadlist("PKG_OPTIONS_GROUP.*", BtOption)
+ reg.pkgloadlist("PKG_OPTIONS_LEGACY_OPTS", BtUnknown)
+ reg.pkgloadlist("PKG_OPTIONS_LEGACY_VARS", BtUnknown)
+ reg.pkgloadlist("PKG_OPTIONS_NONEMPTY_SETS", BtIdentifier)
+ reg.pkgloadlist("PKG_OPTIONS_OPTIONAL_GROUPS", BtIdentifier)
+ reg.pkgloadlist("PKG_OPTIONS_REQUIRED_GROUPS", BtIdentifier)
+ reg.pkgloadlist("PKG_OPTIONS_SET.*", BtOption)
+ reg.pkgload("PKG_OPTIONS_VAR", BtPkgOptionsVar)
+ reg.pkgloadlist("PKG_SUGGESTED_OPTIONS", BtOption)
+ reg.pkgloadlist("PKG_SUGGESTED_OPTIONS.*", BtOption)
+ reg.pkgloadlist("PKG_SUPPORTED_OPTIONS", BtOption)
+ reg.pkgloadlist("PKG_SUPPORTED_OPTIONS.*", BtOption)
// end PKG_OPTIONS section
reg.pkg("PKG_PRESERVE", BtYes)
reg.pkg("PKG_SHELL", BtPathname)
reg.pkg("PKG_SHELL.*", BtPathname)
reg.sys("PKG_SHLIBTOOL", BtPathname)
+ reg.pkglist("PKG_SKIP_REASON", BtShellWord)
// The special exception for buildlink3.mk is only here because
// of textproc/xmlcatmgr.
reg.acl("PKG_SYSCONFDIR*", BtPathname,
@@ -1447,7 +1457,7 @@ func (reg *VarTypeRegistry) Init(src *Pkgsrc) {
reg.pkglist("PKG_USERS_VARS", BtVariableName)
reg.pkg("PKG_USE_KERBEROS", BtYes)
reg.pkgload("PLIST.*", BtYes)
- reg.pkglist("PLIST_VARS", BtIdentifier)
+ reg.pkgloadlist("PLIST_VARS", BtIdentifier)
reg.pkglist("PLIST_SRC", BtRelativePkgPath)
reg.pkglist("PLIST_SUBST", BtShellWord)
reg.pkg("PLIST_TYPE", enum("dynamic static"))