Sat Dec 14 18:04:16 2019 UTC ()
pkgtools/pkglint: update to 19.3.18

Changes since 19.3.17:

The SUBST check has been completely rewritten. It can handle several
SUBST classes at the same time now. This reduces the number of wrong
warnings.


(rillig)
diff -r1.618 -r1.619 pkgsrc/pkgtools/pkglint/Makefile
diff -r1.2 -r1.3 pkgsrc/pkgtools/pkglint/files/mkcondchecker_test.go
diff -r1.33 -r1.34 pkgsrc/pkgtools/pkglint/files/substcontext.go
diff -r1.32 -r1.33 pkgsrc/pkgtools/pkglint/files/substcontext_test.go
diff -r1.83 -r1.84 pkgsrc/pkgtools/pkglint/files/vardefs.go
diff -r1.43 -r1.44 pkgsrc/pkgtools/pkglint/files/vartype.go
diff -r1.73 -r1.74 pkgsrc/pkgtools/pkglint/files/vartypecheck.go
diff -r1.66 -r1.67 pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go

cvs diff -r1.618 -r1.619 pkgsrc/pkgtools/pkglint/Makefile (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/Makefile 2019/12/13 07:44:02 1.618
+++ pkgsrc/pkgtools/pkglint/Makefile 2019/12/14 18:04:15 1.619
@@ -1,17 +1,16 @@ @@ -1,17 +1,16 @@
1# $NetBSD: Makefile,v 1.618 2019/12/13 07:44:02 bsiegert Exp $ 1# $NetBSD: Makefile,v 1.619 2019/12/14 18:04:15 rillig Exp $
2 2
3PKGNAME= pkglint-19.3.17 3PKGNAME= pkglint-19.3.18
4PKGREVISION= 1 
5CATEGORIES= pkgtools 4CATEGORIES= pkgtools
6DISTNAME= tools 5DISTNAME= tools
7MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/} 6MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/}
8GITHUB_PROJECT= tools 7GITHUB_PROJECT= tools
9GITHUB_TAG= 92d8274bd7b8a4c65f24bafe401a029e58392704 8GITHUB_TAG= 92d8274bd7b8a4c65f24bafe401a029e58392704
10 9
11MAINTAINER= rillig@NetBSD.org 10MAINTAINER= rillig@NetBSD.org
12HOMEPAGE= https://github.com/rillig/pkglint 11HOMEPAGE= https://github.com/rillig/pkglint
13COMMENT= Verifier for NetBSD packages 12COMMENT= Verifier for NetBSD packages
14LICENSE= 2-clause-bsd 13LICENSE= 2-clause-bsd
15CONFLICTS+= pkglint4-[0-9]* 14CONFLICTS+= pkglint4-[0-9]*
16 15
17USE_TOOLS+= pax 16USE_TOOLS+= pax

cvs diff -r1.2 -r1.3 pkgsrc/pkgtools/pkglint/files/Attic/mkcondchecker_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/mkcondchecker_test.go 2019/12/08 22:03:38 1.2
+++ pkgsrc/pkgtools/pkglint/files/Attic/mkcondchecker_test.go 2019/12/14 18:04:15 1.3
@@ -63,28 +63,27 @@ func (s *Suite) Test_MkCondChecker_Check @@ -63,28 +63,27 @@ func (s *Suite) Test_MkCondChecker_Check
63 "ERROR: filename.mk:4: Use ${PKGSRC_COMPILER:Mmsvc} instead of the == operator.") 63 "ERROR: filename.mk:4: Use ${PKGSRC_COMPILER:Mmsvc} instead of the == operator.")
64 64
65 // PKG_LIBTOOL is only available after including bsd.pkg.mk, 65 // PKG_LIBTOOL is only available after including bsd.pkg.mk,
66 // therefore the :U and the subsequent warning. 66 // therefore the :U and the subsequent warning.
67 test(".if ${PKG_LIBTOOL:U:Mlibtool}", 67 test(".if ${PKG_LIBTOOL:U:Mlibtool}",
68 "NOTE: filename.mk:4: PKG_LIBTOOL can be "+ 68 "NOTE: filename.mk:4: PKG_LIBTOOL can be "+
69 "compared using the simpler \"${PKG_LIBTOOL:U} == libtool\" "+ 69 "compared using the simpler \"${PKG_LIBTOOL:U} == libtool\" "+
70 "instead of matching against \":Mlibtool\".", 70 "instead of matching against \":Mlibtool\".",
71 "WARN: filename.mk:4: PKG_LIBTOOL should not be used at load time in any file.") 71 "WARN: filename.mk:4: PKG_LIBTOOL should not be used at load time in any file.")
72 72
73 test(".if ${MACHINE_PLATFORM:MUnknownOS-*-*} || ${MACHINE_ARCH:Mx86}", 73 test(".if ${MACHINE_PLATFORM:MUnknownOS-*-*} || ${MACHINE_ARCH:Mx86}",
74 "WARN: filename.mk:4: "+ 74 "WARN: filename.mk:4: "+
75 "The pattern \"UnknownOS\" cannot match any of "+ 75 "The pattern \"UnknownOS\" cannot match any of "+
76 "{ AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD HPUX Haiku "+ 76 "{ Cygwin DragonFly FreeBSD Linux NetBSD SunOS "+
77 "IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare "+ 
78 "} for the operating system part of MACHINE_PLATFORM.", 77 "} for the operating system part of MACHINE_PLATFORM.",
79 "WARN: filename.mk:4: "+ 78 "WARN: filename.mk:4: "+
80 "The pattern \"x86\" cannot match any of "+ 79 "The pattern \"x86\" cannot match any of "+
81 "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 cobalt coldfire convex dreamcast earm "+ 80 "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 cobalt coldfire convex dreamcast earm "+
82 "earmeb earmhf earmhfeb earmv4 earmv4eb earmv5 earmv5eb earmv6 earmv6eb earmv6hf earmv6hfeb "+ 81 "earmeb earmhf earmhfeb earmv4 earmv4eb earmv5 earmv5eb earmv6 earmv6eb earmv6hf earmv6hfeb "+
83 "earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 i386 i586 i686 ia64 "+ 82 "earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 i386 i586 i686 ia64 "+
84 "m68000 m68k m88k mips mips64 mips64eb mips64el mipseb mipsel mipsn32 mlrisc ns32k pc532 pmax "+ 83 "m68000 m68k m88k mips mips64 mips64eb mips64el mipseb mipsel mipsn32 mlrisc ns32k pc532 pmax "+
85 "powerpc powerpc64 rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+ 84 "powerpc powerpc64 rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+
86 "} for MACHINE_ARCH.", 85 "} for MACHINE_ARCH.",
87 "NOTE: filename.mk:4: MACHINE_ARCH can be "+ 86 "NOTE: filename.mk:4: MACHINE_ARCH can be "+
88 "compared using the simpler \"${MACHINE_ARCH} == x86\" "+ 87 "compared using the simpler \"${MACHINE_ARCH} == x86\" "+
89 "instead of matching against \":Mx86\".") 88 "instead of matching against \":Mx86\".")
90 89

cvs diff -r1.33 -r1.34 pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go 2019/12/13 01:39:23 1.33
+++ pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go 2019/12/14 18:04:15 1.34
@@ -1,183 +1,416 @@ @@ -1,183 +1,416 @@
1package pkglint 1package pkglint
2 2
3import "netbsd.org/pkglint/textproc" 3import "netbsd.org/pkglint/textproc"
4 4
5// SubstContext records the state of a block of variable assignments 5// SubstContext records the state of a block of variable assignments
6// that make up a SUBST class (see `mk/subst.mk`). 6// that make up a SUBST class.
 7//
 8// See mk/subst.mk.
7type SubstContext struct { 9type SubstContext struct {
8 queuedIds []string 10 active *substBlock
9 id string 
10 doneIds map[string]bool 
11 11
12 foreignAllowed map[string]struct{} 12 scopes []*substScope
13 foreign []*MkLine 
14 
15 conds []*substCond 
16 13
17 once Once 14 once Once
18} 15}
19 16
20func NewSubstContext() *SubstContext { 17func NewSubstContext() *SubstContext {
21 ctx := SubstContext{} 18 return &SubstContext{scopes: []*substScope{newSubstScope()}}
22 ctx.reset() 
23 return &ctx 
24} 19}
25 20
26func (ctx *SubstContext) Process(mkline *MkLine) { 21func (ctx *SubstContext) Process(mkline *MkLine) {
27 switch { 22 switch {
28 case mkline.IsEmpty(): 23 case mkline.IsEmpty():
29 ctx.finishClass(mkline) 24 ctx.emptyLine()
30 case mkline.IsVarassign(): 25 case mkline.IsVarassign():
31 ctx.varassign(mkline) 26 ctx.varassign(mkline)
32 case mkline.IsDirective(): 27 case mkline.IsDirective():
33 ctx.directive(mkline) 28 ctx.directive(mkline)
34 } 29 }
35} 30}
36 31
37func (ctx *SubstContext) Finish(diag Diagnoser) { 32func (ctx *SubstContext) Finish(diag Diagnoser) {
38 ctx.finishClass(diag) 33 // Prevent panics on unbalanced conditionals.
39 ctx.finishFile(diag) 34 for len(ctx.scopes) > 1 {
 35 ctx.leave(diag)
 36 }
 37
 38 for _, scope := range ctx.scopes {
 39 scope.finish(diag)
 40 }
 41}
 42
 43func (ctx *SubstContext) emptyLine() {
 44 for _, scope := range ctx.scopes {
 45 scope.emptyLine()
 46 }
40} 47}
41 48
42func (ctx *SubstContext) varassign(mkline *MkLine) { 49func (ctx *SubstContext) varassign(mkline *MkLine) {
43 varcanon := mkline.Varcanon() 50 varcanon := mkline.Varcanon()
44 if varcanon == "SUBST_CLASSES" || varcanon == "SUBST_CLASSES.*" { 51 if varcanon == "SUBST_CLASSES" || varcanon == "SUBST_CLASSES.*" {
45 ctx.varassignClasses(mkline) 52 ctx.varassignClasses(mkline)
46 return 53 return
47 } 54 }
48 55
49 if ctx.isForeign(mkline.Varcanon()) { 56 if ctx.isForeign(mkline.Varcanon()) {
50 if ctx.isActive() { 57 if ctx.isActive() && !ctx.block().seenEmpty {
51 ctx.rememberForeign(mkline) 58 ctx.block().rememberForeign(mkline)
52 } 59 }
53 return 60 return
54 } 61 }
55 62
56 if !ctx.isActive() { 63 if !ctx.isActive() {
57 if !ctx.varassignOutsideBlock(mkline) { 64 if !ctx.varassignOutsideBlock(mkline) {
58 return 65 return
59 } 66 }
60 } 67 }
61 68
62 if hasPrefix(mkline.Varname(), "SUBST_") && !ctx.isActiveId(mkline.Varparam()) { 69 if hasPrefix(mkline.Varname(), "SUBST_") && mkline.Varparam() != ctx.activeId() {
63 if !ctx.varassignDifferentClass(mkline) { 70 if !ctx.varassignDifferentClass(mkline) {
64 return 71 return
65 } 72 }
66 } 73 }
67 74
 75 block := ctx.block()
68 switch varcanon { 76 switch varcanon {
69 case "SUBST_STAGE.*": 77 case "SUBST_STAGE.*":
70 ctx.varassignStage(mkline) 78 block.varassignStage(mkline)
71 case "SUBST_MESSAGE.*": 79 case "SUBST_MESSAGE.*":
72 ctx.varassignMessages(mkline) 80 block.varassignMessages(mkline)
73 case "SUBST_FILES.*": 81 case "SUBST_FILES.*":
74 ctx.varassignFiles(mkline) 82 block.varassignFiles(mkline)
75 case "SUBST_SED.*": 83 case "SUBST_SED.*":
76 ctx.varassignSed(mkline) 84 block.varassignSed(mkline)
77 case "SUBST_VARS.*": 85 case "SUBST_VARS.*":
78 ctx.varassignVars(mkline) 86 block.varassignVars(mkline)
79 case "SUBST_FILTER_CMD.*": 87 case "SUBST_FILTER_CMD.*":
80 ctx.varassignFilterCmd(mkline) 88 block.varassignFilterCmd(mkline)
81 } 89 }
82} 90}
83 91
84func (ctx *SubstContext) varassignClasses(mkline *MkLine) { 92func (ctx *SubstContext) varassignClasses(mkline *MkLine) {
85 classes := mkline.ValueFields(mkline.WithoutMakeVariables(mkline.Value())) 93 ids := mkline.ValueFields(mkline.WithoutMakeVariables(mkline.Value()))
86 if len(classes) == 0 { 94 if len(ids) == 0 {
87 return 95 return
88 } 96 }
89 97
90 if len(classes) > 1 { 98 if len(ids) > 1 {
91 mkline.Notef("Please add only one class at a time to SUBST_CLASSES.") 99 mkline.Notef("Please add only one class at a time to SUBST_CLASSES.")
92 mkline.Explain( 100 mkline.Explain(
93 "This way, each substitution class forms a block in the package Makefile,", 101 "This way, each substitution class forms a block in the package Makefile,",
94 "and to delete this block, it is not necessary to look anywhere else.") 102 "and to delete this block, it is not necessary to look anywhere else.")
95 } 103 }
96 for _, class := range classes { 
97 ctx.queue(class) 
98 } 
99 104
100 id := classes[0] 105 ctx.prepareSubstClasses(mkline)
101 if ctx.isActive() && !ctx.isActiveId(id) { 106 ctx.deactivate(mkline)
102 id := ctx.activeId() // since ctx.condEndif may reset it 
103 107
104 for ctx.isConditional() { 108 for _, id := range ids {
105 // This will be confusing for the outer SUBST block, 109 if ctx.lookup(id) == nil {
106 // but since that block is assumed to be finished, 110 ctx.scopes[len(ctx.scopes)-1].define(id)
107 // this doesn't matter. 111 } else if mkline.Varparam() == "" {
108 ctx.condEndif(mkline) 112 mkline.Errorf("Duplicate SUBST class %q.", id)
109 } 
110 
111 complete := ctx.isComplete() // since ctx.finishClass will reset it 
112 ctx.finishClass(mkline) 
113 if !complete { 
114 mkline.Warnf("Subst block %q should be finished before adding the next class to SUBST_CLASSES.", id) 
115 } 113 }
116 } 114 }
 115}
117 116
118 ctx.setActiveId(id) 117func (ctx *SubstContext) prepareSubstClasses(mkline *MkLine) {
119 118 for _, scope := range ctx.scopes {
120 return 119 scope.prepareSubstClasses(mkline)
 120 }
121} 121}
122 122
123// varassignOutsideBlock handles variable assignments of SUBST variables that 123// varassignOutsideBlock handles variable assignments of SUBST variables that
124// appear without a directly corresponding SUBST block. 124// appear without a directly corresponding SUBST block.
125func (ctx *SubstContext) varassignOutsideBlock(mkline *MkLine) (continueWithNewId bool) { 125func (ctx *SubstContext) varassignOutsideBlock(mkline *MkLine) (continueWithNewId bool) {
126 varparam := mkline.Varparam() 126 id := mkline.Varparam()
127 127
128 if ctx.isListCanon(mkline.Varcanon()) && ctx.isDone(varparam) { 128 if id != "" && ctx.isListCanon(mkline.Varcanon()) && ctx.isDone(id) {
129 if mkline.Op() != opAssignAppend { 129 if mkline.Op() != opAssignAppend {
130 mkline.Warnf("Late additions to a SUBST variable should use the += operator.") 130 mkline.Warnf("Late additions to a SUBST variable should use the += operator.")
131 } 131 }
132 return 132 return
133 } 133 }
134 if containsWord(mkline.Rationale(), varparam) { 134
135 return 135 return ctx.activate(mkline, ctx.lookup(id) == nil)
 136}
 137
 138func (ctx *SubstContext) varassignDifferentClass(mkline *MkLine) (ok bool) {
 139 varname := mkline.Varname()
 140 unknown := ctx.lookup(mkline.Varparam()) == nil
 141 if unknown && ctx.isActive() && !ctx.block().isComplete() {
 142 mkline.Warnf("Variable %q does not match SUBST class %q.",
 143 varname, ctx.activeId())
 144 if !ctx.block().seenEmpty {
 145 return false
 146 }
 147 }
 148
 149 return ctx.activate(mkline, unknown)
 150}
 151
 152func (ctx *SubstContext) directive(mkline *MkLine) {
 153 dir := mkline.Directive()
 154 switch dir {
 155 case "if":
 156 ctx.enter()
 157 case "elif":
 158 ctx.nextBranch(mkline, false)
 159 case "else":
 160 ctx.nextBranch(mkline, true)
 161 case "endif":
 162 ctx.leave(mkline)
 163 }
 164}
 165
 166func (ctx *SubstContext) enter() {
 167 for _, scope := range ctx.scopes {
 168 scope.enter()
 169 }
 170 ctx.scopes = append(ctx.scopes, newSubstScope())
 171}
 172
 173func (ctx *SubstContext) nextBranch(diag Diagnoser, isElse bool) {
 174 if ctx.isActive() && !ctx.block().isConditional() {
 175 ctx.deactivate(diag)
 176 }
 177
 178 for _, scope := range ctx.scopes {
 179 scope.nextBranch(diag, isElse)
 180 }
 181}
 182
 183func (ctx *SubstContext) leave(diag Diagnoser) {
 184 ctx.deactivate(diag)
 185
 186 for _, scope := range ctx.scopes {
 187 scope.leave(diag)
136 } 188 }
137 189
138 if ctx.start(varparam) { 190 if len(ctx.scopes) > 1 {
 191 ctx.scopes = ctx.scopes[:len(ctx.scopes)-1]
 192 }
 193}
 194
 195func (ctx *SubstContext) activate(mkline *MkLine, deactivate bool) bool {
 196 id := mkline.Varparam()
 197 if id == "" {
 198 mkline.Errorf("Invalid SUBST class %q in variable name.", id)
 199 return false
 200 }
 201
 202 if deactivate {
 203 ctx.deactivate(mkline)
 204 }
 205
 206 if block := ctx.lookup(id); block != nil {
 207 ctx.active = block
139 return true 208 return true
140 } 209 }
141 210
142 if ctx.once.FirstTime(varparam) { 211 if ctx.once.FirstTime(id) && !containsWord(mkline.Rationale(), id) {
143 mkline.Warnf("Before defining %s, the SUBST class "+ 212 mkline.Warnf("Before defining %s, the SUBST class "+
144 "should be declared using \"SUBST_CLASSES+= %s\".", 213 "should be declared using \"SUBST_CLASSES+= %s\".",
145 mkline.Varname(), varparam) 214 mkline.Varname(), id)
146 } 215 }
147 return 216 return false
148} 217}
149 218
150func (ctx *SubstContext) varassignDifferentClass(mkline *MkLine) (ok bool) { 219func (ctx *SubstContext) deactivate(diag Diagnoser) {
151 varname := mkline.Varname() 220 if !ctx.isActive() {
152 varparam := mkline.Varparam() 221 return
 222 }
153 223
154 if !ctx.isComplete() { 224 block := ctx.block()
155 mkline.Warnf("Variable %q does not match SUBST class %q.", varname, ctx.activeId()) 225 if !block.isConditional() {
 226 block.finish(diag)
 227 }
 228 ctx.active = nil
 229}
 230
 231func (*SubstContext) isForeign(varcanon string) bool {
 232 switch varcanon {
 233 case
 234 "SUBST_STAGE.*",
 235 "SUBST_MESSAGE.*",
 236 "SUBST_FILES.*",
 237 "SUBST_SED.*",
 238 "SUBST_VARS.*",
 239 "SUBST_FILTER_CMD.*":
156 return false 240 return false
157 } 241 }
 242 return true
 243}
 244
 245func (*SubstContext) isListCanon(varcanon string) bool {
 246 switch varcanon {
 247 case
 248 "SUBST_FILES.*",
 249 "SUBST_SED.*",
 250 "SUBST_VARS.*":
 251 return true
 252 }
 253 return false
 254}
158 255
159 ctx.finishClass(mkline) 256func (ctx *SubstContext) block() *substBlock {
 257 assertNotNil(ctx.active)
 258 return ctx.active
 259}
160 260
161 ctx.start(varparam) 261func (ctx *SubstContext) lookup(id string) *substBlock {
162 return true 262 for i := len(ctx.scopes) - 1; i >= 0; i-- {
 263 if def := ctx.scopes[i].def(id); def != nil {
 264 return def
 265 }
 266 }
 267 return nil
 268}
 269
 270func (ctx *SubstContext) isDone(id string) bool {
 271 for _, scope := range ctx.scopes {
 272 if scope.isDone(id) {
 273 return true
 274 }
 275 }
 276 return false
 277}
 278
 279func (ctx *SubstContext) isActive() bool { return ctx.active != nil }
 280
 281func (ctx *SubstContext) activeId() string {
 282 assert(ctx.isActive())
 283 return ctx.active.id
 284}
 285
 286type substScope struct {
 287 defs []*substBlock
 288}
 289
 290func newSubstScope() *substScope {
 291 return &substScope{nil}
 292}
 293
 294func (s *substScope) def(id string) *substBlock {
 295 for _, def := range s.defs {
 296 if def.id == id {
 297 return def
 298 }
 299 }
 300 return nil
 301}
 302
 303func (s *substScope) define(id string) {
 304 assert(s.def(id) == nil)
 305 s.defs = append(s.defs, newSubstBlock(id))
 306}
 307
 308func (s *substScope) isDone(id string) bool {
 309 def := s.def(id)
 310 return def != nil && def.done
163} 311}
164 312
165func (ctx *SubstContext) varassignStage(mkline *MkLine) { 313func (s *substScope) emptyLine() {
166 if ctx.isConditional() { 314 for _, block := range s.defs {
 315 block.seenEmpty = true
 316 }
 317}
 318
 319// finish brings all blocks that are defined in the current scope
 320// to an end.
 321func (s *substScope) finish(diag Diagnoser) {
 322 for _, block := range s.defs {
 323 block.finish(diag)
 324 }
 325}
 326
 327func (s *substScope) prepareSubstClasses(diag Diagnoser) {
 328 for _, block := range s.defs {
 329 if block.hasStarted() && !block.isComplete() {
 330 diag.Warnf("Subst block %q should be finished "+
 331 "before adding the next class to SUBST_CLASSES.",
 332 block.id)
 333 }
 334 }
 335}
 336
 337func (s *substScope) enter() {
 338 for _, block := range s.defs {
 339 block.enter()
 340 }
 341}
 342
 343func (s *substScope) nextBranch(diag Diagnoser, isElse bool) {
 344 for _, block := range s.defs {
 345 if block.isConditional() {
 346 block.nextBranch(isElse)
 347 } else {
 348 block.finish(diag)
 349 }
 350 }
 351}
 352
 353func (s *substScope) leave(diag Diagnoser) {
 354 for _, block := range s.defs {
 355 if block.isConditional() {
 356 block.leave()
 357 } else {
 358 block.finish(diag)
 359 }
 360 }
 361}
 362
 363type substBlock struct {
 364 id string
 365
 366 // Records which of the SUBST variables have been seen either
 367 // directly or in a conditional branch.
 368 conds []*substCond
 369
 370 // In the paragraph of a SUBST block, there should be only variables
 371 // that actually belong to the SUBST block.
 372 //
 373 // In addition, variables that are mentioned in SUBST_VARS may also
 374 // be defined there because they closely relate to the SUBST block.
 375 foreignAllowed map[string]struct{}
 376 foreign []*MkLine
 377
 378 // Whether there has been an empty line between the SUBST_CLASSES
 379 // line and the current line.
 380 //
 381 // Before the empty line, variables that don't obviously belong to
 382 // this SUBST block generate warnings since they may be typos,
 383 // such as for a different SUBST block.
 384 seenEmpty bool
 385
 386 // Whether the SUBST_CLASSES has already gone out of scope.
 387 //
 388 // XXX: When it is out of scope, it should also be unreachable
 389 // by any pkglint code. There's something inconsistent here.
 390 done bool
 391}
 392
 393func newSubstBlock(id string) *substBlock {
 394 assert(id != "")
 395 return &substBlock{id: id, conds: []*substCond{newSubstCond()}}
 396}
 397
 398func (b *substBlock) varassignStage(mkline *MkLine) {
 399 if b.isConditional() {
167 mkline.Warnf("%s should not be defined conditionally.", mkline.Varname()) 400 mkline.Warnf("%s should not be defined conditionally.", mkline.Varname())
168 } 401 }
169 402
170 ctx.dupString(mkline, ssStage) 403 b.dupString(mkline, ssStage)
171 404
172 value := mkline.Value() 405 value := mkline.Value()
173 if value == "pre-patch" || value == "post-patch" { 406 if value == "pre-patch" || value == "post-patch" {
174 fix := mkline.Autofix() 407 fix := mkline.Autofix()
175 fix.Warnf("Substitutions should not happen in the patch phase.") 408 fix.Warnf("Substitutions should not happen in the patch phase.")
176 fix.Explain( 409 fix.Explain(
177 "Performing substitutions during post-patch breaks tools such as", 410 "Performing substitutions during post-patch breaks tools such as",
178 "mkpatches, making it very difficult to regenerate correct patches", 411 "mkpatches, making it very difficult to regenerate correct patches",
179 "after making changes, and often leading to substituted string", 412 "after making changes, and often leading to substituted string",
180 "replacements being committed.", 413 "replacements being committed.",
181 "", 414 "",
182 "Instead of pre-patch, use post-extract.", 415 "Instead of pre-patch, use post-extract.",
183 "Instead of post-patch, use pre-configure.") 416 "Instead of post-patch, use pre-configure.")
@@ -186,128 +419,129 @@ func (ctx *SubstContext) varassignStage( @@ -186,128 +419,129 @@ func (ctx *SubstContext) varassignStage(
186 fix.Apply() 419 fix.Apply()
187 } 420 }
188 421
189 if G.Pkg != nil && (value == "pre-configure" || value == "post-configure") { 422 if G.Pkg != nil && (value == "pre-configure" || value == "post-configure") {
190 if noConfigureLine := G.Pkg.vars.FirstDefinition("NO_CONFIGURE"); noConfigureLine != nil { 423 if noConfigureLine := G.Pkg.vars.FirstDefinition("NO_CONFIGURE"); noConfigureLine != nil {
191 mkline.Warnf("SUBST_STAGE %s has no effect when NO_CONFIGURE is set (in %s).", 424 mkline.Warnf("SUBST_STAGE %s has no effect when NO_CONFIGURE is set (in %s).",
192 value, mkline.RelMkLine(noConfigureLine)) 425 value, mkline.RelMkLine(noConfigureLine))
193 mkline.Explain( 426 mkline.Explain(
194 "To fix this properly, remove the definition of NO_CONFIGURE.") 427 "To fix this properly, remove the definition of NO_CONFIGURE.")
195 } 428 }
196 } 429 }
197} 430}
198 431
199func (ctx *SubstContext) varassignMessages(mkline *MkLine) { 432func (b *substBlock) varassignMessages(mkline *MkLine) {
200 varname := mkline.Varname() 433 varname := mkline.Varname()
201 434
202 if ctx.isConditional() { 435 if b.isConditional() {
203 mkline.Warnf("%s should not be defined conditionally.", varname) 436 mkline.Warnf("%s should not be defined conditionally.", varname)
204 } 437 }
205 438
206 ctx.dupString(mkline, ssMessage) 439 b.dupString(mkline, ssMessage)
207} 440}
208 441
209func (ctx *SubstContext) varassignFiles(mkline *MkLine) { 442func (b *substBlock) varassignFiles(mkline *MkLine) {
210 ctx.dupList(mkline, ssFiles, ssNone) 443 b.dupList(mkline, ssFiles, ssNone)
211} 444}
212 445
213func (ctx *SubstContext) varassignSed(mkline *MkLine) { 446func (b *substBlock) varassignSed(mkline *MkLine) {
214 ctx.dupList(mkline, ssSed, ssNone) 447 b.dupList(mkline, ssSed, ssNone)
215 ctx.seen().set(ssTransform) 448 b.addSeen(ssTransform)
216 449
217 ctx.suggestSubstVars(mkline) 450 b.suggestSubstVars(mkline)
218} 451}
219 452
220func (ctx *SubstContext) varassignVars(mkline *MkLine) { 453func (b *substBlock) varassignVars(mkline *MkLine) {
221 ctx.dupList(mkline, ssVars, ssVarsAutofix) 454 b.dupList(mkline, ssVars, ssVarsAutofix)
222 ctx.seen().set(ssTransform) 455 b.addSeen(ssTransform)
223 456
224 for _, substVar := range mkline.Fields() { 457 for _, substVar := range mkline.Fields() {
225 ctx.allowVar(substVar) 458 b.allowVar(substVar)
226 } 459 }
227} 460}
228 461
229func (ctx *SubstContext) varassignFilterCmd(mkline *MkLine) { 462func (b *substBlock) varassignFilterCmd(mkline *MkLine) {
230 ctx.dupString(mkline, ssFilterCmd) 463 b.dupString(mkline, ssFilterCmd)
231 ctx.seen().set(ssTransform) 464 b.addSeen(ssTransform)
232} 465}
233 466
234func (ctx *SubstContext) dupList(mkline *MkLine, part substSeen, autofixPart substSeen) { 467func (b *substBlock) suggestSubstVars(mkline *MkLine) {
235 if ctx.seenInBranch(part) && mkline.Op() != opAssignAppend { 
236 ctx.fixOperatorAppend(mkline, ctx.seenInBranch(autofixPart)) 
237 } 
238 ctx.seen().set(part) 
239} 
240 
241func (ctx *SubstContext) dupString(mkline *MkLine, part substSeen) { 
242 if ctx.seenInBranch(part) { 
243 mkline.Warnf("Duplicate definition of %q.", mkline.Varname()) 
244 } 
245 ctx.seen().set(part) 
246} 
247 
248func (ctx *SubstContext) fixOperatorAppend(mkline *MkLine, dueToAutofix bool) { 
249 before := mkline.ValueAlign() 
250 after := alignWith(mkline.Varname()+"+=", before) 
251 
252 fix := mkline.Autofix() 
253 if dueToAutofix { 
254 fix.Notef(SilentAutofixFormat) 
255 } else { 
256 fix.Warnf("All but the first assignment to %q should use the \"+=\" operator.", 
257 mkline.Varname()) 
258 } 
259 fix.Replace(before, after) 
260 fix.Apply() 
261} 
262 
263func (ctx *SubstContext) suggestSubstVars(mkline *MkLine) { 
264 468
265 tokens, _ := splitIntoShellTokens(mkline.Line, mkline.Value()) 469 tokens, _ := splitIntoShellTokens(mkline.Line, mkline.Value())
266 for _, token := range tokens { 470 for _, token := range tokens {
267 varname := ctx.extractVarname(mkline.UnquoteShell(token, false)) 471 varname := b.extractVarname(mkline.UnquoteShell(token, false))
268 if varname == "" { 472 if varname == "" {
269 continue 473 continue
270 } 474 }
271 475
272 id := ctx.activeId() 476 id := b.id
273 varop := sprintf("SUBST_VARS.%s%s%s", 477 varop := sprintf("SUBST_VARS.%s%s%s",
274 id, 478 id,
275 condStr(hasSuffix(id, "+"), " ", ""), 479 condStr(hasSuffix(id, "+"), " ", ""),
276 condStr(ctx.seenInBranch(ssVars), "+=", "=")) 480 condStr(b.hasSeen(ssVars), "+=", "="))
277 481
278 fix := mkline.Autofix() 482 fix := mkline.Autofix()
279 fix.Notef("The substitution command %q can be replaced with \"%s %s\".", 483 fix.Notef("The substitution command %q can be replaced with \"%s %s\".",
280 token, varop, varname) 484 token, varop, varname)
281 fix.Explain( 485 fix.Explain(
282 "Replacing @VAR@ with ${VAR} is such a typical pattern that pkgsrc has built-in support for it,", 486 "Replacing @VAR@ with ${VAR} is such a typical pattern that pkgsrc has built-in support for it,",
283 "requiring only the variable name instead of the full sed command.") 487 "requiring only the variable name instead of the full sed command.")
284 if !mkline.HasComment() && len(tokens) == 2 && tokens[0] == "-e" { 488 if !mkline.HasComment() && len(tokens) == 2 && tokens[0] == "-e" {
285 fix.Replace(mkline.Text, alignWith(varop, mkline.ValueAlign())+varname) 489 fix.Replace(mkline.Text, alignWith(varop, mkline.ValueAlign())+varname)
286 } 490 }
287 fix.Apply() 491 fix.Apply()
288 492
289 // At this point the number of SUBST_SED assignments is one 493 // At this point the number of SUBST_SED assignments is one
290 // less than before. Therefore it is possible to adjust the 494 // less than before. Therefore it is possible to adjust the
291 // assignment operators on them. It's probably not worth the 495 // assignment operators on them. It's probably not worth the
292 // effort, though. 496 // effort, though.
293 497
294 ctx.seen().set(ssVars | ssVarsAutofix) 498 b.addSeen(ssVars)
 499 b.addSeen(ssVarsAutofix)
 500 }
 501}
 502
 503func (b *substBlock) dupString(mkline *MkLine, part substSeen) {
 504 if b.hasSeen(part) {
 505 mkline.Warnf("Duplicate definition of %q.", mkline.Varname())
 506 }
 507 b.addSeen(part)
 508}
 509
 510func (b *substBlock) dupList(mkline *MkLine, part substSeen, autofixPart substSeen) {
 511 if b.hasSeenAnywhere(part) && mkline.Op() != opAssignAppend {
 512 b.fixOperatorAppend(mkline, b.hasSeen(autofixPart))
 513 }
 514 b.addSeen(part)
 515}
 516
 517func (b *substBlock) fixOperatorAppend(mkline *MkLine, dueToAutofix bool) {
 518 before := mkline.ValueAlign()
 519 after := alignWith(mkline.Varname()+"+=", before)
 520
 521 fix := mkline.Autofix()
 522 if dueToAutofix {
 523 fix.Notef(SilentAutofixFormat)
 524 } else {
 525 fix.Warnf("All but the first assignment to %q should use the \"+=\" operator.",
 526 mkline.Varname())
295 } 527 }
 528 fix.Replace(before, after)
 529 fix.Apply()
296} 530}
297 531
298// extractVarname extracts the variable name from a sed command of the form 532// extractVarname extracts the variable name from a sed command of the form
299// s,@VARNAME@,${VARNAME}, and some related variants thereof. 533// s,@VARNAME@,${VARNAME}, and some related variants thereof.
300func (*SubstContext) extractVarname(token string) string { 534func (*substBlock) extractVarname(token string) string {
301 parser := NewMkLexer(token, nil) 535 parser := NewMkLexer(token, nil)
302 lexer := parser.lexer 536 lexer := parser.lexer
303 if !lexer.SkipByte('s') { 537 if !lexer.SkipByte('s') {
304 return "" 538 return ""
305 } 539 }
306 540
307 separator := lexer.NextByteSet(textproc.XPrint) // Really any character works 541 separator := lexer.NextByteSet(textproc.XPrint) // Really any character works
308 if separator == -1 { 542 if separator == -1 {
309 return "" 543 return ""
310 } 544 }
311 545
312 if !lexer.SkipByte('@') { 546 if !lexer.SkipByte('@') {
313 return "" 547 return ""
@@ -327,298 +561,209 @@ func (*SubstContext) extractVarname(toke @@ -327,298 +561,209 @@ func (*SubstContext) extractVarname(toke
327 case "", ":Q": 561 case "", ":Q":
328 break 562 break
329 default: 563 default:
330 return "" 564 return ""
331 } 565 }
332 566
333 if !lexer.SkipByte(byte(separator)) { 567 if !lexer.SkipByte(byte(separator)) {
334 return "" 568 return ""
335 } 569 }
336 570
337 return varname 571 return varname
338} 572}
339 573
340func (*SubstContext) isForeign(varcanon string) bool { 574func (b *substBlock) isComplete() bool {
341 switch varcanon { 575 return b.allSeen().hasAll(ssStage | ssFiles | ssTransform)
342 case 
343 "SUBST_STAGE.*", 
344 "SUBST_MESSAGE.*", 
345 "SUBST_FILES.*", 
346 "SUBST_SED.*", 
347 "SUBST_VARS.*", 
348 "SUBST_FILTER_CMD.*": 
349 return false 
350 } 
351 return true 
352} 576}
353 577
354func (*SubstContext) isListCanon(varcanon string) bool { 578func (b *substBlock) hasSeen(part substSeen) bool {
355 switch varcanon { 579 for _, cond := range b.conds {
356 case 580 if cond.hasSeen(part) {
357 "SUBST_FILES.*", 581 return true
358 "SUBST_SED.*", 582 }
359 "SUBST_VARS.*": 
360 return true 
361 } 583 }
362 return false 584 return false
363} 585}
364 586
365func (ctx *SubstContext) directive(mkline *MkLine) { 587func (b *substBlock) hasSeenAnywhere(part substSeen) bool {
366 dir := mkline.Directive() 588 for _, cond := range b.conds {
367 switch dir { 589 if cond.hasSeenAnywhere(part) {
368 case "if": 590 return true
369 ctx.condIf() 591 }
370 
371 case "elif", "else": 
372 ctx.condElse(mkline, dir) 
373 
374 case "endif": 
375 ctx.condEndif(mkline) 
376 } 592 }
 593 return false
377} 594}
378 595
379func (ctx *SubstContext) condIf() { 596func (b *substBlock) allSeen() substSeen {
380 top := substCond{total: ssAll} 597 all := ssNone
381 ctx.conds = append(ctx.conds, &top) 598 for _, cond := range b.conds {
 599 all.addAll(cond.curr)
 600 }
 601 return all
382} 602}
383 603
384func (ctx *SubstContext) condElse(mkline *MkLine, dir string) { 604func (b *substBlock) addSeen(part substSeen) {
385 top := ctx.cond() 605 b.conds[len(b.conds)-1].addSeen(part)
386 top.total.retain(top.curr) 
387 if !ctx.isConditional() { 
388 ctx.finishClass(mkline) 
389 } 
390 top.curr = ssNone 
391 top.seenElse = dir == "else" 
392} 606}
393 607
394func (ctx *SubstContext) condEndif(diag Diagnoser) { 608func (b *substBlock) rememberForeign(mkline *MkLine) {
395 top := ctx.cond() 609 b.foreign = append(b.foreign, mkline)
396 top.total.retain(top.curr) 
397 if !ctx.isConditional() { 
398 ctx.finishClass(diag) 
399 } 
400 if !top.seenElse { 
401 top.total = ssNone 
402 } 
403 if len(ctx.conds) > 1 { 
404 ctx.conds = ctx.conds[:len(ctx.conds)-1] 
405 } 
406 ctx.seen().union(top.total) 
407} 610}
408 611
409func (ctx *SubstContext) finishClass(diag Diagnoser) { 612// isConditional returns whether the current line is at a deeper conditional
410 if !ctx.isActive() { 613// level than the corresponding SUBST_CLASSES line.
411 return 614func (b *substBlock) isConditional() bool { return len(b.conds) > 1 }
412 } 
413 615
414 if ctx.seen().get(ssAll) { 616func (b *substBlock) allowVar(varname string) {
415 ctx.checkBlockComplete(diag) 617 if b.foreignAllowed == nil {
416 ctx.checkForeignVariables() 618 b.foreignAllowed = map[string]struct{}{}
417 } else { 
418 ctx.markAsNotDone() 
419 } 619 }
420 620 b.foreignAllowed[varname] = struct{}{}
421 ctx.reset() 
422} 621}
423 622
424func (ctx *SubstContext) checkBlockComplete(diag Diagnoser) { 623func (b *substBlock) enter() { b.conds = append(b.conds, newSubstCond()) }
425 id := ctx.activeId() 
426 seen := ctx.seen() 
427 if !seen.get(ssStage) { 
428 diag.Warnf("Incomplete SUBST block: SUBST_STAGE.%s missing.", id) 
429 } 
430 if !seen.get(ssFiles) { 
431 diag.Warnf("Incomplete SUBST block: SUBST_FILES.%s missing.", id) 
432 } 
433 if !seen.get(ssTransform) { 
434 diag.Warnf("Incomplete SUBST block: SUBST_SED.%[1]s, SUBST_VARS.%[1]s or SUBST_FILTER_CMD.%[1]s missing.", id) 
435 } 
436} 
437 624
438func (ctx *SubstContext) checkForeignVariables() { 625func (b *substBlock) nextBranch(isElse bool) {
439 ctx.forEachForeignVar(func(mkline *MkLine) { 626 cond := b.conds[len(b.conds)-1]
440 mkline.Warnf("Foreign variable %q in SUBST block.", mkline.Varname()) 627 cond.leaveBranch()
441 }) 628 cond.enterBranch(isElse)
442} 629}
443 630
444func (ctx *SubstContext) finishFile(diag Diagnoser) { 631func (b *substBlock) leave() {
445 for _, id := range ctx.queuedIds { 632 assert(b.isConditional())
446 if id != "" && !ctx.isDone(id) { 
447 ctx.warnUndefinedBlock(diag, id) 
448 } 
449 } 
450} 
451 633
452func (*SubstContext) warnUndefinedBlock(diag Diagnoser, id string) { 634 n := len(b.conds)
453 diag.Warnf("Missing SUBST block for %q.", id) 635 b.conds[n-2].leaveLevel(b.conds[n-1])
454 diag.Explain( 636 b.conds = b.conds[:n-1]
455 "After adding a SUBST class to SUBST_CLASSES,", 
456 "the remaining SUBST variables should be defined in the same file.", 
457 "", 
458 "See mk/subst.mk for the comprehensive documentation.") 
459} 637}
460 638
461// In the paragraph of a SUBST block, there should be only variables 639func (b *substBlock) finish(diag Diagnoser) {
462// that actually belong to the SUBST block. 640 assert(len(b.conds) == 1)
463// 
464// In addition, variables that are mentioned in SUBST_VARS may also 
465// be defined there because they closely relate to the SUBST block. 
466 641
467func (ctx *SubstContext) allowVar(varname string) { 642 if b.done {
468 if ctx.foreignAllowed == nil { 643 return
469 ctx.foreignAllowed = make(map[string]struct{}) 
470 } 644 }
471 ctx.foreignAllowed[varname] = struct{}{} 645 b.done = true
472} 
473 646
474func (ctx *SubstContext) rememberForeign(mkline *MkLine) { 647 if !b.hasStarted() {
475 ctx.foreign = append(ctx.foreign, mkline) 648 diag.Warnf("Missing SUBST block for %q.", b.id)
476} 649 return
477 
478// forEachForeignVar performs the given action for each variable that 
479// is defined in the SUBST block and is not mentioned in SUBST_VARS. 
480func (ctx *SubstContext) forEachForeignVar(action func(*MkLine)) { 
481 for _, foreign := range ctx.foreign { 
482 if _, ok := ctx.foreignAllowed[foreign.Varname()]; !ok { 
483 action(foreign) 
484 } 
485 } 650 }
486} 
487 
488func (ctx *SubstContext) reset() { 
489 ctx.id = "" 
490 ctx.foreignAllowed = nil 
491 ctx.foreign = nil 
492 ctx.conds = []*substCond{{seenElse: true}} 
493} 
494 
495func (ctx *SubstContext) isActive() bool { return ctx.id != "" } 
496 
497func (ctx *SubstContext) isActiveId(id string) bool { return ctx.id == id } 
498 
499func (ctx *SubstContext) activeId() string { 
500 assert(ctx.isActive()) 
501 return ctx.id 
502} 
503 
504func (ctx *SubstContext) setActiveId(id string) { 
505 ctx.id = id 
506 ctx.cond().top = true 
507 ctx.markAsDone(id) 
508} 
509 651
510func (ctx *SubstContext) queue(id string) { 652 if !b.hasSeen(ssStage) {
511 ctx.queuedIds = append(ctx.queuedIds, id) 653 diag.Warnf("Incomplete SUBST block: SUBST_STAGE.%s missing.", b.id)
512} 
513 
514func (ctx *SubstContext) start(id string) bool { 
515 for i, queuedId := range ctx.queuedIds { 
516 if queuedId == id { 
517 ctx.queuedIds[i] = "" 
518 ctx.setActiveId(id) 
519 return true 
520 } 
521 } 654 }
522 return false 655 if !b.hasSeen(ssFiles) {
523} 656 diag.Warnf("Incomplete SUBST block: SUBST_FILES.%s missing.", b.id)
524 657 }
525func (ctx *SubstContext) markAsDone(id string) { 658 if !b.hasSeen(ssTransform) {
526 if ctx.doneIds == nil { 659 diag.Warnf(
527 ctx.doneIds = map[string]bool{} 660 "Incomplete SUBST block: SUBST_SED.%[1]s, "+
 661 "SUBST_VARS.%[1]s or SUBST_FILTER_CMD.%[1]s missing.",
 662 b.id)
528 } 663 }
529 ctx.doneIds[id] = true 
530} 
531 
532func (ctx *SubstContext) markAsNotDone() { 
533 ctx.doneIds[ctx.id] = false 
534} 
535 
536func (ctx *SubstContext) isDone(varparam string) bool { 
537 return ctx.doneIds[varparam] 
538} 
539 
540func (ctx *SubstContext) isComplete() bool { 
541 return ctx.seen().hasAll(ssStage | ssFiles | ssTransform) 
542} 
543 
544// isConditional returns whether the current line is at a deeper conditional 
545// level than the assignment where the corresponding class ID is added to 
546// SUBST_CLASSES. 
547// 
548// TODO: Adjust the implementation to this description. 
549func (ctx *SubstContext) isConditional() bool { 
550 return !ctx.cond().top 
551} 
552 664
553// cond returns information about the parts of the SUBST block that 665 b.checkForeignVariables()
554// have already been seen in the current leaf branch of the conditionals. 
555func (ctx *SubstContext) seen() *substSeen { 
556 return &ctx.cond().curr 
557} 666}
558 667
559// cond returns information about the current branch of conditionals. 668func (b *substBlock) checkForeignVariables() {
560func (ctx *SubstContext) cond() *substCond { 669 for _, mkline := range b.foreign {
561 return ctx.conds[len(ctx.conds)-1] 670 if _, ok := b.foreignAllowed[mkline.Varname()]; !ok {
 671 mkline.Warnf("Foreign variable %q in SUBST block.", mkline.Varname())
 672 }
 673 }
562} 674}
563 675
564// Returns true if the given flag from substSeen has been seen 676func (b *substBlock) hasStarted() bool {
565// somewhere in the conditional path of the current line. 677 for _, cond := range b.conds {
566func (ctx *SubstContext) seenInBranch(part substSeen) bool { 678 if cond.currAny != ssNone {
567 for _, cond := range ctx.conds { 
568 if cond.curr.get(part) { 
569 return true 679 return true
570 } 680 }
571 } 681 }
572 return false 682 return false
573} 683}
574 684
575type substCond struct { 685type substCond struct {
576 // Tells whether a SUBST block has started at this conditional level. 
577 // All variable assignments that belong to this class must happen at 
578 // this conditional level or below it. 
579 // 
580 // TODO: For Test_SubstContext_Directive__conditional_complete, 
581 // this needs to be changed to the set of classes that have been 
582 // added to SUBST_CLASSES at this level. 
583 top bool 
584 
585 // Collects the parts of the SUBST block that have been defined in all 686 // Collects the parts of the SUBST block that have been defined in all
586 // branches that have been parsed completely. 687 // branches that have been parsed completely.
587 total substSeen 688 total substSeen
 689 totalAny substSeen
588 690
589 // Collects the parts of the SUBST block that are defined in the current 691 // Collects the parts of the SUBST block that are defined in the current
590 // branch of the conditional. At the end of the branch, they are merged 692 // branch of the conditional. At the end of the branch, they are merged
591 // into the total. 693 // into the total.
592 curr substSeen 694 curr substSeen
 695 currAny substSeen
593 696
594 // Marks whether the current conditional statement has 697 // Marks whether the current conditional statement has
595 // an .else branch. If it doesn't, this means that all variables 698 // an .else branch. If it doesn't, this means that all variables
596 // are potentially unset in that branch. 699 // are potentially unset in that branch.
597 seenElse bool 700 seenElse bool
598} 701}
599 702
 703func newSubstCond() *substCond { return &substCond{total: ssAll} }
 704
 705func (c *substCond) enterBranch(isElse bool) {
 706 c.curr = ssNone
 707 c.currAny = ssNone
 708 c.seenElse = isElse
 709}
 710
 711func (c *substCond) leaveBranch() {
 712 c.total.retainAll(c.curr)
 713 c.totalAny.addAll(c.currAny)
 714 c.curr = ssNone
 715 c.currAny = ssNone
 716}
 717
 718func (c *substCond) leaveLevel(child *substCond) {
 719 child.leaveBranch()
 720 if child.seenElse {
 721 c.curr.addAll(child.total)
 722 }
 723 c.currAny.addAll(child.totalAny)
 724}
 725
 726func (c *substCond) hasSeen(part substSeen) bool { return c.curr.has(part) }
 727
 728func (c *substCond) hasSeenAnywhere(part substSeen) bool {
 729 return c.currAny.has(part)
 730}
 731
 732func (c *substCond) addSeen(part substSeen) {
 733 c.curr.set(part)
 734 c.currAny.set(part)
 735}
 736
600// substSeen contains all variables that depend on a particular SUBST 737// substSeen contains all variables that depend on a particular SUBST
601// class ID. These variables can be set in conditional branches, and 738// class ID. These variables can be set in conditional branches, and
602// pkglint keeps track whether they are set in all branches or only 739// pkglint keeps track whether they are set in all branches or only
603// in some of them. 740// in some of them.
604type substSeen uint8 741type substSeen uint8
605 742
606const ( 743const (
607 ssStage substSeen = 1 << iota 744 ssStage substSeen = 1 << iota
608 ssMessage 745 ssMessage
609 ssFiles 746 ssFiles
610 ssSed 747 ssSed
611 ssVars 748 ssVars
612 ssVarsAutofix 749 ssVarsAutofix
613 ssFilterCmd 750 ssFilterCmd
614 ssTransform 751 ssTransform
615 752
616 ssAll substSeen = 1<<iota - 1 753 ssAll substSeen = 1<<iota - 1
617 ssNone substSeen = 0 754 ssNone substSeen = 0
618) 755)
619 756
620func (s *substSeen) set(part substSeen) { *s |= part } 757func (s *substSeen) set(part substSeen) {
621func (s *substSeen) get(part substSeen) bool { return *s&part != 0 } 758 assert(part&(part-1) == 0)
622func (s *substSeen) hasAll(other substSeen) bool { return *s&other == other } 759 *s |= part
623func (s *substSeen) union(other substSeen) { *s |= other } 760}
624func (s *substSeen) retain(other substSeen) { *s &= other } 761
 762func (s *substSeen) has(part substSeen) bool {
 763 assert(part&(part-1) == 0)
 764 return *s&part != 0
 765}
 766
 767func (s substSeen) hasAll(other substSeen) bool { return s&other == other }
 768func (s *substSeen) addAll(other substSeen) { *s |= other }
 769func (s *substSeen) retainAll(other substSeen) { *s &= other }

cvs diff -r1.32 -r1.33 pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go 2019/12/13 01:39:23 1.32
+++ pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go 2019/12/14 18:04:15 1.33
@@ -1,443 +1,381 @@ @@ -1,443 +1,381 @@
1package pkglint 1package pkglint
2 2
3import "gopkg.in/check.v1" 3import "gopkg.in/check.v1"
4 4
5func (t *Tester) NewSubstAutofixTest(lines ...string) func(bool) { 5func (t *Tester) NewSubstAutofixTest(lines ...string) func(bool) {
6 return func(autofix bool) { 6 return func(autofix bool) {
7 mklines := t.NewMkLines("filename.mk", lines...) 7 mklines := t.NewMkLines("filename.mk", lines...)
 8 mklines.collectRationale()
8 ctx := NewSubstContext() 9 ctx := NewSubstContext()
9 10
10 mklines.ForEach(ctx.Process) 11 mklines.ForEach(ctx.Process)
11 ctx.Finish(mklines.EOFLine()) 12 ctx.Finish(mklines.EOFLine())
12 13
13 mklines.SaveAutofixChanges() 14 mklines.SaveAutofixChanges()
14 } 15 }
15} 16}
16 17
17func (t *Tester) RunSubst(lines ...string) { 18func (t *Tester) RunSubst(lines ...string) {
18 assert(lines[len(lines)-1] != "") 19 assert(lines[len(lines)-1] != "")
19 20
20 mklines := t.NewMkLines("filename.mk", lines...) 21 mklines := t.NewMkLines("filename.mk", lines...)
 22 mklines.collectRationale()
21 ctx := NewSubstContext() 23 ctx := NewSubstContext()
22 24
23 mklines.ForEach(ctx.Process) 25 mklines.ForEach(ctx.Process)
24 ctx.Finish(mklines.EOFLine()) 26 ctx.Finish(mklines.EOFLine())
25} 27}
26 28
27func (s *Suite) Test_SubstContext__OPSYSVARS(c *check.C) { 
28 t := s.Init(c) 
29 
30 ctx := NewSubstContext() 
31 
32 // SUBST_CLASSES is added to OPSYSVARS in mk/bsd.pkg.mk. 
33 ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES.SunOS+=prefix")) 
34 ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_CLASSES.NetBSD+=prefix")) 
35 ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_FILES.prefix=Makefile")) 
36 ctx.varassign(t.NewMkLine("filename.mk", 14, "SUBST_SED.prefix=s,@PREFIX@,${PREFIX},g")) 
37 ctx.varassign(t.NewMkLine("filename.mk", 15, "SUBST_STAGE.prefix=post-configure")) 
38 
39 t.CheckEquals(ctx.isComplete(), true) 
40 
41 ctx.Finish(t.NewMkLine("filename.mk", 15, "")) 
42 
43 t.CheckOutputLines( 
44 "NOTE: filename.mk:14: The substitution command \"s,@PREFIX@,${PREFIX},g\" " + 
45 "can be replaced with \"SUBST_VARS.prefix= PREFIX\".") 
46} 
47 
48func (s *Suite) Test_SubstContext__no_class(c *check.C) { 
49 t := s.Init(c) 
50 
51 t.RunSubst( 
52 "UNRELATED=anything", 
53 "SUBST_FILES.repl+=Makefile.in", 
54 "SUBST_SED.repl+=-e s,from,to,g") 
55 
56 t.CheckOutputLines( 
57 "WARN: filename.mk:2: Before defining SUBST_FILES.repl, " + 
58 "the SUBST class should be declared using \"SUBST_CLASSES+= repl\".") 
59} 
60 
61func (s *Suite) Test_SubstContext__multiple_classes_in_one_line(c *check.C) { 
62 t := s.Init(c) 
63 
64 t.RunSubst( 
65 "SUBST_CLASSES+= one two", 
66 "SUBST_STAGE.one= post-configure", 
67 "SUBST_FILES.one= one.txt", 
68 "SUBST_SED.one= s,one,1,g", 
69 "SUBST_STAGE.two= post-configure", 
70 "SUBST_FILES.two= two.txt") 
71 
72 t.CheckOutputLines( 
73 "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", 
74 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_SED.two, SUBST_VARS.two or SUBST_FILTER_CMD.two missing.") 
75} 
76 
77func (s *Suite) Test_SubstContext__multiple_classes_in_one_line_multiple_blocks(c *check.C) { 
78 t := s.Init(c) 
79 
80 t.RunSubst( 
81 "SUBST_CLASSES+= one two", 
82 "SUBST_STAGE.one= post-configure", 
83 "SUBST_FILES.one= one.txt", 
84 "SUBST_SED.one= s,one,1,g", 
85 "", 
86 "SUBST_STAGE.two= post-configure", 
87 "SUBST_FILES.two= two.txt", 
88 "", 
89 "SUBST_STAGE.three= post-configure", 
90 "", 
91 "SUBST_VARS.four= PREFIX", 
92 "", 
93 "SUBST_VARS.three= PREFIX") 
94 
95 t.CheckOutputLines( 
96 "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", 
97 "WARN: filename.mk:8: Incomplete SUBST block: "+ 
98 "SUBST_SED.two, SUBST_VARS.two or SUBST_FILTER_CMD.two missing.", 
99 "WARN: filename.mk:9: Before defining SUBST_STAGE.three, "+ 
100 "the SUBST class should be declared using \"SUBST_CLASSES+= three\".", 
101 "WARN: filename.mk:11: Before defining SUBST_VARS.four, "+ 
102 "the SUBST class should be declared using \"SUBST_CLASSES+= four\".") 
103} 
104 
105func (s *Suite) Test_SubstContext__multiple_classes_in_one_block(c *check.C) { 
106 t := s.Init(c) 
107 
108 t.RunSubst( 
109 "SUBST_CLASSES+= one", 
110 "SUBST_STAGE.one= post-configure", 
111 "SUBST_STAGE.one= post-configure", 
112 "SUBST_FILES.one= one.txt", 
113 "SUBST_CLASSES+= two", // The block "one" is not finished yet. 
114 "SUBST_SED.one= s,one,1,g", 
115 "SUBST_STAGE.two= post-configure", 
116 "SUBST_FILES.two= two.txt", 
117 "SUBST_SED.two= s,two,2,g") 
118 
119 t.CheckOutputLines( 
120 "WARN: filename.mk:3: Duplicate definition of \"SUBST_STAGE.one\".", 
121 "WARN: filename.mk:5: Incomplete SUBST block: SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.", 
122 "WARN: filename.mk:5: Subst block \"one\" should be finished before adding the next class to SUBST_CLASSES.", 
123 "WARN: filename.mk:6: Variable \"SUBST_SED.one\" does not match SUBST class \"two\".") 
124} 
125 
126// This is a strange example that probably won't occur in practice. 29// This is a strange example that probably won't occur in practice.
127// 30//
128// Continuing a SUBST class in one of the branches and starting 31// Continuing a SUBST class in one of the branches and starting
129// a fresh one in the other seems far-fetched. 32// a fresh one in the other seems far-fetched.
130func (s *Suite) Test_SubstContext__partially_continued_class_in_conditional(c *check.C) { 33func (s *Suite) Test_SubstContext__partially_continued_class_in_conditional(c *check.C) {
131 t := s.Init(c) 34 t := s.Init(c)
132 35
133 t.RunSubst( 36 t.RunSubst(
134 "SUBST_CLASSES+= outer", 37 "SUBST_CLASSES+= outer",
135 "SUBST_STAGE.outer= post-configure", 38 "SUBST_STAGE.outer= post-configure",
136 "SUBST_FILES.outer= files", 39 "SUBST_FILES.outer= files",
137 "SUBST_VARS.outer= OUTER.first", 40 "SUBST_VARS.outer= OUTER.first",
138 ".if ${:Ualways}", 41 ".if ${:Ualways}",
139 "SUBST_VARS.outer+= OUTER.second", 42 "SUBST_VARS.outer+= OUTER.second",
140 ".else", 43 ".else",
141 "SUBST_CLASSES+= inner", 44 "SUBST_CLASSES+= inner",
142 "SUBST_STAGE.inner= post-configure", 45 "SUBST_STAGE.inner= post-configure",
143 "SUBST_FILES.inner= files", 46 "SUBST_FILES.inner= files",
144 "SUBST_VARS.inner= INNER", 47 "SUBST_VARS.inner= INNER",
145 ".endif") 48 ".endif")
146 49
147 t.CheckOutputEmpty() 50 t.CheckOutputEmpty()
148} 51}
149 52
150func (s *Suite) Test_SubstContext__files_missing(c *check.C) { 53func (s *Suite) Test_SubstContext__conditionals(c *check.C) {
151 t := s.Init(c) 
152 
153 t.RunSubst( 
154 "SUBST_CLASSES+= one", 
155 "SUBST_STAGE.one= pre-configure", 
156 "SUBST_CLASSES+= two", 
157 "SUBST_STAGE.two= pre-configure", 
158 "SUBST_FILES.two= two.txt", 
159 "SUBST_SED.two= s,two,2,g") 
160 
161 t.CheckOutputLines( 
162 "WARN: filename.mk:3: Incomplete SUBST block: SUBST_FILES.one missing.", 
163 "WARN: filename.mk:3: Incomplete SUBST block: "+ 
164 "SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.", 
165 "WARN: filename.mk:3: Subst block \"one\" should be finished "+ 
166 "before adding the next class to SUBST_CLASSES.") 
167} 
168 
169func (s *Suite) Test_SubstContext__directives(c *check.C) { 
170 t := s.Init(c) 54 t := s.Init(c)
171 55
172 t.RunSubst( 56 t.RunSubst(
173 "SUBST_CLASSES+= os", 57 "SUBST_CLASSES+= os",
174 "SUBST_STAGE.os= post-configure", 58 "SUBST_STAGE.os= post-configure",
175 "SUBST_MESSAGE.os= Guessing operating system", 59 "SUBST_MESSAGE.os= Guessing operating system",
176 "SUBST_FILES.os= guess-os.h", 60 "SUBST_FILES.os= guess-os.h",
177 ".if ${OPSYS} == NetBSD", 61 ".if ${OPSYS} == NetBSD",
178 "SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS@,NetBSD,", 62 "SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS@,NetBSD,",
179 ".elif ${OPSYS} == Darwin", 63 ".elif ${OPSYS} == Darwin",
180 "SUBST_SED.os= -e s,@OPSYS@,Darwin1,", 64 "SUBST_SED.os= -e s,@OPSYS@,Darwin1,",
181 "SUBST_SED.os= -e s,@OPSYS@,Darwin2,", 65 "SUBST_SED.os= -e s,@OPSYS@,Darwin2,",
182 ".elif ${OPSYS} == Linux", 66 ".elif ${OPSYS} == Linux",
183 "SUBST_SED.os= -e s,@OPSYS@,Linux,", 67 "SUBST_SED.os= -e s,@OPSYS@,Linux,",
184 ".else", 68 ".else",
185 "SUBST_VARS.os= OPSYS", 69 "SUBST_VARS.os= OPSYS",
186 ".endif") 70 ".endif")
187 71
188 // All the other lines are correctly determined as being alternatives 72 // All the other lines are correctly determined as being alternatives
189 // to each other. And since every branch contains some transformation 73 // to each other. And since every branch contains some transformation
190 // (SED, VARS, FILTER_CMD), everything is fine. 74 // (SED, VARS, FILTER_CMD), everything is fine.
191 t.CheckOutputLines( 75 t.CheckOutputLines(
192 "WARN: filename.mk:9: All but the first assignment " + 76 "WARN: filename.mk:9: All but the first assignment " +
193 "to \"SUBST_SED.os\" should use the \"+=\" operator.") 77 "to \"SUBST_SED.os\" should use the \"+=\" operator.")
194} 78}
195 79
196func (s *Suite) Test_SubstContext__adjacent(c *check.C) { 80func (s *Suite) Test_SubstContext_varassign__no_class(c *check.C) {
197 t := s.Init(c) 
198 
199 t.RunSubst( 
200 "SUBST_CLASSES+=\t1", 
201 "SUBST_STAGE.1=\tpre-configure", 
202 "SUBST_FILES.1=\tfile1", 
203 "SUBST_SED.1=\t-e s,subst1,repl1,", 
204 "SUBST_CLASSES+=\t2", 
205 "SUBST_SED.1+=\t-e s,subst1b,repl1b,", // Misplaced 
206 "SUBST_STAGE.2=\tpre-configure", 
207 "SUBST_FILES.2=\tfile2", 
208 "SUBST_SED.2=\t-e s,subst2,repl2,") 
209 
210 t.CheckOutputLines( 
211 "WARN: filename.mk:6: Variable \"SUBST_SED.1\" does not match SUBST class \"2\".") 
212} 
213 
214func (s *Suite) Test_SubstContext__do_patch(c *check.C) { 
215 t := s.Init(c) 
216 
217 t.RunSubst( 
218 "SUBST_CLASSES+=\tos", 
219 "SUBST_STAGE.os=\tdo-patch", 
220 "SUBST_FILES.os=\tguess-os.h", 
221 "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,") 
222 
223 // No warning, since there is nothing to fix automatically. 
224 // This case doesn't occur in practice anyway. 
225 t.CheckOutputEmpty() 
226} 
227 
228// Variables mentioned in SUBST_VARS are not considered "foreign" 
229// in the block and may be mixed with the other SUBST variables. 
230func (s *Suite) Test_SubstContext__SUBST_VARS_defined_in_block(c *check.C) { 
231 t := s.Init(c) 81 t := s.Init(c)
232 82
233 t.RunSubst( 83 t.RunSubst(
234 "SUBST_CLASSES+=\tos", 84 "UNRELATED=anything",
235 "SUBST_STAGE.os=\tpre-configure", 85 "SUBST_FILES.repl+=Makefile.in",
236 "SUBST_FILES.os=\tguess-os.h", 86 "SUBST_SED.repl+=-e s,from,to,g")
237 "SUBST_VARS.os=\tTODAY1", 
238 "TODAY1!=\tdate", 
239 "TODAY2!=\tdate") 
240 87
241 t.CheckOutputLines( 88 t.CheckOutputLines(
242 "WARN: filename.mk:6: Foreign variable \"TODAY2\" in SUBST block.") 89 "WARN: filename.mk:2: Before defining SUBST_FILES.repl, " +
 90 "the SUBST class should be declared using \"SUBST_CLASSES+= repl\".")
243} 91}
244 92
245// Variables mentioned in SUBST_VARS may appear in the same paragraph, 93func (s *Suite) Test_SubstContext_varassign__multiple_classes_in_one_line_multiple_blocks(c *check.C) {
246// or alternatively anywhere else in the file. 
247func (s *Suite) Test_SubstContext__SUBST_VARS_in_next_paragraph(c *check.C) { 
248 t := s.Init(c) 94 t := s.Init(c)
249 95
250 t.RunSubst( 96 t.RunSubst(
251 "SUBST_CLASSES+=\tos", 97 "SUBST_CLASSES+= one two",
252 "SUBST_STAGE.os=\tpre-configure", 98 "SUBST_STAGE.one= post-configure",
253 "SUBST_FILES.os=\tguess-os.h", 99 "SUBST_FILES.one= one.txt",
254 "SUBST_VARS.os=\tTODAY1", 100 "SUBST_SED.one= s,one,1,g",
255 "", 101 "",
256 "TODAY1!=\tdate", 102 "SUBST_STAGE.two= post-configure",
257 "TODAY2!=\tdate") 103 "SUBST_FILES.two= two.txt",
 104 "",
 105 "SUBST_STAGE.three= post-configure",
 106 "",
 107 "SUBST_VARS.four= PREFIX",
 108 "",
 109 "SUBST_VARS.three= PREFIX")
258 110
259 t.CheckOutputEmpty() 111 t.CheckOutputLines(
 112 "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.",
 113 "WARN: filename.mk:9: Variable \"SUBST_STAGE.three\" "+
 114 "does not match SUBST class \"two\".",
 115 "WARN: filename.mk:9: Incomplete SUBST block: "+
 116 "SUBST_SED.two, SUBST_VARS.two or SUBST_FILTER_CMD.two missing.",
 117 "WARN: filename.mk:9: Before defining SUBST_STAGE.three, "+
 118 "the SUBST class should be declared using \"SUBST_CLASSES+= three\".",
 119 "WARN: filename.mk:11: Before defining SUBST_VARS.four, "+
 120 "the SUBST class should be declared using \"SUBST_CLASSES+= four\".")
260} 121}
261 122
262func (s *Suite) Test_SubstContext__multiple_SUBST_VARS(c *check.C) { 123func (s *Suite) Test_SubstContext_varassign__multiple_classes_in_one_block(c *check.C) {
263 t := s.Init(c) 124 t := s.Init(c)
264 125
265 t.RunSubst( 126 t.RunSubst(
266 "SUBST_CLASSES+=\tos", 127 "SUBST_CLASSES+= one",
267 "SUBST_STAGE.os=\tpre-configure", 128 "SUBST_STAGE.one= post-configure",
268 "SUBST_FILES.os=\tguess-os.h", 129 "SUBST_STAGE.one= post-configure",
269 "SUBST_VARS.os=\tPREFIX VARBASE") 130 "SUBST_FILES.one= one.txt",
 131 "SUBST_CLASSES+= two", // The block "one" is not finished yet.
 132 "SUBST_SED.one= s,one,1,g",
 133 "SUBST_STAGE.two= post-configure",
 134 "SUBST_FILES.two= two.txt",
 135 "SUBST_SED.two= s,two,2,g")
270 136
271 t.CheckOutputEmpty() 137 t.CheckOutputLines(
 138 "WARN: filename.mk:3: Duplicate definition of \"SUBST_STAGE.one\".",
 139 "WARN: filename.mk:5: Subst block \"one\" should be finished "+
 140 "before adding the next class to SUBST_CLASSES.",
 141 "WARN: filename.mk:5: Incomplete SUBST block: SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.",
 142 "WARN: filename.mk:6: Late additions to a SUBST variable should use the += operator.")
272} 143}
273 144
274// As of May 2019, pkglint does not check the order of the variables in 145// As of December 2019, pkglint does not check the order of the variables in
275// a SUBST block. Enforcing this order, or at least suggesting it, would 146// a SUBST block. Enforcing this order, or at least suggesting it, would
276// make pkgsrc packages more uniform, which is a good idea, but not urgent. 147// make pkgsrc packages more uniform, which is a good idea, but not urgent.
277func (s *Suite) Test_SubstContext__unusual_variable_order(c *check.C) { 148func (s *Suite) Test_SubstContext_varassign__unusual_order(c *check.C) {
278 t := s.Init(c) 149 t := s.Init(c)
279 150
280 t.RunSubst( 151 t.RunSubst(
281 "SUBST_CLASSES+=\t\tid", 152 "SUBST_CLASSES+=\t\tid",
282 "SUBST_SED.id=\t\t-e /deleteme/d", 153 "SUBST_SED.id=\t\t-e /deleteme/d",
283 "SUBST_FILES.id=\t\tfile", 154 "SUBST_FILES.id=\t\tfile",
284 "SUBST_MESSAGE.id=\tMessage", 155 "SUBST_MESSAGE.id=\tMessage",
285 "SUBST_STAGE.id=\t\tpre-configure") 156 "SUBST_STAGE.id=\t\tpre-configure")
286 157
287 t.CheckOutputEmpty() 158 t.CheckOutputEmpty()
288} 159}
289 160
290func (s *Suite) Test_SubstContext__completely_conditional_then(c *check.C) { 161func (s *Suite) Test_SubstContext_varassign__blocks_in_separate_paragraphs(c *check.C) {
291 t := s.Init(c) 
292 
293 t.RunSubst( 
294 ".if ${OPSYS} == Linux", 
295 "SUBST_CLASSES+=\tid", 
296 "SUBST_STAGE.id=\tpre-configure", 
297 "SUBST_SED.id=\t-e sahara", 
298 ".else", 
299 ".endif") 
300 
301 // The block already ends at the .else, not at the end of the file, 
302 // since that is the scope where the SUBST id is defined. 
303 t.CheckOutputLines( 
304 "WARN: filename.mk:5: Incomplete SUBST block: SUBST_FILES.id missing.") 
305} 
306 
307func (s *Suite) Test_SubstContext__completely_conditional_else(c *check.C) { 
308 t := s.Init(c) 
309 
310 t.RunSubst( 
311 ".if ${OPSYS} == Linux", 
312 ".else", 
313 "SUBST_CLASSES+=\tid", 
314 "SUBST_STAGE.id=\tpre-configure", 
315 "SUBST_SED.id=\t-e sahara", 
316 ".endif") 
317 
318 // The block already ends at the .endif, not at the end of the file, 
319 // since that is the scope where the SUBST id is defined. 
320 t.CheckOutputLines( 
321 "WARN: filename.mk:6: Incomplete SUBST block: SUBST_FILES.id missing.") 
322} 
323 
324func (s *Suite) Test_SubstContext__SUBST_CLASSES_in_separate_paragraph(c *check.C) { 
325 t := s.Init(c) 162 t := s.Init(c)
326 163
327 t.RunSubst( 164 t.RunSubst(
328 "SUBST_CLASSES+= 1 2 3 4", 165 "SUBST_CLASSES+= 1 2 3 4",
329 "", 166 "",
330 "SUBST_STAGE.1= post-configure", 167 "SUBST_STAGE.1= post-configure",
331 "SUBST_FILES.1= files", 168 "SUBST_FILES.1= files",
332 "SUBST_VARS.1= VAR1", 169 "SUBST_VARS.1= VAR1",
333 "", 170 "",
334 "SUBST_STAGE.2= post-configure", 171 "SUBST_STAGE.2= post-configure",
335 "SUBST_FILES.2= files", 172 "SUBST_FILES.2= files",
336 "SUBST_VARS.2= VAR1", 173 "SUBST_VARS.2= VAR1",
337 "", 174 "",
338 "SUBST_STAGE.3= post-configure", 175 "SUBST_STAGE.3= post-configure",
339 "SUBST_FILES.3= files", 176 "SUBST_FILES.3= files",
340 "SUBST_VARS.3= VAR1") 177 "SUBST_VARS.3= VAR1")
341 178
342 t.CheckOutputLines( 179 t.CheckOutputLines(
343 "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", 180 "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.",
344 "WARN: filename.mk:EOF: Missing SUBST block for \"4\".") 181 "WARN: filename.mk:EOF: Missing SUBST block for \"4\".")
345} 182}
346 183
347func (s *Suite) Test_SubstContext__wrong_class(c *check.C) { 184func (s *Suite) Test_SubstContext_varassign__typo_in_id(c *check.C) {
348 t := s.Init(c) 185 t := s.Init(c)
349 186
350 t.RunSubst( 187 t.RunSubst(
351 "SUBST_CLASSES+= 1 2", 188 "SUBST_CLASSES+= 1 2",
352 "SUBST_STAGE.x= post-configure", 189 "SUBST_STAGE.x= post-configure",
353 "SUBST_FILES.x= files", 190 "SUBST_FILES.x= files",
354 "SUBST_VARS.x= VAR1", 191 "SUBST_VARS.x= VAR1",
355 "SUBST_STAGE.2= post-configure", 192 "SUBST_STAGE.2= post-configure",
356 "SUBST_FILES.2= files", 193 "SUBST_FILES.2= files",
357 "SUBST_VARS.2= VAR1") 194 "SUBST_VARS.2= VAR1")
358 195
359 t.CheckOutputLines( 196 t.CheckOutputLines(
360 "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.", 197 "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.",
361 "WARN: filename.mk:2: Variable \"SUBST_STAGE.x\" does not match SUBST class \"1\".", 198 "WARN: filename.mk:2: Before defining SUBST_STAGE.x, "+
362 "WARN: filename.mk:3: Variable \"SUBST_FILES.x\" does not match SUBST class \"1\".", 199 "the SUBST class should be declared using \"SUBST_CLASSES+= x\".",
363 "WARN: filename.mk:4: Variable \"SUBST_VARS.x\" does not match SUBST class \"1\".", 200 "WARN: filename.mk:EOF: Missing SUBST block for \"1\".")
364 // XXX: This line could change to 2, since that is already in the queue. 
365 "WARN: filename.mk:5: Variable \"SUBST_STAGE.2\" does not match SUBST class \"1\".", 
366 "WARN: filename.mk:6: Variable \"SUBST_FILES.2\" does not match SUBST class \"1\".", 
367 "WARN: filename.mk:7: Variable \"SUBST_VARS.2\" does not match SUBST class \"1\".", 
368 "WARN: filename.mk:EOF: Missing SUBST block for \"1\".", 
369 "WARN: filename.mk:EOF: Missing SUBST block for \"2\".") 
370} 
371 
372func (s *Suite) Test_SubstContext_varassign__late_addition(c *check.C) { 
373 t := s.Init(c) 
374 
375 t.RunSubst( 
376 "SUBST_CLASSES+=\tid", 
377 "SUBST_STAGE.id=\tpost-configure", 
378 "SUBST_FILES.id=\tfiles", 
379 "SUBST_VARS.id=\tPREFIX", 
380 "", 
381 ".if ${OPSYS} == NetBSD", 
382 "SUBST_VARS.id=\tOPSYS", 
383 ".endif") 
384 
385 t.CheckOutputLines( 
386 "WARN: filename.mk:7: Late additions to a SUBST variable " + 
387 "should use the += operator.") 
388} 201}
389 202
390func (s *Suite) Test_SubstContext_varassign__late_addition_to_unknown_class(c *check.C) { 203func (s *Suite) Test_SubstContext_varassign__late_addition_to_unknown_class(c *check.C) {
391 t := s.Init(c) 204 t := s.Init(c)
392 205
393 mklines := t.NewMkLines("filename.mk", 206 mklines := t.NewMkLines("filename.mk",
394 "SUBST_VARS.id=\tOPSYS", 207 "SUBST_VARS.id=\tOPSYS",
395 "") 208 "")
396 ctx := NewSubstContext() 209 ctx := NewSubstContext()
397 mklines.collectRationale() 210 mklines.collectRationale()
398 211
399 mklines.ForEach(ctx.Process) 212 mklines.ForEach(ctx.Process)
400 213
401 t.CheckOutputLines( 214 t.CheckOutputLines(
402 "WARN: filename.mk:1: Before defining SUBST_VARS.id, " + 215 "WARN: filename.mk:1: Before defining SUBST_VARS.id, " +
403 "the SUBST class should be declared using \"SUBST_CLASSES+= id\".") 216 "the SUBST class should be declared using \"SUBST_CLASSES+= id\".")
404} 217}
405 218
406func (s *Suite) Test_SubstContext_varassignClasses__none(c *check.C) { 219func (s *Suite) Test_SubstContext_varassign__rationale(c *check.C) {
407 t := s.Init(c) 220 t := s.Init(c)
408 221
409 t.RunSubst( 222 t.RunSubst(
410 "SUBST_CLASSES+=\t# none") 223 "# Adjust setup.py",
 224 "SUBST_CLASSES+= setup",
 225 "SUBST_STAGE.setup= post-configure",
 226 "SUBST_FILES.setup= setup.py",
 227 "SUBST_VARS.setup= VAR")
411 228
 229 // The rationale in line 1 is supposed to suppress warnings,
 230 // not add new ones.
412 t.CheckOutputEmpty() 231 t.CheckOutputEmpty()
413} 232}
414 233
415func (s *Suite) Test_SubstContext_varassignClasses__indirect(c *check.C) { 234func (s *Suite) Test_SubstContext_varassign__interleaved(c *check.C) {
416 t := s.Init(c) 235 t := s.Init(c)
417 236
418 t.SetUpVartypes() 237 t.RunSubst(
419 mklines := t.NewMkLines("filename.mk", 238 "SUBST_CLASSES+= 1 2 3",
420 MkCvsID, 239 "SUBST_STAGE.1= post-configure",
421 "SUBST_CLASSES+=\t${VAR}") 240 "SUBST_STAGE.2= post-configure",
422 241 "SUBST_STAGE.3= post-configure",
423 mklines.Check() 242 "SUBST_FILES.1= setup.py",
424 243 "SUBST_FILES.2= setup.py",
 244 "SUBST_FILES.3= setup.py",
 245 "SUBST_VARS.1= VAR",
 246 "SUBST_VARS.2= VAR",
 247 "SUBST_VARS.3= VAR")
 248
 249 // The above does not follow the common pattern of defining
 250 // each block on its own.
 251 // It technically works but is not easy to read for humans.
 252 t.CheckOutputLines(
 253 "NOTE: filename.mk:1: " +
 254 "Please add only one class at a time to SUBST_CLASSES.")
 255}
 256
 257func (s *Suite) Test_SubstContext_varassignClasses__OPSYSVARS(c *check.C) {
 258 t := s.Init(c)
 259
 260 ctx := NewSubstContext()
 261
 262 // SUBST_CLASSES is added to OPSYSVARS in mk/bsd.pkg.mk.
 263 ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES.SunOS+=prefix"))
 264 ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_CLASSES.NetBSD+=prefix"))
 265 ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_FILES.prefix=Makefile"))
 266 ctx.varassign(t.NewMkLine("filename.mk", 14, "SUBST_SED.prefix=s,@PREFIX@,${PREFIX},g"))
 267 ctx.varassign(t.NewMkLine("filename.mk", 15, "SUBST_STAGE.prefix=post-configure"))
 268
 269 t.CheckEquals(ctx.block().isComplete(), true)
 270
 271 ctx.Finish(t.NewMkLine("filename.mk", 15, ""))
 272
 273 t.CheckOutputLines(
 274 "NOTE: filename.mk:14: The substitution command \"s,@PREFIX@,${PREFIX},g\" " +
 275 "can be replaced with \"SUBST_VARS.prefix= PREFIX\".")
 276}
 277
 278func (s *Suite) Test_SubstContext_varassignClasses__duplicate_id(c *check.C) {
 279 t := s.Init(c)
 280
 281 t.RunSubst(
 282 "SUBST_CLASSES+= id",
 283 "SUBST_CLASSES+= id")
 284
 285 t.CheckOutputLines(
 286 "ERROR: filename.mk:2: Duplicate SUBST class \"id\".",
 287 "WARN: filename.mk:EOF: Missing SUBST block for \"id\".")
 288}
 289
 290func (s *Suite) Test_SubstContext_varassignClasses__multiple_classes_in_one_line(c *check.C) {
 291 t := s.Init(c)
 292
 293 t.RunSubst(
 294 "SUBST_CLASSES+= one two",
 295 "SUBST_STAGE.one= post-configure",
 296 "SUBST_FILES.one= one.txt",
 297 "SUBST_SED.one= s,one,1,g",
 298 "SUBST_STAGE.two= post-configure",
 299 "SUBST_FILES.two= two.txt")
 300
 301 t.CheckOutputLines(
 302 "NOTE: filename.mk:1: Please add only one class at a time to SUBST_CLASSES.",
 303 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_SED.two, SUBST_VARS.two or SUBST_FILTER_CMD.two missing.")
 304}
 305
 306func (s *Suite) Test_SubstContext_varassignClasses__none(c *check.C) {
 307 t := s.Init(c)
 308
 309 t.RunSubst(
 310 "SUBST_CLASSES+=\t# none")
 311
 312 t.CheckOutputEmpty()
 313}
 314
 315func (s *Suite) Test_SubstContext_varassignClasses__indirect(c *check.C) {
 316 t := s.Init(c)
 317
 318 t.SetUpVartypes()
 319 mklines := t.NewMkLines("filename.mk",
 320 MkCvsID,
 321 "SUBST_CLASSES+=\t${VAR}")
 322
 323 mklines.Check()
 324
425 t.CheckOutputLines( 325 t.CheckOutputLines(
426 "ERROR: filename.mk:2: Identifiers for SUBST_CLASSES "+ 326 "ERROR: filename.mk:2: Identifiers for SUBST_CLASSES "+
427 "must not refer to other variables.", 327 "must not refer to other variables.",
428 "WARN: filename.mk:2: VAR is used but not defined.") 328 "WARN: filename.mk:2: VAR is used but not defined.")
429} 329}
430 330
 331func (s *Suite) Test_SubstContext_varassignOutsideBlock__assign(c *check.C) {
 332 t := s.Init(c)
 333
 334 t.RunSubst(
 335 "SUBST_CLASSES+=\t1",
 336 "SUBST_STAGE.1=\tpre-configure",
 337 "SUBST_FILES.1=\tfile1",
 338 "SUBST_SED.1=\t-e s,subst1,repl1,",
 339 "SUBST_CLASSES+=\t2",
 340 "SUBST_SED.1=\t-e s,subst1b,repl1b,", // Misplaced
 341 "SUBST_STAGE.2=\tpre-configure",
 342 "SUBST_FILES.2=\tfile2",
 343 "SUBST_SED.2=\t-e s,subst2,repl2,")
 344
 345 t.CheckOutputLines(
 346 "WARN: filename.mk:6: Late additions to a SUBST variable " +
 347 "should use the += operator.")
 348}
 349
 350func (s *Suite) Test_SubstContext_varassignOutsideBlock__append(c *check.C) {
 351 t := s.Init(c)
 352
 353 t.RunSubst(
 354 "SUBST_CLASSES+=\t1",
 355 "SUBST_STAGE.1=\tpre-configure",
 356 "SUBST_FILES.1=\tfile1",
 357 "SUBST_SED.1=\t-e s,subst1,repl1,",
 358 "SUBST_CLASSES+=\t2",
 359 "SUBST_SED.1+=\t-e s,subst1b,repl1b,", // Misplaced
 360 "SUBST_STAGE.2=\tpre-configure",
 361 "SUBST_FILES.2=\tfile2",
 362 "SUBST_SED.2=\t-e s,subst2,repl2,")
 363
 364 // The SUBST_SED.1 line is misplaced. It uses the += operator,
 365 // which makes it unclear whether this is a typo or not.
 366 t.CheckOutputEmpty()
 367}
 368
431// The rationale for the stray SUBST variables has to be specific. 369// The rationale for the stray SUBST variables has to be specific.
432// 370//
433// For example, in the following snippet from mail/dkim-milter/options.mk 371// For example, in the following snippet from mail/dkim-milter/options.mk
434// revision 1.9, there is a comment, but that is not a rationale and also 372// revision 1.9, there is a comment, but that is not a rationale and also
435// not related to the SUBST_CLASS variable at all: 373// not related to the SUBST_CLASS variable at all:
436// ### IPv6 support. 374// ### IPv6 support.
437// .if !empty(PKG_OPTIONS:Minet6) 375// .if !empty(PKG_OPTIONS:Minet6)
438// SUBST_SED.libs+= -e 's|@INET6@||g' 376// SUBST_SED.libs+= -e 's|@INET6@||g'
439// .endif 377// .endif
440func (s *Suite) Test_SubstContext_varassignOutsideBlock__rationale(c *check.C) { 378func (s *Suite) Test_SubstContext_varassignOutsideBlock__rationale(c *check.C) {
441 t := s.Init(c) 379 t := s.Init(c)
442 380
443 mklines := t.NewMkLines("filename.mk", 381 mklines := t.NewMkLines("filename.mk",
@@ -450,108 +388,540 @@ func (s *Suite) Test_SubstContext_varass @@ -450,108 +388,540 @@ func (s *Suite) Test_SubstContext_varass
450 "# The two class is defined somewhere else.", 388 "# The two class is defined somewhere else.",
451 "SUBST_VARS.two=\tOPSYS", 389 "SUBST_VARS.two=\tOPSYS",
452 "", 390 "",
453 // The word "defined" doesn't match the subst class "def". 391 // The word "defined" doesn't match the subst class "def".
454 "# This subst class is defined somewhere else.", 392 "# This subst class is defined somewhere else.",
455 "SUBST_VARS.def=\tOPSYS", 393 "SUBST_VARS.def=\tOPSYS",
456 "", 394 "",
457 "# Rationale that is completely irrelevant.", 395 "# Rationale that is completely irrelevant.",
458 "SUBST_SED.libs+=\t-e sahara", 396 "SUBST_SED.libs+=\t-e sahara",
459 "") 397 "")
460 ctx := NewSubstContext() 398 ctx := NewSubstContext()
461 mklines.collectRationale() 399 mklines.collectRationale()
462 400
463 mklines.ForEach(ctx.Process) 401 mklines.ForEach(ctx.Process)
 402
 403 t.CheckOutputLines(
 404 "WARN: filename.mk:2: Before defining SUBST_VARS.one, "+
 405 "the SUBST class should be declared using \"SUBST_CLASSES+= one\".",
 406 // In filename.mk:5 there is a proper rationale, thus no warning.
 407 "WARN: filename.mk:8: Before defining SUBST_VARS.def, "+
 408 "the SUBST class should be declared using \"SUBST_CLASSES+= def\".",
 409 "WARN: filename.mk:11: Before defining SUBST_SED.libs, "+
 410 "the SUBST class should be declared using \"SUBST_CLASSES+= libs\".")
 411}
 412
 413// Unbalanced conditionals must not lead to a panic.
 414func (s *Suite) Test_SubstContext_directive__before_SUBST_CLASSES(c *check.C) {
 415 t := s.Init(c)
 416
 417 t.RunSubst(
 418 ".if 0",
 419 ".endif",
 420 "SUBST_CLASSES+=\tos",
 421 ".elif 0")
 422
 423 t.CheckOutputLines(
 424 "WARN: filename.mk:4: Missing SUBST block for \"os\".")
 425}
 426
 427func (s *Suite) Test_SubstContext_directive__conditional_blocks_complete(c *check.C) {
 428 t := s.Init(c)
 429
 430 t.RunSubst(
 431 ".if ${OPSYS} == NetBSD",
 432 "SUBST_CLASSES+= nb",
 433 "SUBST_STAGE.nb= post-configure",
 434 "SUBST_FILES.nb= guess-netbsd.h",
 435 "SUBST_VARS.nb= HAVE_NETBSD",
 436 ".else",
 437 "SUBST_CLASSES+= os",
 438 "SUBST_STAGE.os= post-configure",
 439 "SUBST_FILES.os= guess-netbsd.h",
 440 "SUBST_VARS.os= HAVE_OTHER",
 441 ".endif")
 442
 443 t.CheckOutputEmpty()
 444}
 445
 446func (s *Suite) Test_SubstContext_directive__conditional_blocks_incomplete(c *check.C) {
 447 t := s.Init(c)
 448
 449 t.RunSubst(
 450 ".if ${OPSYS} == NetBSD",
 451 "SUBST_CLASSES+= nb",
 452 "SUBST_STAGE.nb= post-configure",
 453 "SUBST_VARS.nb= HAVE_NETBSD",
 454 ".else",
 455 "SUBST_CLASSES+= os",
 456 "SUBST_STAGE.os= post-configure",
 457 "SUBST_FILES.os= guess-netbsd.h",
 458 ".endif")
 459
 460 t.CheckOutputLines(
 461 "WARN: filename.mk:5: Incomplete SUBST block: SUBST_FILES.nb missing.",
 462 "WARN: filename.mk:6: Subst block \"nb\" should be finished "+
 463 "before adding the next class to SUBST_CLASSES.",
 464 "WARN: filename.mk:9: Incomplete SUBST block: "+
 465 "SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.")
 466}
 467
 468func (s *Suite) Test_SubstContext_directive__conditional_complete(c *check.C) {
 469 t := s.Init(c)
 470
 471 t.RunSubst(
 472 "SUBST_CLASSES+= id",
 473 ".if ${OPSYS} == NetBSD",
 474 "SUBST_STAGE.id=\t\tpost-configure",
 475 "SUBST_MESSAGE.id=\tpost-configure",
 476 "SUBST_FILES.id=\t\tguess-netbsd.h",
 477 "SUBST_SED.id=\t\t-e s,from,to,",
 478 "SUBST_VARS.id=\t\tHAVE_OTHER",
 479 "SUBST_FILTER_CMD.id=\tHAVE_OTHER",
 480 ".else",
 481 "SUBST_STAGE.id=\t\tpost-configure",
 482 "SUBST_MESSAGE.id=\tpost-configure",
 483 "SUBST_FILES.id=\t\tguess-netbsd.h",
 484 "SUBST_SED.id=\t\t-e s,from,to,",
 485 "SUBST_VARS.id=\t\tHAVE_OTHER",
 486 "SUBST_FILTER_CMD.id=\tHAVE_OTHER",
 487 ".endif")
 488
 489 t.CheckOutputLines(
 490 "WARN: filename.mk:3: SUBST_STAGE.id should not be defined conditionally.",
 491 "WARN: filename.mk:4: SUBST_MESSAGE.id should not be defined conditionally.",
 492 "WARN: filename.mk:10: SUBST_STAGE.id should not be defined conditionally.",
 493 "WARN: filename.mk:11: SUBST_MESSAGE.id should not be defined conditionally.")
 494}
 495
 496func (s *Suite) Test_SubstContext_directive__conditionally_overwritten_filter(c *check.C) {
 497 t := s.Init(c)
 498
 499 t.RunSubst(
 500 "SUBST_CLASSES+= id",
 501 "SUBST_STAGE.id=\t\tpost-configure",
 502 "SUBST_MESSAGE.id=\tpost-configure",
 503 "SUBST_FILES.id=\t\tguess-netbsd.h",
 504 "SUBST_FILTER_CMD.id=\tHAVE_OTHER",
 505 ".if ${OPSYS} == NetBSD",
 506 "SUBST_FILTER_CMD.id=\tHAVE_OTHER",
 507 ".endif")
 508
 509 t.CheckOutputLines(
 510 "WARN: filename.mk:7: Duplicate definition of \"SUBST_FILTER_CMD.id\".")
 511}
 512
 513// Hopefully nobody will ever trigger this case in real pkgsrc.
 514// It's plain confusing to a casual reader to nest a complete
 515// SUBST block into another SUBST block.
 516func (s *Suite) Test_SubstContext_directive__conditionally_nested_block(c *check.C) {
 517 t := s.Init(c)
 518
 519 t.RunSubst(
 520 "SUBST_CLASSES+= outer",
 521 "SUBST_STAGE.outer= post-configure",
 522 "SUBST_FILES.outer= outer.txt",
 523 ".if ${OPSYS} == NetBSD",
 524 "SUBST_CLASSES+= inner",
 525 "SUBST_STAGE.inner= post-configure",
 526 "SUBST_FILES.inner= inner.txt",
 527 "SUBST_VARS.inner= INNER",
 528 ".endif",
 529 "SUBST_VARS.outer= OUTER")
 530
 531 t.CheckOutputLines(
 532 "WARN: filename.mk:5: Subst block \"outer\" should be finished " +
 533 "before adding the next class to SUBST_CLASSES.")
 534}
 535
 536// It's completely valid to have several SUBST blocks in a single paragraph.
 537// As soon as a SUBST_CLASSES line appears, pkglint assumes that all previous
 538// SUBST blocks are finished. That's exactly the case here.
 539func (s *Suite) Test_SubstContext_directive__conditionally_following_block(c *check.C) {
 540 t := s.Init(c)
 541
 542 t.RunSubst(
 543 "SUBST_CLASSES+= outer",
 544 "SUBST_STAGE.outer= post-configure",
 545 "SUBST_FILES.outer= outer.txt",
 546 "SUBST_VARS.outer= OUTER",
 547 ".if ${OPSYS} == NetBSD",
 548 "SUBST_CLASSES+= middle",
 549 "SUBST_STAGE.middle= post-configure",
 550 "SUBST_FILES.middle= inner.txt",
 551 "SUBST_VARS.middle= INNER",
 552 ". if ${MACHINE_ARCH} == amd64",
 553 "SUBST_CLASSES+= inner",
 554 "SUBST_STAGE.inner= post-configure",
 555 "SUBST_FILES.inner= inner.txt",
 556 "SUBST_VARS.inner= INNER",
 557 ". endif",
 558 ".endif")
 559
 560 t.CheckOutputEmpty()
 561}
 562
 563func (s *Suite) Test_SubstContext_directive__two_blocks_in_condition(c *check.C) {
 564 t := s.Init(c)
 565
 566 t.RunSubst(
 567 ".if ${OPSYS} == NetBSD",
 568 "SUBST_CLASSES+= a",
 569 "SUBST_STAGE.a= post-configure",
 570 "SUBST_FILES.a= outer.txt",
 571 "SUBST_VARS.a= OUTER",
 572 "SUBST_CLASSES+= b",
 573 "SUBST_STAGE.b= post-configure",
 574 "SUBST_FILES.b= inner.txt",
 575 "SUBST_VARS.b= INNER",
 576 ".endif")
 577
 578 // Up to 2019-12-12, pkglint wrongly warned in filename.mk:6:
 579 // Subst block "a" should be finished before adding
 580 // the next class to SUBST_CLASSES.
 581 // The warning was wrong since block "a" has all required fields set.
 582 // The warning was caused by an inconsistent check whether the current
 583 // block had any conditional variables.
 584 t.CheckOutputEmpty()
 585}
 586
 587func (s *Suite) Test_SubstContext_directive__nested_conditional_incomplete_block(c *check.C) {
 588 t := s.Init(c)
 589
 590 t.RunSubst(
 591 "SUBST_CLASSES+= outer",
 592 "SUBST_STAGE.outer= post-configure",
 593 "SUBST_FILES.outer= outer.txt",
 594 "SUBST_VARS.outer= OUTER",
 595 ".if ${OPSYS} == NetBSD",
 596 "SUBST_CLASSES+= inner1",
 597 "SUBST_STAGE.inner1= post-configure",
 598 "SUBST_VARS.inner1= INNER",
 599 "SUBST_CLASSES+= inner2",
 600 "SUBST_STAGE.inner2= post-configure",
 601 "SUBST_FILES.inner2= inner.txt",
 602 "SUBST_VARS.inner2= INNER",
 603 ".endif")
 604
 605 t.CheckOutputLines(
 606 "WARN: filename.mk:9: Subst block \"inner1\" should be finished "+
 607 "before adding the next class to SUBST_CLASSES.",
 608 "WARN: filename.mk:9: Incomplete SUBST block: SUBST_FILES.inner1 missing.")
 609}
 610
 611func (s *Suite) Test_SubstContext_leave__details_in_then_branch(c *check.C) {
 612 t := s.Init(c)
 613
 614 t.RunSubst(
 615 "SUBST_CLASSES+= os",
 616 ".if ${OPSYS} == NetBSD",
 617 "SUBST_VARS.os= OPSYS",
 618 "SUBST_SED.os= -e s,@OPSYS@,NetBSD,",
 619 "SUBST_STAGE.os= post-configure",
 620 "SUBST_MESSAGE.os= Guessing operating system",
 621 "SUBST_FILES.os= guess-os.h",
 622 ".endif")
 623
 624 t.CheckOutputLines(
 625 "WARN: filename.mk:5: SUBST_STAGE.os should not be defined conditionally.",
 626 "WARN: filename.mk:6: SUBST_MESSAGE.os should not be defined conditionally.",
 627 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_STAGE.os missing.",
 628 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.os missing.",
 629 "WARN: filename.mk:EOF: Incomplete SUBST block: "+
 630 "SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.")
 631}
 632
 633func (s *Suite) Test_SubstContext_leave__details_in_else_branch(c *check.C) {
 634 t := s.Init(c)
 635
 636 t.RunSubst(
 637 "SUBST_CLASSES+= os",
 638 ".if ${OPSYS} == NetBSD",
 639 ".else",
 640 "SUBST_VARS.os= OPSYS",
 641 "SUBST_SED.os= -e s,@OPSYS@,NetBSD,",
 642 "SUBST_STAGE.os= post-configure",
 643 "SUBST_MESSAGE.os= Guessing operating system",
 644 "SUBST_FILES.os= guess-os.h",
 645 ".endif")
 646
 647 t.CheckOutputLines(
 648 "WARN: filename.mk:6: SUBST_STAGE.os should not be defined conditionally.",
 649 "WARN: filename.mk:7: SUBST_MESSAGE.os should not be defined conditionally.",
 650 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_STAGE.os missing.",
 651 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.os missing.",
 652 "WARN: filename.mk:EOF: Incomplete SUBST block: "+
 653 "SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.")
 654}
 655
 656func (s *Suite) Test_SubstContext_leave__empty_conditional_at_end(c *check.C) {
 657 t := s.Init(c)
 658
 659 t.RunSubst(
 660 "SUBST_CLASSES+= os",
 661 "SUBST_VARS.os= OPSYS",
 662 "SUBST_SED.os= -e s,@OPSYS@,NetBSD,",
 663 "SUBST_STAGE.os= post-configure",
 664 "SUBST_MESSAGE.os= Guessing operating system",
 665 "SUBST_FILES.os= guess-os.h",
 666 ".if ${OPSYS} == NetBSD",
 667 ".else",
 668 ".endif")
 669
 670 t.CheckOutputEmpty()
 671}
 672
 673func (s *Suite) Test_SubstContext_leave__missing_transformation_in_one_branch(c *check.C) {
 674 t := s.Init(c)
 675
 676 t.RunSubst(
 677 "SUBST_CLASSES+= os",
 678 "SUBST_STAGE.os= post-configure",
 679 "SUBST_MESSAGE.os= Guessing operating system",
 680 "SUBST_FILES.os= guess-os.h",
 681 ".if ${OPSYS} == NetBSD",
 682 "SUBST_FILES.os= -e s,@OpSYS@,NetBSD,", // A simple typo, this should be SUBST_SED.
 683 ".elif ${OPSYS} == Darwin",
 684 "SUBST_SED.os= -e s,@OPSYS@,Darwin1,",
 685 "SUBST_SED.os= -e s,@OPSYS@,Darwin2,",
 686 ".else",
 687 "SUBST_VARS.os= OPSYS",
 688 ".endif")
 689
 690 t.CheckOutputLines(
 691 "WARN: filename.mk:6: All but the first assignment "+
 692 "to \"SUBST_FILES.os\" should use the \"+=\" operator.",
 693 "WARN: filename.mk:9: All but the first assignment "+
 694 "to \"SUBST_SED.os\" should use the \"+=\" operator.",
 695 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_SED.os, "+
 696 "SUBST_VARS.os or SUBST_FILTER_CMD.os missing.")
 697}
 698
 699func (s *Suite) Test_SubstContext_leave__nested_conditionals(c *check.C) {
 700 t := s.Init(c)
 701
 702 t.RunSubst(
 703 "SUBST_CLASSES+= os",
 704 "SUBST_STAGE.os= post-configure",
 705 "SUBST_MESSAGE.os= Guessing operating system",
 706 ".if ${OPSYS} == NetBSD",
 707 "SUBST_FILES.os= guess-netbsd.h",
 708 ". if ${ARCH} == i386",
 709 "SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS,NetBSD-i386,",
 710 ". elif ${ARCH} == x86_64",
 711 "SUBST_VARS.os= OPSYS",
 712 ". else",
 713 "SUBST_SED.os= -e s,@OPSYS,NetBSD-unknown",
 714 ". endif",
 715 ".else",
 716 // This branch omits SUBST_FILES.
 717 "SUBST_SED.os= -e s,@OPSYS@,unknown,",
 718 ".endif")
 719
 720 t.CheckOutputLines(
 721 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.os missing.")
 722}
 723
 724// With every .if directive, a new scope is created, to properly
 725// keep track of the conditional level at which the SUBST classes
 726// are declared.
 727//
 728// The scopes are even created when there is no SUBST variable
 729// anywhere close. The conditionals must be tracked for determining
 730// the end of the scope for the SUBST_CLASSES IDs.
 731func (s *Suite) Test_substScope__conditionals(c *check.C) {
 732 t := s.Init(c)
 733
 734 ctx := NewSubstContext()
 735
 736 line := func(text string) {
 737 mkline := t.NewMkLine("filename.mk", 123, text)
 738 ctx.Process(mkline)
 739 }
 740 verifyScopes := func(n int) {
 741 t.CheckEquals(len(ctx.scopes), n)
 742 }
 743
 744 verifyScopes(1)
 745
 746 line(".if 1")
 747 verifyScopes(2)
 748
 749 line(". if 1")
 750 verifyScopes(3)
 751
 752 line(". if 1")
 753 verifyScopes(4)
 754
 755 line(". elif 1")
 756 verifyScopes(4)
 757
 758 line(". else")
 759 verifyScopes(4)
 760
 761 line(". endif")
 762 verifyScopes(3)
 763
 764 line(". endif")
 765 verifyScopes(2)
 766
 767 line(".endif")
 768 verifyScopes(1)
 769
 770 // An unbalanced .endif must not lead to a panic.
 771 line(".endif")
 772 verifyScopes(1)
 773
 774 ctx.Finish(NewLineEOF("filename.mk"))
 775}
 776
 777func (s *Suite) Test_substScope_prepareSubstClasses(c *check.C) {
 778 t := s.Init(c)
464 779
 780 t.RunSubst(
 781 "SUBST_CLASSES+= 1",
 782 "SUBST_STAGE.1= post-configure",
 783 ".if 0")
 784
 785 // There's no need to warn about unbalanced conditionals
 786 // since that is already done by MkLines.Check.
465 t.CheckOutputLines( 787 t.CheckOutputLines(
466 "WARN: filename.mk:2: Before defining SUBST_VARS.one, "+ 788 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.1 missing.",
467 "the SUBST class should be declared using \"SUBST_CLASSES+= one\".", 789 "WARN: filename.mk:EOF: Incomplete SUBST block: "+
468 // In filename.mk:5 there is a proper rationale, thus no warning. 790 "SUBST_SED.1, SUBST_VARS.1 or SUBST_FILTER_CMD.1 missing.")
469 "WARN: filename.mk:8: Before defining SUBST_VARS.def, "+ 791}
470 "the SUBST class should be declared using \"SUBST_CLASSES+= def\".", 792
471 "WARN: filename.mk:11: Before defining SUBST_SED.libs, "+ 793func (s *Suite) Test_substScope_prepareSubstClasses__nested(c *check.C) {
472 "the SUBST class should be declared using \"SUBST_CLASSES+= libs\".") 794 t := s.Init(c)
 795
 796 t.RunSubst(
 797 "SUBST_CLASSES+= 1",
 798 "SUBST_STAGE.1= post-configure",
 799 ".if 0",
 800 ".if 0",
 801 "SUBST_CLASSES+= 2")
 802
 803 t.CheckOutputLines(
 804 "WARN: filename.mk:5: Subst block \"1\" should be finished "+
 805 "before adding the next class to SUBST_CLASSES.",
 806 "WARN: filename.mk:EOF: Missing SUBST block for \"2\".",
 807 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.1 missing.",
 808 "WARN: filename.mk:EOF: Incomplete SUBST block: "+
 809 "SUBST_SED.1, SUBST_VARS.1 or SUBST_FILTER_CMD.1 missing.")
 810}
 811
 812func (s *Suite) Test_substBlock_varassignStage__do_patch(c *check.C) {
 813 t := s.Init(c)
 814
 815 t.RunSubst(
 816 "SUBST_CLASSES+=\tos",
 817 "SUBST_STAGE.os=\tdo-patch",
 818 "SUBST_FILES.os=\tguess-os.h",
 819 "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,")
 820
 821 // No warning, since there is nothing to fix automatically.
 822 // This case doesn't occur in practice anyway.
 823 t.CheckOutputEmpty()
473} 824}
474 825
475func (s *Suite) Test_SubstContext_varassignStage__pre_patch(c *check.C) { 826func (s *Suite) Test_substBlock_varassignStage__pre_patch(c *check.C) {
476 t := s.Init(c) 827 t := s.Init(c)
477 828
478 t.Chdir(".") 829 t.Chdir(".")
479 830
480 doTest := t.NewSubstAutofixTest( 831 doTest := t.NewSubstAutofixTest(
481 "SUBST_CLASSES+=\tos", 832 "SUBST_CLASSES+=\tos",
482 "SUBST_STAGE.os=\tpre-patch", 833 "SUBST_STAGE.os=\tpre-patch",
483 "SUBST_FILES.os=\tguess-os.h", 834 "SUBST_FILES.os=\tguess-os.h",
484 "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,") 835 "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,")
485 836
486 t.ExpectDiagnosticsAutofix( 837 t.ExpectDiagnosticsAutofix(
487 doTest, 838 doTest,
488 "WARN: filename.mk:2: Substitutions should not happen in the patch phase.", 839 "WARN: filename.mk:2: Substitutions should not happen in the patch phase.",
489 "AUTOFIX: filename.mk:2: Replacing \"pre-patch\" with \"post-extract\".") 840 "AUTOFIX: filename.mk:2: Replacing \"pre-patch\" with \"post-extract\".")
490 841
491 t.CheckFileLinesDetab("filename.mk", 842 t.CheckFileLinesDetab("filename.mk",
492 "SUBST_CLASSES+= os", 843 "SUBST_CLASSES+= os",
493 "SUBST_STAGE.os= post-extract", 844 "SUBST_STAGE.os= post-extract",
494 "SUBST_FILES.os= guess-os.h", 845 "SUBST_FILES.os= guess-os.h",
495 "SUBST_SED.os= -e s,@OPSYS@,Darwin,") 846 "SUBST_SED.os= -e s,@OPSYS@,Darwin,")
496} 847}
497 848
498func (s *Suite) Test_SubstContext_varassignStage__post_patch(c *check.C) { 849func (s *Suite) Test_substBlock_varassignStage__post_patch(c *check.C) {
499 t := s.Init(c) 850 t := s.Init(c)
500 851
501 t.Chdir(".") 852 t.Chdir(".")
502 853
503 doTest := t.NewSubstAutofixTest( 854 doTest := t.NewSubstAutofixTest(
504 "SUBST_CLASSES+=\tos", 855 "SUBST_CLASSES+=\tos",
505 "SUBST_STAGE.os=\tpost-patch", 856 "SUBST_STAGE.os=\tpost-patch",
506 "SUBST_FILES.os=\tguess-os.h", 857 "SUBST_FILES.os=\tguess-os.h",
507 "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,") 858 "SUBST_SED.os=\t-e s,@OPSYS@,Darwin,")
508 859
509 t.ExpectDiagnosticsAutofix( 860 t.ExpectDiagnosticsAutofix(
510 doTest, 861 doTest,
511 "WARN: filename.mk:2: Substitutions should not happen in the patch phase.", 862 "WARN: filename.mk:2: Substitutions should not happen in the patch phase.",
512 "AUTOFIX: filename.mk:2: Replacing \"post-patch\" with \"pre-configure\".") 863 "AUTOFIX: filename.mk:2: Replacing \"post-patch\" with \"pre-configure\".")
513 864
514 t.CheckFileLinesDetab("filename.mk", 865 t.CheckFileLinesDetab("filename.mk",
515 "SUBST_CLASSES+= os", 866 "SUBST_CLASSES+= os",
516 "SUBST_STAGE.os= pre-configure", 867 "SUBST_STAGE.os= pre-configure",
517 "SUBST_FILES.os= guess-os.h", 868 "SUBST_FILES.os= guess-os.h",
518 "SUBST_SED.os= -e s,@OPSYS@,Darwin,") 869 "SUBST_SED.os= -e s,@OPSYS@,Darwin,")
519} 870}
520 871
 872func (s *Suite) Test_substBlock_varassignStage__empty_class(c *check.C) {
 873 t := s.Init(c)
 874
 875 t.Chdir(".")
 876
 877 t.RunSubst(
 878 "SUBST_CLASSES+= id",
 879 "",
 880 "SUBST_STAGE.= post-patch",
 881 "SUBST_STAGE.id= post-configure",
 882 "SUBST_FILES.id= files",
 883 "SUBST_VARS.id= VAR",
 884 "SUBST_VARS.= VAR")
 885
 886 t.CheckOutputLines(
 887 "ERROR: filename.mk:3: Invalid SUBST class \"\" in variable name.",
 888 "ERROR: filename.mk:7: Invalid SUBST class \"\" in variable name.")
 889}
 890
521// As of December 2019, pkglint does not use token positions internally. 891// As of December 2019, pkglint does not use token positions internally.
522// Instead it only does simple string replacement when autofixing things. 892// Instead it only does simple string replacement when autofixing things.
523// To avoid damaging anything, replacements are only done if they are 893// To avoid damaging anything, replacements are only done if they are
524// unambiguous. This is not the case here, since line 4 contains the 894// unambiguous. This is not the case here, since line 4 contains the
525// string "pre-patch" twice. 895// string "pre-patch" twice.
526func (s *Suite) Test_SubstContext_varassignStage__ambiguous_replacement(c *check.C) { 896func (s *Suite) Test_substBlock_varassignStage__ambiguous_replacement(c *check.C) {
527 t := s.Init(c) 897 t := s.Init(c)
528 898
529 t.Chdir(".") 899 t.Chdir(".")
530 900
531 doTest := t.NewSubstAutofixTest( 901 doTest := t.NewSubstAutofixTest(
532 "SUBST_CLASSES+= pre-patch", 902 "SUBST_CLASSES+= pre-patch",
533 "SUBST_STAGE.pre-patch= pre-patch", 903 "SUBST_STAGE.pre-patch= pre-patch",
534 "SUBST_FILES.pre-patch= files", 904 "SUBST_FILES.pre-patch= files",
535 "SUBST_VARS.pre-patch= VARNAME") 905 "SUBST_VARS.pre-patch= VARNAME")
536 906
537 t.ExpectDiagnosticsAutofix( 907 t.ExpectDiagnosticsAutofix(
538 doTest, 908 doTest,
539 "WARN: filename.mk:2: Substitutions should not happen in the patch phase.") 909 "WARN: filename.mk:2: Substitutions should not happen in the patch phase.")
540 910
541 t.CheckEquals(t.File("filename.mk").IsFile(), false) 911 t.CheckEquals(t.File("filename.mk").IsFile(), false)
542} 912}
543 913
544func (s *Suite) Test_SubstContext_varassignStage__with_NO_CONFIGURE(c *check.C) { 914func (s *Suite) Test_substBlock_varassignStage__with_NO_CONFIGURE(c *check.C) {
545 t := s.Init(c) 915 t := s.Init(c)
546 916
547 pkg := t.SetUpPackage("category/package", 917 pkg := t.SetUpPackage("category/package",
548 "SUBST_CLASSES+=\t\tpre", 918 "SUBST_CLASSES+=\t\tpre",
549 "SUBST_STAGE.pre=\tpre-configure", 919 "SUBST_STAGE.pre=\tpre-configure",
550 "SUBST_FILES.pre=\tguess-os.h", 920 "SUBST_FILES.pre=\tguess-os.h",
551 "SUBST_SED.pre=\t\t-e s,@OPSYS@,Darwin,", 921 "SUBST_SED.pre=\t\t-e s,@OPSYS@,Darwin,",
552 "", 922 "",
553 "SUBST_CLASSES+=\t\tpost", 923 "SUBST_CLASSES+=\t\tpost",
554 "SUBST_STAGE.post=\tpost-configure", 924 "SUBST_STAGE.post=\tpost-configure",
555 "SUBST_FILES.post=\tguess-os.h", 925 "SUBST_FILES.post=\tguess-os.h",
556 "SUBST_SED.post=\t\t-e s,@OPSYS@,Darwin,", 926 "SUBST_SED.post=\t\t-e s,@OPSYS@,Darwin,",
557 "", 927 "",
@@ -562,83 +932,79 @@ func (s *Suite) Test_SubstContext_varass @@ -562,83 +932,79 @@ func (s *Suite) Test_SubstContext_varass
562 "", 932 "",
563 "NO_CONFIGURE=\tyes") 933 "NO_CONFIGURE=\tyes")
564 t.FinishSetUp() 934 t.FinishSetUp()
565 935
566 G.Check(pkg) 936 G.Check(pkg)
567 937
568 t.CheckOutputLines( 938 t.CheckOutputLines(
569 "WARN: ~/category/package/Makefile:21: SUBST_STAGE pre-configure has no effect "+ 939 "WARN: ~/category/package/Makefile:21: SUBST_STAGE pre-configure has no effect "+
570 "when NO_CONFIGURE is set (in line 35).", 940 "when NO_CONFIGURE is set (in line 35).",
571 "WARN: ~/category/package/Makefile:26: SUBST_STAGE post-configure has no effect "+ 941 "WARN: ~/category/package/Makefile:26: SUBST_STAGE post-configure has no effect "+
572 "when NO_CONFIGURE is set (in line 35).") 942 "when NO_CONFIGURE is set (in line 35).")
573} 943}
574 944
575func (s *Suite) Test_SubstContext_varassignStage__without_NO_CONFIGURE(c *check.C) { 945func (s *Suite) Test_substBlock_varassignStage__without_NO_CONFIGURE(c *check.C) {
576 t := s.Init(c) 946 t := s.Init(c)
577 947
578 pkg := t.SetUpPackage("category/package", 948 pkg := t.SetUpPackage("category/package",
579 "SUBST_CLASSES+=\t\tpre", 949 "SUBST_CLASSES+=\t\tpre",
580 "SUBST_STAGE.pre=\tpre-configure", 950 "SUBST_STAGE.pre=\tpre-configure",
581 "SUBST_FILES.pre=\tguess-os.h", 951 "SUBST_FILES.pre=\tguess-os.h",
582 "SUBST_SED.pre=\t\t-e s,@OPSYS@,Darwin,") 952 "SUBST_SED.pre=\t\t-e s,@OPSYS@,Darwin,")
583 t.FinishSetUp() 953 t.FinishSetUp()
584 954
585 G.Check(pkg) 955 G.Check(pkg)
586 956
587 t.CheckOutputEmpty() 957 t.CheckOutputEmpty()
588} 958}
589 959
590// Before 2019-12-12, pkglint wrongly warned about variables that were 960// Before 2019-12-12, pkglint wrongly warned about variables that were
591// not obviously SUBST variables, even if they were used later in SUBST_VARS. 961// not obviously SUBST variables, even if they were used later in SUBST_VARS.
592func (s *Suite) Test_SubstContext_varassignVars__var_before_SUBST_VARS(c *check.C) { 962func (s *Suite) Test_substBlock_varassignVars__var_before_SUBST_VARS(c *check.C) {
593 t := s.Init(c) 963 t := s.Init(c)
594 964
595 t.RunSubst( 965 t.RunSubst(
596 "SUBST_CLASSES+= id", 966 "SUBST_CLASSES+= id",
597 "SUBST_STAGE.id= post-configure", 967 "SUBST_STAGE.id= post-configure",
598 "SUBST_FILES.id= files", 968 "SUBST_FILES.id= files",
599 "FOREIGN= not mentioned in SUBST_VARS", 969 "FOREIGN= not mentioned in SUBST_VARS",
600 "VAR= ok", 970 "VAR= ok",
601 "SUBST_VARS.id= VAR", 971 "SUBST_VARS.id= VAR",
602 "", 972 "",
603 // This second block makes sure that the list of foreign variables 973 // This second block makes sure that the list of foreign variables
604 // is properly reset at the end of a SUBST block. 974 // is properly reset at the end of a SUBST block.
605 // If it weren't, there would be additional warnings. 975 // If it weren't, there would be additional warnings.
606 "SUBST_CLASSES+= 2", 976 "SUBST_CLASSES+= 2",
607 "SUBST_STAGE.2= post-configure", 977 "SUBST_STAGE.2= post-configure",
608 "SUBST_FILES.2= files", 978 "SUBST_FILES.2= files",
609 "SUBST_VARS.2= OTHER") 979 "SUBST_VARS.2= OTHER")
610 980
611 t.CheckOutputLines( 981 t.CheckOutputLines(
612 "WARN: filename.mk:4: Foreign variable \"FOREIGN\" in SUBST block.") 982 "WARN: filename.mk:4: Foreign variable \"FOREIGN\" in SUBST block.")
613} 983}
614 984
615func (s *Suite) Test_SubstContext_dupList__conditional_before_unconditional(c *check.C) { 985func (s *Suite) Test_substBlock_varassignVars(c *check.C) {
616 t := s.Init(c) 986 t := s.Init(c)
617 987
618 t.RunSubst( 988 t.RunSubst(
619 "SUBST_CLASSES+= os", 989 "SUBST_CLASSES+=\tos",
620 "SUBST_STAGE.os= post-configure", 990 "SUBST_STAGE.os=\tpre-configure",
621 ".if 1", 991 "SUBST_FILES.os=\tguess-os.h",
622 "SUBST_FILES.os= conditional", 992 "SUBST_VARS.os=\tPREFIX VARBASE")
623 ".endif", 
624 "SUBST_FILES.os= unconditional", 
625 "SUBST_VARS.os= OPSYS") 
626 993
627 // TODO: Warn that the conditional line is overwritten. 
628 t.CheckOutputEmpty() 994 t.CheckOutputEmpty()
629} 995}
630 996
631func (s *Suite) Test_SubstContext_suggestSubstVars(c *check.C) { 997func (s *Suite) Test_substBlock_suggestSubstVars(c *check.C) {
632 t := s.Init(c) 998 t := s.Init(c)
633 999
634 t.Chdir(".") 1000 t.Chdir(".")
635 1001
636 test := func(line string, diagnostics ...string) { 1002 test := func(line string, diagnostics ...string) {
637 doTest := t.NewSubstAutofixTest( 1003 doTest := t.NewSubstAutofixTest(
638 "SUBST_CLASSES+=\t\ttest", 1004 "SUBST_CLASSES+=\t\ttest",
639 "SUBST_STAGE.test=\tpre-configure", 1005 "SUBST_STAGE.test=\tpre-configure",
640 "SUBST_FILES.test=\tfilename", 1006 "SUBST_FILES.test=\tfilename",
641 line) 1007 line)
642 1008
643 t.ExpectDiagnosticsAutofix( 1009 t.ExpectDiagnosticsAutofix(
644 doTest, 1010 doTest,
@@ -742,27 +1108,27 @@ func (s *Suite) Test_SubstContext_sugges @@ -742,27 +1108,27 @@ func (s *Suite) Test_SubstContext_sugges
742 "NOTE: filename.mk:4: The substitution command \"s,@SH@,${SH:Q},\" "+ 1108 "NOTE: filename.mk:4: The substitution command \"s,@SH@,${SH:Q},\" "+
743 "can be replaced with \"SUBST_VARS.test= SH\".") 1109 "can be replaced with \"SUBST_VARS.test= SH\".")
744 1110
745 // Just a note; not fixed because of the -n. 1111 // Just a note; not fixed because of the -n.
746 test( 1112 test(
747 "SUBST_SED.test+=\t-n s,@SH@,${SH:Q},", 1113 "SUBST_SED.test+=\t-n s,@SH@,${SH:Q},",
748 1114
749 "NOTE: filename.mk:4: The substitution command \"s,@SH@,${SH:Q},\" "+ 1115 "NOTE: filename.mk:4: The substitution command \"s,@SH@,${SH:Q},\" "+
750 "can be replaced with \"SUBST_VARS.test= SH\".") 1116 "can be replaced with \"SUBST_VARS.test= SH\".")
751} 1117}
752 1118
753// If the SUBST_CLASS identifier ends with a plus, the generated code must 1119// If the SUBST_CLASS identifier ends with a plus, the generated code must
754// use the correct assignment operator and be nicely formatted. 1120// use the correct assignment operator and be nicely formatted.
755func (s *Suite) Test_SubstContext_suggestSubstVars__plus(c *check.C) { 1121func (s *Suite) Test_substBlock_suggestSubstVars__plus(c *check.C) {
756 t := s.Init(c) 1122 t := s.Init(c)
757 1123
758 t.Chdir(".") 1124 t.Chdir(".")
759 1125
760 doTest := t.NewSubstAutofixTest( 1126 doTest := t.NewSubstAutofixTest(
761 "SUBST_CLASSES+=\t\tgtk+", 1127 "SUBST_CLASSES+=\t\tgtk+",
762 "SUBST_STAGE.gtk+ =\tpre-configure", 1128 "SUBST_STAGE.gtk+ =\tpre-configure",
763 "SUBST_FILES.gtk+ =\tfilename", 1129 "SUBST_FILES.gtk+ =\tfilename",
764 "SUBST_SED.gtk+ +=\t-e s,@SH@,${SH:Q},g", 1130 "SUBST_SED.gtk+ +=\t-e s,@SH@,${SH:Q},g",
765 "SUBST_SED.gtk+ +=\t-e s,@SH@,${SH:Q},g") 1131 "SUBST_SED.gtk+ +=\t-e s,@SH@,${SH:Q},g")
766 1132
767 t.ExpectDiagnosticsAutofix( 1133 t.ExpectDiagnosticsAutofix(
768 doTest, 1134 doTest,
@@ -776,27 +1142,27 @@ func (s *Suite) Test_SubstContext_sugges @@ -776,27 +1142,27 @@ func (s *Suite) Test_SubstContext_sugges
776 "with \"SUBST_VARS.gtk+ +=\\tSH\".") 1142 "with \"SUBST_VARS.gtk+ +=\\tSH\".")
777 1143
778 t.CheckFileLinesDetab("filename.mk", 1144 t.CheckFileLinesDetab("filename.mk",
779 "SUBST_CLASSES+= gtk+", 1145 "SUBST_CLASSES+= gtk+",
780 "SUBST_STAGE.gtk+ = pre-configure", 1146 "SUBST_STAGE.gtk+ = pre-configure",
781 "SUBST_FILES.gtk+ = filename", 1147 "SUBST_FILES.gtk+ = filename",
782 "SUBST_VARS.gtk+ = SH", 1148 "SUBST_VARS.gtk+ = SH",
783 "SUBST_VARS.gtk+ += SH") 1149 "SUBST_VARS.gtk+ += SH")
784} 1150}
785 1151
786// The last of the SUBST_SED variables is 15 characters wide. When SUBST_SED 1152// The last of the SUBST_SED variables is 15 characters wide. When SUBST_SED
787// is replaced with SUBST_VARS, this becomes 16 characters and therefore 1153// is replaced with SUBST_VARS, this becomes 16 characters and therefore
788// requires the whole paragraph to be indented by one more tab. 1154// requires the whole paragraph to be indented by one more tab.
789func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_realign_paragraph(c *check.C) { 1155func (s *Suite) Test_substBlock_suggestSubstVars__autofix_realign_paragraph(c *check.C) {
790 t := s.Init(c) 1156 t := s.Init(c)
791 1157
792 t.Chdir(".") 1158 t.Chdir(".")
793 1159
794 doTest := t.NewSubstAutofixTest( 1160 doTest := t.NewSubstAutofixTest(
795 "SUBST_CLASSES+=\t\tpfx", 1161 "SUBST_CLASSES+=\t\tpfx",
796 "SUBST_STAGE.pfx=\tpre-configure", 1162 "SUBST_STAGE.pfx=\tpre-configure",
797 "SUBST_FILES.pfx=\tfilename", 1163 "SUBST_FILES.pfx=\tfilename",
798 "SUBST_SED.pfx=\t\t-e s,@PREFIX@,${PREFIX},g", 1164 "SUBST_SED.pfx=\t\t-e s,@PREFIX@,${PREFIX},g",
799 "SUBST_SED.pfx+=\t\t-e s,@PREFIX@,${PREFIX},g") 1165 "SUBST_SED.pfx+=\t\t-e s,@PREFIX@,${PREFIX},g")
800 1166
801 t.ExpectDiagnosticsAutofix( 1167 t.ExpectDiagnosticsAutofix(
802 doTest, 1168 doTest,
@@ -808,27 +1174,27 @@ func (s *Suite) Test_SubstContext_sugges @@ -808,27 +1174,27 @@ func (s *Suite) Test_SubstContext_sugges
808 "AUTOFIX: filename.mk:4: Replacing \"SUBST_SED.pfx=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ 1174 "AUTOFIX: filename.mk:4: Replacing \"SUBST_SED.pfx=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+
809 "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".", 1175 "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".",
810 "AUTOFIX: filename.mk:5: Replacing \"SUBST_SED.pfx+=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ 1176 "AUTOFIX: filename.mk:5: Replacing \"SUBST_SED.pfx+=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+
811 "with \"SUBST_VARS.pfx+=\\tPREFIX\".") 1177 "with \"SUBST_VARS.pfx+=\\tPREFIX\".")
812 1178
813 t.CheckFileLinesDetab("filename.mk", 1179 t.CheckFileLinesDetab("filename.mk",
814 "SUBST_CLASSES+= pfx", 1180 "SUBST_CLASSES+= pfx",
815 "SUBST_STAGE.pfx= pre-configure", 1181 "SUBST_STAGE.pfx= pre-configure",
816 "SUBST_FILES.pfx= filename", 1182 "SUBST_FILES.pfx= filename",
817 "SUBST_VARS.pfx= PREFIX", 1183 "SUBST_VARS.pfx= PREFIX",
818 "SUBST_VARS.pfx+= PREFIX") 1184 "SUBST_VARS.pfx+= PREFIX")
819} 1185}
820 1186
821func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_plus_sed(c *check.C) { 1187func (s *Suite) Test_substBlock_suggestSubstVars__autofix_plus_sed(c *check.C) {
822 t := s.Init(c) 1188 t := s.Init(c)
823 1189
824 t.Chdir(".") 1190 t.Chdir(".")
825 1191
826 doTest := t.NewSubstAutofixTest( 1192 doTest := t.NewSubstAutofixTest(
827 "SUBST_CLASSES+=\t\tpfx", 1193 "SUBST_CLASSES+=\t\tpfx",
828 "SUBST_STAGE.pfx=\tpre-configure", 1194 "SUBST_STAGE.pfx=\tpre-configure",
829 "SUBST_FILES.pfx=\tfilename", 1195 "SUBST_FILES.pfx=\tfilename",
830 "SUBST_SED.pfx=\t\t-e s,@PREFIX@,${PREFIX},g", 1196 "SUBST_SED.pfx=\t\t-e s,@PREFIX@,${PREFIX},g",
831 "SUBST_SED.pfx+=\t\t-e s,@PREFIX@,other,g") 1197 "SUBST_SED.pfx+=\t\t-e s,@PREFIX@,other,g")
832 1198
833 t.ExpectDiagnosticsAutofix( 1199 t.ExpectDiagnosticsAutofix(
834 doTest, 1200 doTest,
@@ -839,27 +1205,27 @@ func (s *Suite) Test_SubstContext_sugges @@ -839,27 +1205,27 @@ func (s *Suite) Test_SubstContext_sugges
839 "Replacing \"SUBST_SED.pfx=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ 1205 "Replacing \"SUBST_SED.pfx=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+
840 "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".") 1206 "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".")
841 1207
842 t.CheckFileLinesDetab("filename.mk", 1208 t.CheckFileLinesDetab("filename.mk",
843 "SUBST_CLASSES+= pfx", 1209 "SUBST_CLASSES+= pfx",
844 "SUBST_STAGE.pfx= pre-configure", 1210 "SUBST_STAGE.pfx= pre-configure",
845 "SUBST_FILES.pfx= filename", 1211 "SUBST_FILES.pfx= filename",
846 "SUBST_VARS.pfx= PREFIX", 1212 "SUBST_VARS.pfx= PREFIX",
847 // Since the SUBST_SED that was previously here used the = operator, 1213 // Since the SUBST_SED that was previously here used the = operator,
848 // this += might be replaced with a simple =. 1214 // this += might be replaced with a simple =.
849 "SUBST_SED.pfx+= -e s,@PREFIX@,other,g") 1215 "SUBST_SED.pfx+= -e s,@PREFIX@,other,g")
850} 1216}
851 1217
852func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_plus_vars(c *check.C) { 1218func (s *Suite) Test_substBlock_suggestSubstVars__autofix_plus_vars(c *check.C) {
853 t := s.Init(c) 1219 t := s.Init(c)
854 1220
855 t.Chdir(".") 1221 t.Chdir(".")
856 1222
857 doTest := t.NewSubstAutofixTest( 1223 doTest := t.NewSubstAutofixTest(
858 "SUBST_CLASSES+=\tid", 1224 "SUBST_CLASSES+=\tid",
859 "SUBST_STAGE.id=\tpre-configure", 1225 "SUBST_STAGE.id=\tpre-configure",
860 "SUBST_FILES.id=\tfilename", 1226 "SUBST_FILES.id=\tfilename",
861 "SUBST_SED.id=\t-e s,@PREFIX@,${PREFIX},g", 1227 "SUBST_SED.id=\t-e s,@PREFIX@,${PREFIX},g",
862 "SUBST_VARS.id=\tPKGMANDIR") 1228 "SUBST_VARS.id=\tPKGMANDIR")
863 1229
864 t.ExpectDiagnosticsAutofix( 1230 t.ExpectDiagnosticsAutofix(
865 doTest, 1231 doTest,
@@ -871,27 +1237,27 @@ func (s *Suite) Test_SubstContext_sugges @@ -871,27 +1237,27 @@ func (s *Suite) Test_SubstContext_sugges
871 "with \"SUBST_VARS.id=\\tPREFIX\".", 1237 "with \"SUBST_VARS.id=\\tPREFIX\".",
872 "AUTOFIX: filename.mk:5: "+ 1238 "AUTOFIX: filename.mk:5: "+
873 "Replacing \"SUBST_VARS.id=\\t\" "+ 1239 "Replacing \"SUBST_VARS.id=\\t\" "+
874 "with \"SUBST_VARS.id+=\\t\".") 1240 "with \"SUBST_VARS.id+=\\t\".")
875 1241
876 t.CheckFileLinesDetab("filename.mk", 1242 t.CheckFileLinesDetab("filename.mk",
877 "SUBST_CLASSES+= id", 1243 "SUBST_CLASSES+= id",
878 "SUBST_STAGE.id= pre-configure", 1244 "SUBST_STAGE.id= pre-configure",
879 "SUBST_FILES.id= filename", 1245 "SUBST_FILES.id= filename",
880 "SUBST_VARS.id= PREFIX", 1246 "SUBST_VARS.id= PREFIX",
881 "SUBST_VARS.id+= PKGMANDIR") 1247 "SUBST_VARS.id+= PKGMANDIR")
882} 1248}
883 1249
884func (s *Suite) Test_SubstContext_suggestSubstVars__autofix_indentation(c *check.C) { 1250func (s *Suite) Test_substBlock_suggestSubstVars__autofix_indentation(c *check.C) {
885 t := s.Init(c) 1251 t := s.Init(c)
886 1252
887 t.Chdir(".") 1253 t.Chdir(".")
888 1254
889 doTest := t.NewSubstAutofixTest( 1255 doTest := t.NewSubstAutofixTest(
890 "SUBST_CLASSES+=\t\t\tfix-paths", 1256 "SUBST_CLASSES+=\t\t\tfix-paths",
891 "SUBST_STAGE.fix-paths=\t\tpre-configure", 1257 "SUBST_STAGE.fix-paths=\t\tpre-configure",
892 "SUBST_MESSAGE.fix-paths=\tMessage", 1258 "SUBST_MESSAGE.fix-paths=\tMessage",
893 "SUBST_FILES.fix-paths=\t\tfilename", 1259 "SUBST_FILES.fix-paths=\t\tfilename",
894 "SUBST_SED.fix-paths=\t\t-e s,@PREFIX@,${PREFIX},g") 1260 "SUBST_SED.fix-paths=\t\t-e s,@PREFIX@,${PREFIX},g")
895 1261
896 t.ExpectDiagnosticsAutofix( 1262 t.ExpectDiagnosticsAutofix(
897 doTest, 1263 doTest,
@@ -901,454 +1267,286 @@ func (s *Suite) Test_SubstContext_sugges @@ -901,454 +1267,286 @@ func (s *Suite) Test_SubstContext_sugges
901 "can be replaced with \"SUBST_VARS.fix-paths= PREFIX\".", 1267 "can be replaced with \"SUBST_VARS.fix-paths= PREFIX\".",
902 "AUTOFIX: filename.mk:5: Replacing "+ 1268 "AUTOFIX: filename.mk:5: Replacing "+
903 "\"SUBST_SED.fix-paths=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+ 1269 "\"SUBST_SED.fix-paths=\\t\\t-e s,@PREFIX@,${PREFIX},g\" "+
904 "with \"SUBST_VARS.fix-paths=\\t\\tPREFIX\".") 1270 "with \"SUBST_VARS.fix-paths=\\t\\tPREFIX\".")
905 1271
906 t.CheckFileLinesDetab("filename.mk", 1272 t.CheckFileLinesDetab("filename.mk",
907 "SUBST_CLASSES+= fix-paths", 1273 "SUBST_CLASSES+= fix-paths",
908 "SUBST_STAGE.fix-paths= pre-configure", 1274 "SUBST_STAGE.fix-paths= pre-configure",
909 "SUBST_MESSAGE.fix-paths= Message", 1275 "SUBST_MESSAGE.fix-paths= Message",
910 "SUBST_FILES.fix-paths= filename", 1276 "SUBST_FILES.fix-paths= filename",
911 "SUBST_VARS.fix-paths= PREFIX") 1277 "SUBST_VARS.fix-paths= PREFIX")
912} 1278}
913 1279
914func (s *Suite) Test_SubstContext_suggestSubstVars__conditional(c *check.C) { 1280func (s *Suite) Test_substBlock_suggestSubstVars__conditional(c *check.C) {
915 t := s.Init(c) 1281 t := s.Init(c)
916 1282
917 t.Chdir(".") 1283 t.Chdir(".")
918 1284
919 doTest := t.NewSubstAutofixTest( 1285 doTest := t.NewSubstAutofixTest(
920 "SUBST_CLASSES+= id", 1286 "SUBST_CLASSES+= id",
921 "SUBST_STAGE.id= pre-configure", 1287 "SUBST_STAGE.id= pre-configure",
922 "SUBST_FILES.id= files", 1288 "SUBST_FILES.id= files",
923 "SUBST_SED.id= -e s,@VAR@,${VAR},", 1289 "SUBST_SED.id= -e s,@VAR@,${VAR},",
924 ".if 1", 1290 ".if 1",
925 "SUBST_SED.id+= -e s,@VAR2@,${VAR2},", 1291 "SUBST_SED.id+= -e s,@VAR2@,${VAR2},",
926 ".endif") 1292 ".endif")
927 1293
928 t.ExpectDiagnosticsAutofix( 1294 t.ExpectDiagnosticsAutofix(
929 doTest, 1295 doTest,
930 1296
931 "NOTE: filename.mk:4: The substitution command \"s,@VAR@,${VAR},\" "+ 1297 "NOTE: filename.mk:4: The substitution command \"s,@VAR@,${VAR},\" "+
932 "can be replaced with \"SUBST_VARS.id= VAR\".", 1298 "can be replaced with \"SUBST_VARS.id= VAR\".",
933 "NOTE: filename.mk:6: The substitution command \"s,@VAR2@,${VAR2},\" "+ 1299 "NOTE: filename.mk:6: The substitution command \"s,@VAR2@,${VAR2},\" "+
934 "can be replaced with \"SUBST_VARS.id+= VAR2\".", 1300 "can be replaced with \"SUBST_VARS.id+= VAR2\".",
935 "AUTOFIX: filename.mk:4: Replacing \"SUBST_SED.id= -e s,@VAR@,${VAR},\" "+ 1301 "AUTOFIX: filename.mk:4: Replacing \"SUBST_SED.id= -e s,@VAR@,${VAR},\" "+
936 "with \"SUBST_VARS.id=\\tVAR\".", 1302 "with \"SUBST_VARS.id=\\tVAR\".",
937 "AUTOFIX: filename.mk:6: Replacing \"SUBST_SED.id+= -e s,@VAR2@,${VAR2},\" "+ 1303 "AUTOFIX: filename.mk:6: Replacing \"SUBST_SED.id+= -e s,@VAR2@,${VAR2},\" "+
938 "with \"SUBST_VARS.id+=\\tVAR2\".") 1304 "with \"SUBST_VARS.id+=\\tVAR2\".")
939 1305
940 t.CheckFileLinesDetab("filename.mk", 1306 t.CheckFileLinesDetab("filename.mk",
941 "SUBST_CLASSES+= id", 1307 "SUBST_CLASSES+= id",
942 "SUBST_STAGE.id= pre-configure", 1308 "SUBST_STAGE.id= pre-configure",
943 "SUBST_FILES.id= files", 1309 "SUBST_FILES.id= files",
944 "SUBST_VARS.id= VAR", 1310 "SUBST_VARS.id= VAR",
945 ".if 1", 1311 ".if 1",
946 "SUBST_VARS.id+= VAR2", 1312 "SUBST_VARS.id+= VAR2",
947 ".endif") 
948} 
949 
950func (s *Suite) Test_SubstContext_extractVarname(c *check.C) { 
951 t := s.Init(c) 
952 
953 test := func(input, expected string) { 
954 t.CheckEquals((*SubstContext).extractVarname(nil, input), expected) 
955 } 
956 
957 // A simple variable name. 
958 test("s,@VAR@,${VAR},", "VAR") 
959 
960 // A parameterized variable name. 
961 test("s,@VAR.param@,${VAR.param},", "VAR.param") 
962 
963 // Only substitution commands can be replaced with SUBST_VARS. 
964 test("/pattern/d", "") 
965 
966 // An incomplete substitution command. 
967 test("s", "") 
968 
969 // Wrong placeholder character, only @ works. 
970 test("s,!VAR!,${VAR},", "") 
971 
972 // The placeholder must have exactly 1 @ on each side. 
973 test("s,@@VAR@@,${VAR},", "") 
974 
975 // Malformed because the comma is the separator. 
976 test("s,@VAR,VAR@,${VAR},", "") 
977 
978 // The replacement pattern is not a simple variable name enclosed in @. 
979 test("s,@VAR!VAR@,${VAR},", "") 
980 
981 // The replacement may only contain the :Q modifier. 
982 test("s,@VAR@,${VAR:Mpattern},", "") 
983 
984 // The :Q modifier is allowed in the replacement. 
985 test("s,@VAR@,${VAR:Q},", "VAR") 
986 
987 // The replacement may contain the :Q modifier only once. 
988 test("s,@VAR@,${VAR:Q:Q},", "") 
989 
990 // The replacement must be a plain variable expression, without prefix. 
991 test("s,@VAR@,prefix${VAR},", "") 
992 
993 // The replacement must be a plain variable expression, without suffix. 
994 test("s,@VAR@,${VAR}suffix,", "") 
995} 
996 
997func (s *Suite) Test_SubstContext_directive__before_SUBST_CLASSES(c *check.C) { 
998 t := s.Init(c) 
999 
1000 t.RunSubst( 
1001 ".if 0", 
1002 ".endif", 
1003 "SUBST_CLASSES+=\tos", 
1004 ".elif 0") // Just for branch coverage. 
1005 
1006 t.CheckOutputLines( 
1007 "WARN: filename.mk:EOF: Missing SUBST block for \"os\".") 
1008} 
1009 
1010func (s *Suite) Test_SubstContext_directive__conditional_blocks_complete(c *check.C) { 
1011 t := s.Init(c) 
1012 
1013 t.RunSubst( 
1014 ".if ${OPSYS} == NetBSD", 
1015 "SUBST_CLASSES+= nb", 
1016 "SUBST_STAGE.nb= post-configure", 
1017 "SUBST_FILES.nb= guess-netbsd.h", 
1018 "SUBST_VARS.nb= HAVE_NETBSD", 
1019 ".else", 
1020 "SUBST_CLASSES+= os", 
1021 "SUBST_STAGE.os= post-configure", 
1022 "SUBST_FILES.os= guess-netbsd.h", 
1023 "SUBST_VARS.os= HAVE_OTHER", 
1024 ".endif") 
1025 
1026 t.CheckOutputEmpty() 
1027} 
1028 
1029func (s *Suite) Test_SubstContext_directive__conditional_blocks_incomplete(c *check.C) { 
1030 t := s.Init(c) 
1031 
1032 t.RunSubst( 
1033 ".if ${OPSYS} == NetBSD", 
1034 "SUBST_CLASSES+= nb", 
1035 "SUBST_STAGE.nb= post-configure", 
1036 "SUBST_VARS.nb= HAVE_NETBSD", 
1037 ".else", 
1038 "SUBST_CLASSES+= os", 
1039 "SUBST_STAGE.os= post-configure", 
1040 "SUBST_FILES.os= guess-netbsd.h", 
1041 ".endif") 
1042 
1043 t.CheckOutputLines( 
1044 "WARN: filename.mk:5: Incomplete SUBST block: SUBST_FILES.nb missing.", 
1045 "WARN: filename.mk:9: Incomplete SUBST block: "+ 
1046 "SUBST_SED.os, SUBST_VARS.os or SUBST_FILTER_CMD.os missing.") 
1047} 
1048 
1049func (s *Suite) Test_SubstContext_directive__conditional_complete(c *check.C) { 
1050 t := s.Init(c) 
1051 
1052 t.RunSubst( 
1053 "SUBST_CLASSES+= id", 
1054 ".if ${OPSYS} == NetBSD", 
1055 "SUBST_STAGE.id=\t\tpost-configure", 
1056 "SUBST_MESSAGE.id=\tpost-configure", 
1057 "SUBST_FILES.id=\t\tguess-netbsd.h", 
1058 "SUBST_SED.id=\t\t-e s,from,to,", 
1059 "SUBST_VARS.id=\t\tHAVE_OTHER", 
1060 "SUBST_FILTER_CMD.id=\tHAVE_OTHER", 
1061 ".else", 
1062 "SUBST_STAGE.id=\t\tpost-configure", 
1063 "SUBST_MESSAGE.id=\tpost-configure", 
1064 "SUBST_FILES.id=\t\tguess-netbsd.h", 
1065 "SUBST_SED.id=\t\t-e s,from,to,", 
1066 "SUBST_VARS.id=\t\tHAVE_OTHER", 
1067 "SUBST_FILTER_CMD.id=\tHAVE_OTHER", 
1068 ".endif") 
1069 
1070 t.CheckOutputLines( 
1071 "WARN: filename.mk:3: SUBST_STAGE.id should not be defined conditionally.", 
1072 "WARN: filename.mk:4: SUBST_MESSAGE.id should not be defined conditionally.", 
1073 "WARN: filename.mk:10: SUBST_STAGE.id should not be defined conditionally.", 
1074 "WARN: filename.mk:11: SUBST_MESSAGE.id should not be defined conditionally.") 
1075} 
1076 
1077func (s *Suite) Test_SubstContext_directive__conditionally_overwritten_filter(c *check.C) { 
1078 t := s.Init(c) 
1079 
1080 t.RunSubst( 
1081 "SUBST_CLASSES+= id", 
1082 "SUBST_STAGE.id=\t\tpost-configure", 
1083 "SUBST_MESSAGE.id=\tpost-configure", 
1084 "SUBST_FILES.id=\t\tguess-netbsd.h", 
1085 "SUBST_FILTER_CMD.id=\tHAVE_OTHER", 
1086 ".if ${OPSYS} == NetBSD", 
1087 "SUBST_FILTER_CMD.id=\tHAVE_OTHER", 
1088 ".endif") 
1089 
1090 t.CheckOutputLines( 
1091 "WARN: filename.mk:7: Duplicate definition of \"SUBST_FILTER_CMD.id\".") 
1092} 
1093 
1094// Hopefully nobody will ever trigger this case in real pkgsrc. 
1095// It's plain confusing to a casual reader to nest a complete 
1096// SUBST block into another SUBST block. 
1097// That's why pkglint doesn't cover this case correctly. 
1098func (s *Suite) Test_SubstContext_directive__conditionally_nested_block(c *check.C) { 
1099 t := s.Init(c) 
1100 
1101 t.RunSubst( 
1102 "SUBST_CLASSES+= outer", 
1103 "SUBST_STAGE.outer= post-configure", 
1104 "SUBST_FILES.outer= outer.txt", 
1105 ".if ${OPSYS} == NetBSD", 
1106 "SUBST_CLASSES+= inner", 
1107 "SUBST_STAGE.inner= post-configure", 
1108 "SUBST_FILES.inner= inner.txt", 
1109 "SUBST_VARS.inner= INNER", 
1110 ".endif", 
1111 "SUBST_VARS.outer= OUTER") 
1112 
1113 t.CheckOutputLines( 
1114 "WARN: filename.mk:5: Incomplete SUBST block: "+ 
1115 "SUBST_SED.outer, SUBST_VARS.outer or SUBST_FILTER_CMD.outer missing.", 
1116 "WARN: filename.mk:5: Subst block \"outer\" should be finished "+ 
1117 "before adding the next class to SUBST_CLASSES.", 
1118 "WARN: filename.mk:10: "+ 
1119 "Late additions to a SUBST variable should use the += operator.") 
1120} 
1121 
1122// It's completely valid to have several SUBST blocks in a single paragraph. 
1123// As soon as a SUBST_CLASSES line appears, pkglint assumes that all previous 
1124// SUBST blocks are finished. That's exactly the case here. 
1125func (s *Suite) Test_SubstContext_directive__conditionally_following_block(c *check.C) { 
1126 t := s.Init(c) 
1127 
1128 t.RunSubst( 
1129 "SUBST_CLASSES+= outer", 
1130 "SUBST_STAGE.outer= post-configure", 
1131 "SUBST_FILES.outer= outer.txt", 
1132 "SUBST_VARS.outer= OUTER", 
1133 ".if ${OPSYS} == NetBSD", 
1134 "SUBST_CLASSES+= middle", 
1135 "SUBST_STAGE.middle= post-configure", 
1136 "SUBST_FILES.middle= inner.txt", 
1137 "SUBST_VARS.middle= INNER", 
1138 ". if ${MACHINE_ARCH} == amd64", 
1139 "SUBST_CLASSES+= inner", 
1140 "SUBST_STAGE.inner= post-configure", 
1141 "SUBST_FILES.inner= inner.txt", 
1142 "SUBST_VARS.inner= INNER", 
1143 ". endif", 
1144 ".endif") 1313 ".endif")
1145 
1146 t.CheckOutputEmpty() 
1147} 1314}
1148 1315
1149func (s *Suite) Test_SubstContext_directive__two_blocks_in_condition(c *check.C) { 1316func (s *Suite) Test_substBlock_dupList__late_addition(c *check.C) {
1150 t := s.Init(c) 1317 t := s.Init(c)
1151 1318
1152 t.RunSubst( 1319 t.RunSubst(
 1320 "SUBST_CLASSES+=\tid",
 1321 "SUBST_STAGE.id=\tpost-configure",
 1322 "SUBST_FILES.id=\tfiles",
 1323 "SUBST_VARS.id=\tPREFIX",
 1324 "",
1153 ".if ${OPSYS} == NetBSD", 1325 ".if ${OPSYS} == NetBSD",
1154 "SUBST_CLASSES+= a", 1326 "SUBST_VARS.id=\tOPSYS",
1155 "SUBST_STAGE.a= post-configure", 
1156 "SUBST_FILES.a= outer.txt", 
1157 "SUBST_VARS.a= OUTER", 
1158 "SUBST_CLASSES+= b", 
1159 "SUBST_STAGE.b= post-configure", 
1160 "SUBST_FILES.b= inner.txt", 
1161 "SUBST_VARS.b= INNER", 
1162 ".endif") 1327 ".endif")
1163 1328
1164 // Up to 2019-12-12, pkglint wrongly warned in filename.mk:6: 1329 t.CheckOutputLines(
1165 // Subst block "a" should be finished before adding 1330 "WARN: filename.mk:7: All but the first assignment " +
1166 // the next class to SUBST_CLASSES. 1331 "to \"SUBST_VARS.id\" should use the \"+=\" operator.")
1167 // The warning was wrong since block "a" has all required fields set. 
1168 // The warning was caused by an inconsistent check whether the current 
1169 // block had any conditional variables. 
1170 t.CheckOutputEmpty() 
1171} 1332}
1172 1333
1173func (s *Suite) Test_SubstContext_directive__nested_conditional_incomplete_block(c *check.C) { 1334func (s *Suite) Test_substBlock_dupList__conditional_before_unconditional(c *check.C) {
1174 t := s.Init(c) 1335 t := s.Init(c)
1175 1336
1176 t.RunSubst( 1337 t.RunSubst(
1177 "SUBST_CLASSES+= outer", 1338 "SUBST_CLASSES+= os",
1178 "SUBST_STAGE.outer= post-configure", 1339 "SUBST_STAGE.os= post-configure",
1179 "SUBST_FILES.outer= outer.txt", 1340 ".if 1",
1180 "SUBST_VARS.outer= OUTER", 1341 "SUBST_FILES.os= conditional",
1181 ".if ${OPSYS} == NetBSD", 1342 ".endif",
1182 "SUBST_CLASSES+= inner1", 1343 "SUBST_FILES.os= unconditional",
1183 "SUBST_STAGE.inner1= post-configure", 1344 "SUBST_VARS.os= OPSYS")
1184 "SUBST_VARS.inner1= INNER", 
1185 "SUBST_CLASSES+= inner2", 
1186 "SUBST_STAGE.inner2= post-configure", 
1187 "SUBST_FILES.inner2= inner.txt", 
1188 "SUBST_VARS.inner2= INNER", 
1189 ".endif") 
1190 1345
1191 t.CheckOutputLines( 1346 t.CheckOutputLines(
1192 "WARN: filename.mk:9: Incomplete SUBST block: SUBST_FILES.inner1 missing.", 1347 "WARN: filename.mk:6: All but the first assignment " +
1193 "WARN: filename.mk:9: Subst block \"inner1\" should be finished "+ 1348 "to \"SUBST_FILES.os\" should use the \"+=\" operator.")
1194 "before adding the next class to SUBST_CLASSES.") 
1195} 1349}
1196 1350
1197func (s *Suite) Test_SubstContext_finishClass__details_in_then_branch(c *check.C) { 1351func (s *Suite) Test_substBlock_extractVarname(c *check.C) {
1198 t := s.Init(c) 1352 t := s.Init(c)
1199 1353
1200 t.RunSubst( 1354 test := func(input, expected string) {
1201 "SUBST_CLASSES+= os", 1355 t.CheckEquals((*substBlock).extractVarname(nil, input), expected)
1202 ".if ${OPSYS} == NetBSD", 1356 }
1203 "SUBST_VARS.os= OPSYS", 
1204 "SUBST_SED.os= -e s,@OPSYS@,NetBSD,", 
1205 "SUBST_STAGE.os= post-configure", 
1206 "SUBST_MESSAGE.os= Guessing operating system", 
1207 "SUBST_FILES.os= guess-os.h", 
1208 ".endif") 
1209 1357
1210 t.CheckOutputLines( 1358 // A simple variable name.
1211 "WARN: filename.mk:5: SUBST_STAGE.os should not be defined conditionally.", 1359 test("s,@VAR@,${VAR},", "VAR")
1212 "WARN: filename.mk:6: SUBST_MESSAGE.os should not be defined conditionally.", 
1213 "WARN: filename.mk:EOF: Missing SUBST block for \"os\".") 
1214} 
1215 1360
1216func (s *Suite) Test_SubstContext_finishClass__details_in_else_branch(c *check.C) { 1361 // A parameterized variable name.
1217 t := s.Init(c) 1362 test("s,@VAR.param@,${VAR.param},", "VAR.param")
1218 1363
1219 t.RunSubst( 1364 // Only substitution commands can be replaced with SUBST_VARS.
1220 "SUBST_CLASSES+= os", 1365 test("/pattern/d", "")
1221 ".if ${OPSYS} == NetBSD", 
1222 ".else", 
1223 "SUBST_VARS.os= OPSYS", 
1224 "SUBST_SED.os= -e s,@OPSYS@,NetBSD,", 
1225 "SUBST_STAGE.os= post-configure", 
1226 "SUBST_MESSAGE.os= Guessing operating system", 
1227 "SUBST_FILES.os= guess-os.h", 
1228 ".endif") 
1229 1366
1230 t.CheckOutputLines( 1367 // An incomplete substitution command.
1231 "WARN: filename.mk:6: SUBST_STAGE.os should not be defined conditionally.", 1368 test("s", "")
1232 "WARN: filename.mk:7: SUBST_MESSAGE.os should not be defined conditionally.", 
1233 "WARN: filename.mk:EOF: Missing SUBST block for \"os\".") 
1234} 
1235 1369
1236func (s *Suite) Test_SubstContext_finishClass__empty_conditional_at_end(c *check.C) { 1370 // Wrong placeholder character, only @ works.
1237 t := s.Init(c) 1371 test("s,!VAR!,${VAR},", "")
1238 1372
1239 t.RunSubst( 1373 // The placeholder must have exactly 1 @ on each side.
1240 "SUBST_CLASSES+= os", 1374 test("s,@@VAR@@,${VAR},", "")
1241 "SUBST_VARS.os= OPSYS", 
1242 "SUBST_SED.os= -e s,@OPSYS@,NetBSD,", 
1243 "SUBST_STAGE.os= post-configure", 
1244 "SUBST_MESSAGE.os= Guessing operating system", 
1245 "SUBST_FILES.os= guess-os.h", 
1246 ".if ${OPSYS} == NetBSD", 
1247 ".else", 
1248 ".endif") 
1249 1375
1250 t.CheckOutputEmpty() 1376 // Malformed because the comma is the separator.
1251} 1377 test("s,@VAR,VAR@,${VAR},", "")
1252 1378
1253func (s *Suite) Test_SubstContext_finishClass__missing_transformation_in_one_branch(c *check.C) { 1379 // The replacement pattern is not a simple variable name enclosed in @.
1254 t := s.Init(c) 1380 test("s,@VAR!VAR@,${VAR},", "")
1255 1381
1256 t.RunSubst( 1382 // The replacement may only contain the :Q modifier.
1257 "SUBST_CLASSES+= os", 1383 test("s,@VAR@,${VAR:Mpattern},", "")
1258 "SUBST_STAGE.os= post-configure", 
1259 "SUBST_MESSAGE.os= Guessing operating system", 
1260 "SUBST_FILES.os= guess-os.h", 
1261 ".if ${OPSYS} == NetBSD", 
1262 "SUBST_FILES.os= -e s,@OpSYS@,NetBSD,", // A simple typo, this should be SUBST_SED. 
1263 ".elif ${OPSYS} == Darwin", 
1264 "SUBST_SED.os= -e s,@OPSYS@,Darwin1,", 
1265 "SUBST_SED.os= -e s,@OPSYS@,Darwin2,", 
1266 ".else", 
1267 "SUBST_VARS.os= OPSYS", 
1268 ".endif") 
1269 1384
1270 t.CheckOutputLines( 1385 // The :Q modifier is allowed in the replacement.
1271 "WARN: filename.mk:6: All but the first assignment "+ 1386 test("s,@VAR@,${VAR:Q},", "VAR")
1272 "to \"SUBST_FILES.os\" should use the \"+=\" operator.", 
1273 "WARN: filename.mk:9: All but the first assignment "+ 
1274 "to \"SUBST_SED.os\" should use the \"+=\" operator.", 
1275 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_SED.os, "+ 
1276 "SUBST_VARS.os or SUBST_FILTER_CMD.os missing.") 
1277} 
1278 1387
1279func (s *Suite) Test_SubstContext_finishClass__nested_conditionals(c *check.C) { 1388 // The replacement may contain the :Q modifier only once.
1280 t := s.Init(c) 1389 test("s,@VAR@,${VAR:Q:Q},", "")
1281 1390
1282 t.RunSubst( 1391 // The replacement must be a plain variable expression, without prefix.
1283 "SUBST_CLASSES+= os", 1392 test("s,@VAR@,prefix${VAR},", "")
1284 "SUBST_STAGE.os= post-configure", 
1285 "SUBST_MESSAGE.os= Guessing operating system", 
1286 ".if ${OPSYS} == NetBSD", 
1287 "SUBST_FILES.os= guess-netbsd.h", 
1288 ". if ${ARCH} == i386", 
1289 "SUBST_FILTER_CMD.os= ${SED} -e s,@OPSYS,NetBSD-i386,", 
1290 ". elif ${ARCH} == x86_64", 
1291 "SUBST_VARS.os= OPSYS", 
1292 ". else", 
1293 "SUBST_SED.os= -e s,@OPSYS,NetBSD-unknown", 
1294 ". endif", 
1295 ".else", 
1296 // This branch omits SUBST_FILES. 
1297 "SUBST_SED.os= -e s,@OPSYS@,unknown,", 
1298 ".endif") 
1299 1393
1300 t.CheckOutputLines( 1394 // The replacement must be a plain variable expression, without suffix.
1301 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.os missing.") 1395 test("s,@VAR@,${VAR}suffix,", "")
1302} 1396}
1303 1397
1304func (s *Suite) Test_SubstContext_isComplete__incomplete(c *check.C) { 1398func (s *Suite) Test_substBlock_isComplete__incomplete(c *check.C) {
1305 t := s.Init(c) 1399 t := s.Init(c)
1306 1400
1307 ctx := NewSubstContext() 1401 ctx := NewSubstContext()
1308 1402
1309 ctx.varassign(t.NewMkLine("filename.mk", 10, "PKGNAME=pkgname-1.0")) 1403 ctx.varassign(t.NewMkLine("filename.mk", 10, "PKGNAME=pkgname-1.0"))
1310 1404
1311 t.CheckEquals(ctx.id, "") 1405 t.CheckEquals(ctx.isActive(), false)
1312 1406
1313 ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES+=interp")) 1407 ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES+=interp"))
1314 1408
1315 t.CheckEquals(ctx.id, "interp") 1409 t.CheckEquals(ctx.isActive(), false)
1316 1410
1317 ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_FILES.interp=Makefile")) 1411 ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_FILES.interp=Makefile"))
1318 1412
1319 t.CheckEquals(ctx.isComplete(), false) 1413 t.CheckEquals(ctx.activeId(), "interp")
 1414 t.CheckEquals(ctx.block().isComplete(), false)
1320 1415
1321 ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_SED.interp=s,@PREFIX@,${PREFIX},g")) 1416 ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_SED.interp=s,@PREFIX@,${PREFIX},g"))
1322 1417
1323 t.CheckEquals(ctx.isComplete(), false) 1418 t.CheckEquals(ctx.block().isComplete(), false)
1324 1419
1325 ctx.Finish(t.NewMkLine("filename.mk", 14, "")) 1420 ctx.Finish(t.NewMkLine("filename.mk", 14, ""))
1326 1421
1327 t.CheckOutputLines( 1422 t.CheckOutputLines(
1328 "NOTE: filename.mk:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" "+ 1423 "NOTE: filename.mk:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" "+
1329 "can be replaced with \"SUBST_VARS.interp= PREFIX\".", 1424 "can be replaced with \"SUBST_VARS.interp= PREFIX\".",
1330 "WARN: filename.mk:14: Incomplete SUBST block: SUBST_STAGE.interp missing.") 1425 "WARN: filename.mk:14: Incomplete SUBST block: SUBST_STAGE.interp missing.")
1331} 1426}
1332 1427
1333func (s *Suite) Test_SubstContext_isComplete__complete(c *check.C) { 1428func (s *Suite) Test_substBlock_isComplete__complete(c *check.C) {
1334 t := s.Init(c) 1429 t := s.Init(c)
1335 1430
1336 ctx := NewSubstContext() 1431 ctx := NewSubstContext()
1337 1432
1338 ctx.varassign(t.NewMkLine("filename.mk", 10, "PKGNAME=pkgname-1.0")) 1433 ctx.varassign(t.NewMkLine("filename.mk", 10, "PKGNAME=pkgname-1.0"))
1339 ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES+=p")) 1434 ctx.varassign(t.NewMkLine("filename.mk", 11, "SUBST_CLASSES+=p"))
1340 ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_FILES.p=Makefile")) 1435 ctx.varassign(t.NewMkLine("filename.mk", 12, "SUBST_FILES.p=Makefile"))
1341 ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_SED.p=s,@PREFIX@,${PREFIX},g")) 1436 ctx.varassign(t.NewMkLine("filename.mk", 13, "SUBST_SED.p=s,@PREFIX@,${PREFIX},g"))
1342 1437
1343 t.CheckEquals(ctx.isComplete(), false) 1438 t.CheckEquals(ctx.block().isComplete(), false)
1344 1439
1345 ctx.varassign(t.NewMkLine("filename.mk", 14, "SUBST_STAGE.p=post-configure")) 1440 ctx.varassign(t.NewMkLine("filename.mk", 14, "SUBST_STAGE.p=post-configure"))
1346 1441
1347 t.CheckEquals(ctx.isComplete(), true) 1442 t.CheckEquals(ctx.block().isComplete(), true)
1348 1443
1349 ctx.Finish(t.NewMkLine("filename.mk", 15, "")) 1444 ctx.Finish(t.NewMkLine("filename.mk", 15, ""))
1350 1445
1351 t.CheckOutputLines( 1446 t.CheckOutputLines(
1352 "NOTE: filename.mk:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" " + 1447 "NOTE: filename.mk:13: The substitution command \"s,@PREFIX@,${PREFIX},g\" " +
1353 "can be replaced with \"SUBST_VARS.p= PREFIX\".") 1448 "can be replaced with \"SUBST_VARS.p= PREFIX\".")
1354} 1449}
 1450
 1451func (s *Suite) Test_substBlock_finish__conditional_inside_then(c *check.C) {
 1452 t := s.Init(c)
 1453
 1454 t.RunSubst(
 1455 ".if ${OPSYS} == Linux",
 1456 "SUBST_CLASSES+=\tid",
 1457 "SUBST_STAGE.id=\tpre-configure",
 1458 "SUBST_SED.id=\t-e sahara",
 1459 ".else",
 1460 ".endif")
 1461
 1462 // The block already ends at the .else, not at the end of the file,
 1463 // since that is the scope where the SUBST id is defined.
 1464 t.CheckOutputLines(
 1465 "WARN: filename.mk:5: Incomplete SUBST block: SUBST_FILES.id missing.")
 1466}
 1467
 1468func (s *Suite) Test_substBlock_finish__conditional_inside_else(c *check.C) {
 1469 t := s.Init(c)
 1470
 1471 t.RunSubst(
 1472 ".if ${OPSYS} == Linux",
 1473 ".else",
 1474 "SUBST_CLASSES+=\tid",
 1475 "SUBST_STAGE.id=\tpre-configure",
 1476 "SUBST_SED.id=\t-e sahara",
 1477 ".endif")
 1478
 1479 // The block already ends at the .endif, not at the end of the file,
 1480 // since that is the scope where the SUBST id is defined.
 1481 t.CheckOutputLines(
 1482 "WARN: filename.mk:6: Incomplete SUBST block: SUBST_FILES.id missing.")
 1483}
 1484
 1485func (s *Suite) Test_substBlock_finish__files_missing(c *check.C) {
 1486 t := s.Init(c)
 1487
 1488 t.RunSubst(
 1489 "SUBST_CLASSES+= one",
 1490 "SUBST_STAGE.one= pre-configure",
 1491 "SUBST_CLASSES+= two",
 1492 "SUBST_STAGE.two= pre-configure",
 1493 "SUBST_FILES.two= two.txt",
 1494 "SUBST_SED.two= s,two,2,g")
 1495
 1496 t.CheckOutputLines(
 1497 "WARN: filename.mk:3: Subst block \"one\" should be finished "+
 1498 "before adding the next class to SUBST_CLASSES.",
 1499 "WARN: filename.mk:3: Incomplete SUBST block: SUBST_FILES.one missing.",
 1500 "WARN: filename.mk:3: Incomplete SUBST block: "+
 1501 "SUBST_SED.one, SUBST_VARS.one or SUBST_FILTER_CMD.one missing.")
 1502}
 1503
 1504// Variables mentioned in SUBST_VARS may appear in the same paragraph,
 1505// or alternatively anywhere else in the file.
 1506func (s *Suite) Test_substBlock_checkForeignVariables__in_next_paragraph(c *check.C) {
 1507 t := s.Init(c)
 1508
 1509 t.RunSubst(
 1510 "SUBST_CLASSES+=\tos",
 1511 "SUBST_STAGE.os=\tpre-configure",
 1512 "SUBST_FILES.os=\tguess-os.h",
 1513 "SUBST_VARS.os=\tTODAY1",
 1514 "",
 1515 "TODAY1!=\tdate",
 1516 "TODAY2!=\tdate")
 1517
 1518 t.CheckOutputEmpty()
 1519}
 1520
 1521func (s *Suite) Test_substBlock_checkForeignVariables__mixed_separate(c *check.C) {
 1522 t := s.Init(c)
 1523
 1524 t.RunSubst(
 1525 "SUBST_CLASSES+= 1",
 1526 "SUBST_STAGE.1= post-configure",
 1527 "SUBST_FILES.1= files",
 1528 "",
 1529 "SUBST_VARS.1= VAR",
 1530 "USE_TOOLS+= gmake")
 1531
 1532 // The USE_TOOLS is not in the SUBST block anymore since there is
 1533 // an empty line between SUBST_CLASSES and SUBST_VARS.
 1534 t.CheckOutputEmpty()
 1535}
 1536
 1537// Variables mentioned in SUBST_VARS are not considered "foreign"
 1538// in the block and may be mixed with the other SUBST variables.
 1539func (s *Suite) Test_substBlock_checkForeignVariables__in_block(c *check.C) {
 1540 t := s.Init(c)
 1541
 1542 t.RunSubst(
 1543 "SUBST_CLASSES+=\tos",
 1544 "SUBST_STAGE.os=\tpre-configure",
 1545 "SUBST_FILES.os=\tguess-os.h",
 1546 "SUBST_VARS.os=\tTODAY1",
 1547 "TODAY1!=\tdate",
 1548 "TODAY2!=\tdate")
 1549
 1550 t.CheckOutputLines(
 1551 "WARN: filename.mk:6: Foreign variable \"TODAY2\" in SUBST block.")
 1552}

cvs diff -r1.83 -r1.84 pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go 2019/12/13 01:39:23 1.83
+++ pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go 2019/12/14 18:04:16 1.84
@@ -508,27 +508,27 @@ func (reg *VarTypeRegistry) Init(src *Pk @@ -508,27 +508,27 @@ func (reg *VarTypeRegistry) Init(src *Pk
508 "57 56 55 51 MARIADB55", 508 "57 56 55 51 MARIADB55",
509 "MYSQL_VERSIONS_ACCEPTED") 509 "MYSQL_VERSIONS_ACCEPTED")
510 510
511 pgsqlVersions := reg.enumFrom(src, 511 pgsqlVersions := reg.enumFrom(src,
512 "mk/pgsql.buildlink3.mk", 512 "mk/pgsql.buildlink3.mk",
513 "10 96 95 94 93", 513 "10 96 95 94 93",
514 "PGSQL_VERSIONS_ACCEPTED") 514 "PGSQL_VERSIONS_ACCEPTED")
515 515
516 jvms := reg.enumFrom(src, 516 jvms := reg.enumFrom(src,
517 "mk/java-vm.mk", 517 "mk/java-vm.mk",
518 "openjdk8 oracle-jdk8 openjdk7 sun-jdk7 jdk16 jdk15 kaffe", 518 "openjdk8 oracle-jdk8 openjdk7 sun-jdk7 jdk16 jdk15 kaffe",
519 "_PKG_JVMS.*") 519 "_PKG_JVMS.*")
520 520
521 platforms := reg.enumFromFiles(src, 521 operatingSystems := reg.enumFromFiles(src,
522 "mk/platform", 522 "mk/platform",
523 `(.*)\.mk$`, "$1", 523 `(.*)\.mk$`, "$1",
524 "Cygwin DragonFly FreeBSD Linux NetBSD SunOS") 524 "Cygwin DragonFly FreeBSD Linux NetBSD SunOS")
525 525
526 // TODO: Only mark those variables as user-settable that actually influence 526 // TODO: Only mark those variables as user-settable that actually influence
527 // the generated packages. For example, UPDATE_TARGET doesn't. 527 // the generated packages. For example, UPDATE_TARGET doesn't.
528 528
529 // Last synced with mk/defaults/mk.conf revision 1.300 (abbf617a26f3). 529 // Last synced with mk/defaults/mk.conf revision 1.300 (abbf617a26f3).
530 reg.usr("USE_CWRAPPERS", enum("yes no auto")) 530 reg.usr("USE_CWRAPPERS", enum("yes no auto"))
531 reg.usr("ALLOW_VULNERABLE_PACKAGES", BtYes) 531 reg.usr("ALLOW_VULNERABLE_PACKAGES", BtYes)
532 reg.usrlist("AUDIT_PACKAGES_FLAGS", BtShellWord) 532 reg.usrlist("AUDIT_PACKAGES_FLAGS", BtShellWord)
533 reg.usrlist("MANINSTALL", enum("maninstall catinstall")) 533 reg.usrlist("MANINSTALL", enum("maninstall catinstall"))
534 reg.usr("MANZ", BtYes) 534 reg.usr("MANZ", BtYes)
@@ -1352,27 +1352,27 @@ func (reg *VarTypeRegistry) Init(src *Pk @@ -1352,27 +1352,27 @@ func (reg *VarTypeRegistry) Init(src *Pk
1352 reg.pkgload("NO_BUILD", BtYes) 1352 reg.pkgload("NO_BUILD", BtYes)
1353 reg.pkg("NO_CHECKSUM", BtYes) 1353 reg.pkg("NO_CHECKSUM", BtYes)
1354 reg.pkg("NO_CONFIGURE", BtYes) 1354 reg.pkg("NO_CONFIGURE", BtYes)
1355 reg.pkg("NO_EXPORT_CPP", BtYes) 1355 reg.pkg("NO_EXPORT_CPP", BtYes)
1356 reg.pkg("NO_EXTRACT", BtYes) 1356 reg.pkg("NO_EXTRACT", BtYes)
1357 reg.pkg("NO_INSTALL_MANPAGES", BtYes) // only has an effect for Imake packages. 1357 reg.pkg("NO_INSTALL_MANPAGES", BtYes) // only has an effect for Imake packages.
1358 reg.pkg("NO_PKGTOOLS_REQD_CHECK", BtYes) 1358 reg.pkg("NO_PKGTOOLS_REQD_CHECK", BtYes)
1359 reg.pkg("NO_SRC_ON_CDROM", BtRestricted) 1359 reg.pkg("NO_SRC_ON_CDROM", BtRestricted)
1360 reg.pkg("NO_SRC_ON_FTP", BtRestricted) 1360 reg.pkg("NO_SRC_ON_FTP", BtRestricted)
1361 reg.sysload("OBJECT_FMT", enum("COFF ECOFF ELF SOM XCOFF Mach-O PE a.out")) 1361 reg.sysload("OBJECT_FMT", enum("COFF ECOFF ELF SOM XCOFF Mach-O PE a.out"))
1362 reg.pkglistrat("ONLY_FOR_COMPILER", compilers) 1362 reg.pkglistrat("ONLY_FOR_COMPILER", compilers)
1363 reg.pkglistrat("ONLY_FOR_PLATFORM", BtMachinePlatformPattern) 1363 reg.pkglistrat("ONLY_FOR_PLATFORM", BtMachinePlatformPattern)
1364 reg.pkgrat("ONLY_FOR_UNPRIVILEGED", BtYesNo) 1364 reg.pkgrat("ONLY_FOR_UNPRIVILEGED", BtYesNo)
1365 reg.sysload("OPSYS", platforms, DefinedIfInScope|NonemptyIfDefined) 1365 reg.sysload("OPSYS", operatingSystems, DefinedIfInScope|NonemptyIfDefined)
1366 reg.pkglistbl3("OPSYSVARS", BtVariableName) 1366 reg.pkglistbl3("OPSYSVARS", BtVariableName)
1367 reg.pkg("OSVERSION_SPECIFIC", BtYes) 1367 reg.pkg("OSVERSION_SPECIFIC", BtYes)
1368 reg.sysload("OS_VARIANT", BtIdentifierDirect, DefinedIfInScope) 1368 reg.sysload("OS_VARIANT", BtIdentifierDirect, DefinedIfInScope)
1369 reg.sysload("OS_VERSION", BtVersion) 1369 reg.sysload("OS_VERSION", BtVersion)
1370 reg.sysload("OSX_VERSION", BtVersion) // See mk/platform/Darwin.mk. 1370 reg.sysload("OSX_VERSION", BtVersion) // See mk/platform/Darwin.mk.
1371 reg.pkg("OVERRIDE_DIRDEPTH*", BtInteger) 1371 reg.pkg("OVERRIDE_DIRDEPTH*", BtInteger)
1372 reg.pkg("OVERRIDE_GNU_CONFIG_SCRIPTS", BtYes) 1372 reg.pkg("OVERRIDE_GNU_CONFIG_SCRIPTS", BtYes)
1373 reg.pkg("OWNER", BtMailAddress) 1373 reg.pkg("OWNER", BtMailAddress)
1374 reg.pkglist("OWN_DIRS", BtPathPattern) 1374 reg.pkglist("OWN_DIRS", BtPathPattern)
1375 reg.pkglist("OWN_DIRS_PERMS", BtPerms) 1375 reg.pkglist("OWN_DIRS_PERMS", BtPerms)
1376 reg.sys("PAMBASE", BtPathname) 1376 reg.sys("PAMBASE", BtPathname)
1377 reg.usr("PAM_DEFAULT", enum("linux-pam openpam solaris-pam")) 1377 reg.usr("PAM_DEFAULT", enum("linux-pam openpam solaris-pam"))
1378 reg.pkgload("PATCHDIR", BtRelativePkgPath) 1378 reg.pkgload("PATCHDIR", BtRelativePkgPath)

cvs diff -r1.43 -r1.44 pkgsrc/pkgtools/pkglint/files/Attic/vartype.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/vartype.go 2019/12/13 01:39:23 1.43
+++ pkgsrc/pkgtools/pkglint/files/Attic/vartype.go 2019/12/14 18:04:16 1.44
@@ -457,53 +457,48 @@ var ( @@ -457,53 +457,48 @@ var (
457 BtURL = &BasicType{"URL", (*VartypeCheck).URL} 457 BtURL = &BasicType{"URL", (*VartypeCheck).URL}
458 BtUserGroupName = &BasicType{"UserGroupName", (*VartypeCheck).UserGroupName} 458 BtUserGroupName = &BasicType{"UserGroupName", (*VartypeCheck).UserGroupName}
459 BtVariableName = &BasicType{"VariableName", (*VartypeCheck).VariableName} 459 BtVariableName = &BasicType{"VariableName", (*VartypeCheck).VariableName}
460 BtVariableNamePattern = &BasicType{"VariableNamePattern", (*VartypeCheck).VariableNamePattern} 460 BtVariableNamePattern = &BasicType{"VariableNamePattern", (*VartypeCheck).VariableNamePattern}
461 BtVersion = &BasicType{"Version", (*VartypeCheck).Version} 461 BtVersion = &BasicType{"Version", (*VartypeCheck).Version}
462 BtWrapperReorder = &BasicType{"WrapperReorder", (*VartypeCheck).WrapperReorder} 462 BtWrapperReorder = &BasicType{"WrapperReorder", (*VartypeCheck).WrapperReorder}
463 BtWrapperTransform = &BasicType{"WrapperTransform", (*VartypeCheck).WrapperTransform} 463 BtWrapperTransform = &BasicType{"WrapperTransform", (*VartypeCheck).WrapperTransform}
464 BtWrkdirSubdirectory = &BasicType{"WrkdirSubdirectory", (*VartypeCheck).WrkdirSubdirectory} 464 BtWrkdirSubdirectory = &BasicType{"WrkdirSubdirectory", (*VartypeCheck).WrkdirSubdirectory}
465 BtWrksrcSubdirectory = &BasicType{"WrksrcSubdirectory", (*VartypeCheck).WrksrcSubdirectory} 465 BtWrksrcSubdirectory = &BasicType{"WrksrcSubdirectory", (*VartypeCheck).WrksrcSubdirectory}
466 BtYes = &BasicType{"Yes", (*VartypeCheck).Yes} 466 BtYes = &BasicType{"Yes", (*VartypeCheck).Yes}
467 BtYesNo = &BasicType{"YesNo", (*VartypeCheck).YesNo} 467 BtYesNo = &BasicType{"YesNo", (*VartypeCheck).YesNo}
468 BtYesNoIndirectly = &BasicType{"YesNoIndirectly", (*VartypeCheck).YesNoIndirectly} 468 BtYesNoIndirectly = &BasicType{"YesNoIndirectly", (*VartypeCheck).YesNoIndirectly}
469 469
470 BtMachineOpsys = enumFromValues(machineOpsysValues) 
471 BtMachineArch = enumFromValues(machineArchValues) 470 BtMachineArch = enumFromValues(machineArchValues)
472 BtMachineGnuArch = enumFromValues(machineGnuArchValues) 471 BtMachineGnuArch = enumFromValues(machineGnuArchValues)
473 BtEmulOpsys = enumFromValues(emulOpsysValues) 472 BtEmulOpsys = enumFromValues(emulOpsysValues)
474 BtEmulArch = enumFromValues(machineArchValues) // Just a wild guess. 473 BtEmulArch = enumFromValues(machineArchValues) // Just a wild guess.
475 BtMachineGnuPlatformOpsys = BtEmulOpsys 474 BtMachineGnuPlatformOpsys = BtEmulOpsys
476 475
477 btForLoop = &BasicType{".for loop", nil /* never called */} 476 btForLoop = &BasicType{".for loop", nil /* never called */}
478) 477)
479 478
480// Necessary due to circular dependencies between the checkers. 479// Necessary due to circular dependencies between the checkers.
481// 480//
482// The Go compiler is stricter than absolutely necessary for this particular case. 481// The Go compiler is stricter than absolutely necessary for this particular case.
483// The following methods are only referred to but not invoked during initialization. 482// The following methods are only referred to but not invoked during initialization.
484func init() { 483func init() {
485 BtShellCommand.checker = (*VartypeCheck).ShellCommand 484 BtShellCommand.checker = (*VartypeCheck).ShellCommand
486 BtShellCommands.checker = (*VartypeCheck).ShellCommands 485 BtShellCommands.checker = (*VartypeCheck).ShellCommands
487 BtShellWord.checker = (*VartypeCheck).ShellWord 486 BtShellWord.checker = (*VartypeCheck).ShellWord
488} 487}
489 488
490// TODO: Move these values to VarTypeRegistry.Init and read them from the 489// TODO: Move these values to VarTypeRegistry.Init and read them from the
491// pkgsrc infrastructure files, as far as possible. 490// pkgsrc infrastructure files, as far as possible.
492const ( 491const (
493 machineOpsysValues = "" + // See mk/platform 
494 "AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD " + 
495 "HPUX Haiku IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare" 
496 
497 // See mk/emulator/emulator-vars.mk. 492 // See mk/emulator/emulator-vars.mk.
498 emulOpsysValues = "" + 493 emulOpsysValues = "" +
499 "bitrig bsdos cygwin darwin dragonfly freebsd " + 494 "bitrig bsdos cygwin darwin dragonfly freebsd " +
500 "haiku hpux interix irix linux mirbsd netbsd openbsd osf1 solaris sunos" 495 "haiku hpux interix irix linux mirbsd netbsd openbsd osf1 solaris sunos"
501 496
502 // Hardware architectures having the same name in bsd.own.mk and the GNU world. 497 // Hardware architectures having the same name in bsd.own.mk and the GNU world.
503 // These are best-effort guesses, since they depend on the operating system. 498 // These are best-effort guesses, since they depend on the operating system.
504 archValues = "" + 499 archValues = "" +
505 "aarch64 alpha amd64 arc arm cobalt convex dreamcast i386 " + 500 "aarch64 alpha amd64 arc arm cobalt convex dreamcast i386 " +
506 "hpcmips hpcsh hppa hppa64 ia64 " + 501 "hpcmips hpcsh hppa hppa64 ia64 " +
507 "m68k m88k mips mips64 mips64el mipseb mipsel mipsn32 mlrisc " + 502 "m68k m88k mips mips64 mips64el mipseb mipsel mipsn32 mlrisc " +
508 "ns32k pc532 pmax powerpc powerpc64 rs6000 s390 sparc sparc64 vax x86_64" 503 "ns32k pc532 pmax powerpc powerpc64 rs6000 s390 sparc sparc64 vax x86_64"
509 504

cvs diff -r1.73 -r1.74 pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck.go 2019/12/13 01:39:23 1.73
+++ pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck.go 2019/12/14 18:04:16 1.74
@@ -789,28 +789,34 @@ func (cv *VartypeCheck) MachinePlatformP @@ -789,28 +789,34 @@ func (cv *VartypeCheck) MachinePlatformP
789 return 789 return
790 } 790 }
791 791
792 const rePart = `(?:\[[^\]]+\]|[^-\[])+` 792 const rePart = `(?:\[[^\]]+\]|[^-\[])+`
793 const rePair = `^(` + rePart + `)-(` + rePart + `)$` 793 const rePair = `^(` + rePart + `)-(` + rePart + `)$`
794 const reTriple = `^(` + rePart + `)-(` + rePart + `)-(` + rePart + `)$` 794 const reTriple = `^(` + rePart + `)-(` + rePart + `)-(` + rePart + `)$`
795 795
796 pattern := cv.Value 796 pattern := cv.Value
797 if matches(pattern, rePair) && hasSuffix(pattern, "*") { 797 if matches(pattern, rePair) && hasSuffix(pattern, "*") {
798 pattern += "-*" 798 pattern += "-*"
799 } 799 }
800 800
801 if m, opsysPattern, versionPattern, archPattern := match3(pattern, reTriple); m { 801 if m, opsysPattern, versionPattern, archPattern := match3(pattern, reTriple); m {
 802 // This is cheated, but the dynamically loaded enums are not
 803 // provided in a type registry where they could be looked up
 804 // by a name. The following line therefore assumes that OPSYS
 805 // is an operating system name, which sounds like a safe bet.
 806 btOpsys := G.Pkgsrc.vartypes.types["OPSYS"].basicType
 807
802 opsysCv := cv.WithVarnameValueMatch("the operating system part of "+cv.Varname, opsysPattern) 808 opsysCv := cv.WithVarnameValueMatch("the operating system part of "+cv.Varname, opsysPattern)
803 BtMachineOpsys.checker(opsysCv) 809 btOpsys.checker(opsysCv)
804 810
805 versionCv := cv.WithVarnameValueMatch("the version part of "+cv.Varname, versionPattern) 811 versionCv := cv.WithVarnameValueMatch("the version part of "+cv.Varname, versionPattern)
806 versionCv.Version() 812 versionCv.Version()
807 813
808 archCv := cv.WithVarnameValueMatch("the hardware architecture part of "+cv.Varname, archPattern) 814 archCv := cv.WithVarnameValueMatch("the hardware architecture part of "+cv.Varname, archPattern)
809 BtMachineArch.checker(archCv) 815 BtMachineArch.checker(archCv)
810 816
811 } else { 817 } else {
812 cv.Warnf("%q is not a valid platform pattern.", cv.Value) 818 cv.Warnf("%q is not a valid platform pattern.", cv.Value)
813 cv.Explain( 819 cv.Explain(
814 "A platform pattern has the form <OPSYS>-<OS_VERSION>-<MACHINE_ARCH>.", 820 "A platform pattern has the form <OPSYS>-<OS_VERSION>-<MACHINE_ARCH>.",
815 "Each of these components may be a shell globbing expression.", 821 "Each of these components may be a shell globbing expression.",
816 "", 822 "",

cvs diff -r1.66 -r1.67 pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck_test.go 2019/12/13 01:39:23 1.66
+++ pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck_test.go 2019/12/14 18:04:16 1.67
@@ -1166,43 +1166,41 @@ func (s *Suite) Test_VartypeCheck_Machin @@ -1166,43 +1166,41 @@ func (s *Suite) Test_VartypeCheck_Machin
1166 vt.Values( 1166 vt.Values(
1167 "linux-i386", 1167 "linux-i386",
1168 "nextbsd-5.0-8087", 1168 "nextbsd-5.0-8087",
1169 "netbsd-7.0-l*", 1169 "netbsd-7.0-l*",
1170 "NetBSD-1.6.2-i386", 1170 "NetBSD-1.6.2-i386",
1171 "FreeBSD*", 1171 "FreeBSD*",
1172 "FreeBSD-*", 1172 "FreeBSD-*",
1173 "${LINUX}", 1173 "${LINUX}",
1174 "NetBSD-[0-1]*-*") 1174 "NetBSD-[0-1]*-*")
1175 1175
1176 vt.Output( 1176 vt.Output(
1177 "WARN: filename.mk:1: \"linux-i386\" is not a valid platform pattern.", 1177 "WARN: filename.mk:1: \"linux-i386\" is not a valid platform pattern.",
1178 "WARN: filename.mk:2: The pattern \"nextbsd\" cannot match any of "+ 1178 "WARN: filename.mk:2: The pattern \"nextbsd\" cannot match any of "+
1179 "{ AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD HPUX Haiku "+ 1179 "{ Cygwin DragonFly FreeBSD Linux NetBSD SunOS "+
1180 "IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare "+ 
1181 "} for the operating system part of MACHINE_PLATFORM.", 1180 "} for the operating system part of MACHINE_PLATFORM.",
1182 "WARN: filename.mk:2: The pattern \"8087\" cannot match any of "+ 1181 "WARN: filename.mk:2: The pattern \"8087\" cannot match any of "+
1183 "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+ 1182 "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+
1184 "cobalt coldfire convex dreamcast "+ 1183 "cobalt coldfire convex dreamcast "+
1185 "earm earmeb earmhf earmhfeb earmv4 earmv4eb "+ 1184 "earm earmeb earmhf earmhfeb earmv4 earmv4eb "+
1186 "earmv5 earmv5eb earmv6 earmv6eb earmv6hf "+ 1185 "earmv5 earmv5eb earmv6 earmv6eb earmv6hf "+
1187 "earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+ 1186 "earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+
1188 "i386 i586 i686 ia64 m68000 m68k m88k "+ 1187 "i386 i586 i686 ia64 m68000 m68k m88k "+
1189 "mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+ 1188 "mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+
1190 "mlrisc ns32k pc532 pmax powerpc powerpc64 "+ 1189 "mlrisc ns32k pc532 pmax powerpc powerpc64 "+
1191 "rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+ 1190 "rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+
1192 "} for the hardware architecture part of MACHINE_PLATFORM.", 1191 "} for the hardware architecture part of MACHINE_PLATFORM.",
1193 "WARN: filename.mk:3: The pattern \"netbsd\" cannot match any of "+ 1192 "WARN: filename.mk:3: The pattern \"netbsd\" cannot match any of "+
1194 "{ AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD HPUX Haiku "+ 1193 "{ Cygwin DragonFly FreeBSD Linux NetBSD SunOS "+
1195 "IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare "+ 
1196 "} for the operating system part of MACHINE_PLATFORM.", 1194 "} for the operating system part of MACHINE_PLATFORM.",
1197 "WARN: filename.mk:3: The pattern \"l*\" cannot match any of "+ 1195 "WARN: filename.mk:3: The pattern \"l*\" cannot match any of "+
1198 "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+ 1196 "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+
1199 "cobalt coldfire convex dreamcast "+ 1197 "cobalt coldfire convex dreamcast "+
1200 "earm earmeb earmhf earmhfeb earmv4 earmv4eb "+ 1198 "earm earmeb earmhf earmhfeb earmv4 earmv4eb "+
1201 "earmv5 earmv5eb earmv6 earmv6eb earmv6hf "+ 1199 "earmv5 earmv5eb earmv6 earmv6eb earmv6hf "+
1202 "earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+ 1200 "earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+
1203 "i386 i586 i686 ia64 m68000 m68k m88k "+ 1201 "i386 i586 i686 ia64 m68000 m68k m88k "+
1204 "mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+ 1202 "mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+
1205 "mlrisc ns32k pc532 pmax powerpc powerpc64 "+ 1203 "mlrisc ns32k pc532 pmax powerpc powerpc64 "+
1206 "rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+ 1204 "rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+
1207 "} for the hardware architecture part of MACHINE_PLATFORM.", 1205 "} for the hardware architecture part of MACHINE_PLATFORM.",
1208 "WARN: filename.mk:5: \"FreeBSD*\" is not a valid platform pattern.", 1206 "WARN: filename.mk:5: \"FreeBSD*\" is not a valid platform pattern.",
@@ -1217,43 +1215,41 @@ func (s *Suite) Test_VartypeCheck_Machin @@ -1217,43 +1215,41 @@ func (s *Suite) Test_VartypeCheck_Machin
1217 vt.Values( 1215 vt.Values(
1218 "linux-i386", 1216 "linux-i386",
1219 "nextbsd-5.0-8087", 1217 "nextbsd-5.0-8087",
1220 "netbsd-7.0-l*", 1218 "netbsd-7.0-l*",
1221 "NetBSD-1.6.2-i386", 1219 "NetBSD-1.6.2-i386",
1222 "FreeBSD*", 1220 "FreeBSD*",
1223 "FreeBSD-*", 1221 "FreeBSD-*",
1224 "${LINUX}", 1222 "${LINUX}",
1225 "NetBSD-[0-1]*-*") 1223 "NetBSD-[0-1]*-*")
1226 1224
1227 vt.Output( 1225 vt.Output(
1228 "WARN: filename.mk:1: \"linux-i386\" is not a valid platform pattern.", 1226 "WARN: filename.mk:1: \"linux-i386\" is not a valid platform pattern.",
1229 "WARN: filename.mk:2: The pattern \"nextbsd\" cannot match any of "+ 1227 "WARN: filename.mk:2: The pattern \"nextbsd\" cannot match any of "+
1230 "{ AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD HPUX Haiku "+ 1228 "{ Cygwin DragonFly FreeBSD Linux NetBSD SunOS "+
1231 "IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare "+ 
1232 "} for the operating system part of ONLY_FOR_PLATFORM.", 1229 "} for the operating system part of ONLY_FOR_PLATFORM.",
1233 "WARN: filename.mk:2: The pattern \"8087\" cannot match any of "+ 1230 "WARN: filename.mk:2: The pattern \"8087\" cannot match any of "+
1234 "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+ 1231 "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+
1235 "cobalt coldfire convex dreamcast "+ 1232 "cobalt coldfire convex dreamcast "+
1236 "earm earmeb earmhf earmhfeb earmv4 earmv4eb "+ 1233 "earm earmeb earmhf earmhfeb earmv4 earmv4eb "+
1237 "earmv5 earmv5eb earmv6 earmv6eb earmv6hf "+ 1234 "earmv5 earmv5eb earmv6 earmv6eb earmv6hf "+
1238 "earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+ 1235 "earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+
1239 "i386 i586 i686 ia64 m68000 m68k m88k "+ 1236 "i386 i586 i686 ia64 m68000 m68k m88k "+
1240 "mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+ 1237 "mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+
1241 "mlrisc ns32k pc532 pmax powerpc powerpc64 "+ 1238 "mlrisc ns32k pc532 pmax powerpc powerpc64 "+
1242 "rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+ 1239 "rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+
1243 "} for the hardware architecture part of ONLY_FOR_PLATFORM.", 1240 "} for the hardware architecture part of ONLY_FOR_PLATFORM.",
1244 "WARN: filename.mk:3: The pattern \"netbsd\" cannot match any of "+ 1241 "WARN: filename.mk:3: The pattern \"netbsd\" cannot match any of "+
1245 "{ AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD HPUX Haiku "+ 1242 "{ Cygwin DragonFly FreeBSD Linux NetBSD SunOS "+
1246 "IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare "+ 
1247 "} for the operating system part of ONLY_FOR_PLATFORM.", 1243 "} for the operating system part of ONLY_FOR_PLATFORM.",
1248 "WARN: filename.mk:3: The pattern \"l*\" cannot match any of "+ 1244 "WARN: filename.mk:3: The pattern \"l*\" cannot match any of "+
1249 "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+ 1245 "{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+
1250 "cobalt coldfire convex dreamcast "+ 1246 "cobalt coldfire convex dreamcast "+
1251 "earm earmeb earmhf earmhfeb earmv4 earmv4eb "+ 1247 "earm earmeb earmhf earmhfeb earmv4 earmv4eb "+
1252 "earmv5 earmv5eb earmv6 earmv6eb earmv6hf "+ 1248 "earmv5 earmv5eb earmv6 earmv6eb earmv6hf "+
1253 "earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+ 1249 "earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+
1254 "i386 i586 i686 ia64 m68000 m68k m88k "+ 1250 "i386 i586 i686 ia64 m68000 m68k m88k "+
1255 "mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+ 1251 "mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+
1256 "mlrisc ns32k pc532 pmax powerpc powerpc64 "+ 1252 "mlrisc ns32k pc532 pmax powerpc powerpc64 "+
1257 "rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+ 1253 "rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+
1258 "} for the hardware architecture part of ONLY_FOR_PLATFORM.", 1254 "} for the hardware architecture part of ONLY_FOR_PLATFORM.",
1259 "WARN: filename.mk:5: \"FreeBSD*\" is not a valid platform pattern.", 1255 "WARN: filename.mk:5: \"FreeBSD*\" is not a valid platform pattern.",