1 1.19 rillig # $NetBSD: directive-include-guard.mk,v 1.19 2025/04/11 17:21:31 rillig Exp $ 2 1.1 rillig # 3 1.1 rillig # Tests for multiple-inclusion guards in makefiles. 4 1.1 rillig # 5 1.7 rillig # A file that is guarded by a multiple-inclusion guard has one of the 6 1.7 rillig # following forms: 7 1.1 rillig # 8 1.7 rillig # .ifndef GUARD_VARIABLE 9 1.1 rillig # .endif 10 1.1 rillig # 11 1.7 rillig # .if !defined(GUARD_VARIABLE) 12 1.7 rillig # .endif 13 1.7 rillig # 14 1.7 rillig # .if !target(guard-target) 15 1.7 rillig # .endif 16 1.7 rillig # 17 1.7 rillig # When such a file is included for the second or later time, and the guard 18 1.13 rillig # variable or the guard target is defined, the file is skipped completely, as 19 1.13 rillig # including it would not have any effect, not even on the special variable 20 1.13 rillig # '.MAKE.MAKEFILES', as that variable skips duplicate pathnames. 21 1.1 rillig # 22 1.2 rillig # See also: 23 1.2 rillig # https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html 24 1.1 rillig 25 1.9 rillig # Each of the following test cases creates a temporary file named after the 26 1.9 rillig # test case and writes some lines of text to that file. That file is then 27 1.9 rillig # included twice, to see whether the second '.include' is skipped. 28 1.1 rillig 29 1.9 rillig 30 1.9 rillig # This is the canonical form of a variable-based multiple-inclusion guard. 31 1.14 rillig CASES+= variable-ifndef 32 1.9 rillig LINES.variable-ifndef= \ 33 1.9 rillig '.ifndef VARIABLE_IFNDEF' \ 34 1.9 rillig 'VARIABLE_IFNDEF=' \ 35 1.1 rillig '.endif' 36 1.19 rillig # expect: Parse_PushInput: variable-ifndef.tmp:1 37 1.9 rillig # expect: Skipping 'variable-ifndef.tmp' because 'VARIABLE_IFNDEF' is defined 38 1.1 rillig 39 1.10 rillig # A file that reuses a guard from a previous file (or whose guard is defined 40 1.10 rillig # for any other reason) is only processed once, to see whether it is guarded. 41 1.10 rillig # Its content is skipped, therefore the syntax error is not detected. 42 1.14 rillig CASES+= variable-ifndef-reuse 43 1.10 rillig LINES.variable-ifndef-reuse= \ 44 1.10 rillig '.ifndef VARIABLE_IFNDEF' \ 45 1.10 rillig 'syntax error' \ 46 1.10 rillig '.endif' 47 1.19 rillig # expect: Parse_PushInput: variable-ifndef-reuse.tmp:1 48 1.10 rillig # expect: Skipping 'variable-ifndef-reuse.tmp' because 'VARIABLE_IFNDEF' is defined 49 1.10 rillig 50 1.13 rillig # The guard variable cannot be a number, as numbers are interpreted 51 1.13 rillig # differently from bare words. 52 1.14 rillig CASES+= variable-ifndef-zero 53 1.13 rillig LINES.variable-ifndef-zero= \ 54 1.13 rillig '.ifndef 0e0' \ 55 1.13 rillig 'syntax error' \ 56 1.13 rillig '.endif' 57 1.19 rillig # expect: Parse_PushInput: variable-ifndef-zero.tmp:1 58 1.19 rillig # expect: Parse_PushInput: variable-ifndef-zero.tmp:1 59 1.13 rillig 60 1.13 rillig # The guard variable cannot be a number, as numbers are interpreted 61 1.13 rillig # differently from bare words. 62 1.14 rillig CASES+= variable-ifndef-one 63 1.13 rillig LINES.variable-ifndef-one= \ 64 1.13 rillig '.ifndef 1' \ 65 1.13 rillig '.endif' 66 1.19 rillig # expect: Parse_PushInput: variable-ifndef-one.tmp:1 67 1.19 rillig # expect: Parse_PushInput: variable-ifndef-one.tmp:1 68 1.13 rillig 69 1.9 rillig # Comments and empty lines do not affect the multiple-inclusion guard. 70 1.14 rillig CASES+= comments 71 1.1 rillig LINES.comments= \ 72 1.1 rillig '\# comment' \ 73 1.1 rillig '' \ 74 1.3 rillig '.ifndef COMMENTS' \ 75 1.1 rillig '\# comment' \ 76 1.3 rillig 'COMMENTS=\#comment' \ 77 1.1 rillig '.endif' \ 78 1.1 rillig '\# comment' 79 1.19 rillig # expect: Parse_PushInput: comments.tmp:1 80 1.7 rillig # expect: Skipping 'comments.tmp' because 'COMMENTS' is defined 81 1.1 rillig 82 1.1 rillig # An alternative form uses the 'defined' function. It is more verbose than 83 1.9 rillig # the canonical form but avoids the '.ifndef' directive, as that directive is 84 1.9 rillig # not commonly used. 85 1.14 rillig CASES+= variable-if 86 1.9 rillig LINES.variable-if= \ 87 1.9 rillig '.if !defined(VARIABLE_IF)' \ 88 1.9 rillig 'VARIABLE_IF=' \ 89 1.9 rillig '.endif' 90 1.19 rillig # expect: Parse_PushInput: variable-if.tmp:1 91 1.9 rillig # expect: Skipping 'variable-if.tmp' because 'VARIABLE_IF' is defined 92 1.9 rillig 93 1.10 rillig # A file that reuses a guard from a previous file (or whose guard is defined 94 1.10 rillig # for any other reason) is only processed once, to see whether it is guarded. 95 1.10 rillig # Its content is skipped, therefore the syntax error is not detected. 96 1.14 rillig CASES+= variable-if-reuse 97 1.10 rillig LINES.variable-if-reuse= \ 98 1.10 rillig '.if !defined(VARIABLE_IF)' \ 99 1.10 rillig 'syntax error' \ 100 1.10 rillig '.endif' 101 1.19 rillig # expect: Parse_PushInput: variable-if-reuse.tmp:1 102 1.10 rillig # expect: Skipping 'variable-if-reuse.tmp' because 'VARIABLE_IF' is defined 103 1.10 rillig 104 1.9 rillig # Triple negation is so uncommon that it's not recognized, even though it has 105 1.9 rillig # the same effect as a single negation. 106 1.14 rillig CASES+= variable-if-triple-negation 107 1.9 rillig LINES.variable-if-triple-negation= \ 108 1.9 rillig '.if !!!defined(VARIABLE_IF_TRIPLE_NEGATION)' \ 109 1.9 rillig 'VARIABLE_IF_TRIPLE_NEGATION=' \ 110 1.9 rillig '.endif' 111 1.19 rillig # expect: Parse_PushInput: variable-if-triple-negation.tmp:1 112 1.19 rillig # expect: Parse_PushInput: variable-if-triple-negation.tmp:1 113 1.9 rillig 114 1.15 rillig # If the guard variable is enclosed in spaces, it does not have an effect, as 115 1.15 rillig # that form is not common in practice. 116 1.15 rillig CASES+= variable-if-spaced 117 1.15 rillig LINES.variable-if-spaced= \ 118 1.15 rillig '.if !defined( VARIABLE_IF_SPACED )' \ 119 1.15 rillig 'VARIABLE_IF_SPACED=' \ 120 1.15 rillig '.endif' 121 1.19 rillig # expect: Parse_PushInput: variable-if-spaced.tmp:1 122 1.19 rillig # expect: Parse_PushInput: variable-if-spaced.tmp:1 123 1.15 rillig 124 1.15 rillig # If the guard variable condition is enclosed in parentheses, it does not have 125 1.15 rillig # an effect, as that form is not common in practice. 126 1.15 rillig CASES+= variable-if-parenthesized 127 1.15 rillig LINES.variable-if-parenthesized= \ 128 1.15 rillig '.if (!defined(VARIABLE_IF_PARENTHESIZED))' \ 129 1.15 rillig 'VARIABLE_IF_PARENTHESIZED=' \ 130 1.15 rillig '.endif' 131 1.19 rillig # expect: Parse_PushInput: variable-if-parenthesized.tmp:1 132 1.19 rillig # expect: Parse_PushInput: variable-if-parenthesized.tmp:1 133 1.15 rillig 134 1.9 rillig # A conditional other than '.if' or '.ifndef' does not guard the file, even if 135 1.9 rillig # it is otherwise equivalent to the above accepted forms. 136 1.14 rillig CASES+= variable-ifdef-negated 137 1.9 rillig LINES.variable-ifdef-negated= \ 138 1.9 rillig '.ifdef !VARIABLE_IFDEF_NEGATED' \ 139 1.9 rillig 'VARIABLE_IFDEF_NEGATED=' \ 140 1.6 rillig '.endif' 141 1.19 rillig # expect: Parse_PushInput: variable-ifdef-negated.tmp:1 142 1.19 rillig # expect: Parse_PushInput: variable-ifdef-negated.tmp:1 143 1.6 rillig 144 1.1 rillig # The variable names in the '.if' and the assignment must be the same. 145 1.14 rillig CASES+= variable-name-mismatch 146 1.9 rillig LINES.variable-name-mismatch= \ 147 1.9 rillig '.ifndef VARIABLE_NAME_MISMATCH' \ 148 1.9 rillig 'VARIABLE_NAME_DIFFERENT=' \ 149 1.9 rillig '.endif' 150 1.19 rillig # expect: Parse_PushInput: variable-name-mismatch.tmp:1 151 1.19 rillig # expect: Parse_PushInput: variable-name-mismatch.tmp:1 152 1.9 rillig 153 1.15 rillig # If the guard variable condition is enclosed in parentheses, it does not have 154 1.15 rillig # an effect, as that form is not common in practice. 155 1.15 rillig CASES+= variable-ifndef-parenthesized 156 1.15 rillig LINES.variable-ifndef-parenthesized= \ 157 1.15 rillig '.ifndef (VARIABLE_IFNDEF_PARENTHESIZED)' \ 158 1.15 rillig 'VARIABLE_IFNDEF_PARENTHESIZED=' \ 159 1.15 rillig '.endif' 160 1.19 rillig # expect: Parse_PushInput: variable-ifndef-parenthesized.tmp:1 161 1.19 rillig # expect: Parse_PushInput: variable-ifndef-parenthesized.tmp:1 162 1.15 rillig 163 1.9 rillig # The variable name '!VARNAME' cannot be used in an '.ifndef' directive, as 164 1.9 rillig # the '!' would be a negation. It is syntactically valid in a '.if !defined' 165 1.12 rillig # condition, but this case is so uncommon that the guard mechanism doesn't 166 1.12 rillig # accept '!' in the guard variable name. Furthermore, when defining the 167 1.12 rillig # variable, the character '!' has to be escaped, to prevent it from being 168 1.12 rillig # interpreted as the '!' dependency operator. 169 1.14 rillig CASES+= variable-name-exclamation 170 1.9 rillig LINES.variable-name-exclamation= \ 171 1.9 rillig '.if !defined(!VARIABLE_NAME_EXCLAMATION)' \ 172 1.9 rillig '${:U!}VARIABLE_NAME_EXCLAMATION=' \ 173 1.9 rillig '.endif' 174 1.19 rillig # expect: Parse_PushInput: variable-name-exclamation.tmp:1 175 1.19 rillig # expect: Parse_PushInput: variable-name-exclamation.tmp:1 176 1.9 rillig 177 1.13 rillig # In general, a variable name can contain a '!' in the middle, as that 178 1.13 rillig # character is interpreted as an ordinary character in conditions as well as 179 1.13 rillig # on the left side of a variable assignment. For guard variable names, the 180 1.13 rillig # '!' is not supported in any place, though. 181 1.14 rillig CASES+= variable-name-exclamation-middle 182 1.9 rillig LINES.variable-name-exclamation-middle= \ 183 1.9 rillig '.ifndef VARIABLE_NAME!MIDDLE' \ 184 1.9 rillig 'VARIABLE_NAME!MIDDLE=' \ 185 1.9 rillig '.endif' 186 1.19 rillig # expect: Parse_PushInput: variable-name-exclamation-middle.tmp:1 187 1.19 rillig # expect: Parse_PushInput: variable-name-exclamation-middle.tmp:1 188 1.9 rillig 189 1.9 rillig # A variable name can contain balanced parentheses, at least in conditions and 190 1.9 rillig # on the left side of a variable assignment. There are enough places in make 191 1.9 rillig # where parentheses or braces are handled inconsistently to make this naming 192 1.9 rillig # choice a bad idea, therefore these characters are not allowed in guard 193 1.9 rillig # variable names. 194 1.14 rillig CASES+= variable-name-parentheses 195 1.9 rillig LINES.variable-name-parentheses= \ 196 1.9 rillig '.ifndef VARIABLE_NAME(&)PARENTHESES' \ 197 1.9 rillig 'VARIABLE_NAME(&)PARENTHESES=' \ 198 1.1 rillig '.endif' 199 1.19 rillig # expect: Parse_PushInput: variable-name-parentheses.tmp:1 200 1.19 rillig # expect: Parse_PushInput: variable-name-parentheses.tmp:1 201 1.1 rillig 202 1.6 rillig # The guard condition must consist of only the guard variable, nothing else. 203 1.14 rillig CASES+= variable-ifndef-plus 204 1.9 rillig LINES.variable-ifndef-plus= \ 205 1.9 rillig '.ifndef VARIABLE_IFNDEF_PLUS && VARIABLE_IFNDEF_SECOND' \ 206 1.9 rillig 'VARIABLE_IFNDEF_PLUS=' \ 207 1.9 rillig 'VARIABLE_IFNDEF_SECOND=' \ 208 1.6 rillig '.endif' 209 1.19 rillig # expect: Parse_PushInput: variable-ifndef-plus.tmp:1 210 1.19 rillig # expect: Parse_PushInput: variable-ifndef-plus.tmp:1 211 1.6 rillig 212 1.6 rillig # The guard condition must consist of only the guard variable, nothing else. 213 1.14 rillig CASES+= variable-if-plus 214 1.9 rillig LINES.variable-if-plus= \ 215 1.9 rillig '.if !defined(VARIABLE_IF_PLUS) && !defined(VARIABLE_IF_SECOND)' \ 216 1.9 rillig 'VARIABLE_IF_PLUS=' \ 217 1.9 rillig 'VARIABLE_IF_SECOND=' \ 218 1.6 rillig '.endif' 219 1.19 rillig # expect: Parse_PushInput: variable-if-plus.tmp:1 220 1.19 rillig # expect: Parse_PushInput: variable-if-plus.tmp:1 221 1.6 rillig 222 1.6 rillig # The variable name in an '.ifndef' guard must be given directly, it must not 223 1.6 rillig # contain any '$' expression. 224 1.14 rillig CASES+= variable-ifndef-indirect 225 1.9 rillig LINES.variable-ifndef-indirect= \ 226 1.9 rillig '.ifndef $${VARIABLE_IFNDEF_INDIRECT:L}' \ 227 1.9 rillig 'VARIABLE_IFNDEF_INDIRECT=' \ 228 1.6 rillig '.endif' 229 1.19 rillig # expect: Parse_PushInput: variable-ifndef-indirect.tmp:1 230 1.19 rillig # expect: Parse_PushInput: variable-ifndef-indirect.tmp:1 231 1.9 rillig 232 1.9 rillig # The variable name in an '.if' guard must be given directly, it must not 233 1.9 rillig # contain any '$' expression. 234 1.14 rillig CASES+= variable-if-indirect 235 1.9 rillig LINES.variable-if-indirect= \ 236 1.9 rillig '.if !defined($${VARIABLE_IF_INDIRECT:L})' \ 237 1.9 rillig 'VARIABLE_IF_INDIRECT=' \ 238 1.9 rillig '.endif' 239 1.19 rillig # expect: Parse_PushInput: variable-if-indirect.tmp:1 240 1.19 rillig # expect: Parse_PushInput: variable-if-indirect.tmp:1 241 1.6 rillig 242 1.5 rillig # The variable name in the guard condition must only contain alphanumeric 243 1.12 rillig # characters and underscores. The place where the guard variable is defined 244 1.12 rillig # is more flexible, as long as the variable is defined at the point where the 245 1.12 rillig # file is included the next time. 246 1.14 rillig CASES+= variable-assign-indirect 247 1.9 rillig LINES.variable-assign-indirect= \ 248 1.9 rillig '.ifndef VARIABLE_ASSIGN_INDIRECT' \ 249 1.9 rillig '$${VARIABLE_ASSIGN_INDIRECT:L}=' \ 250 1.1 rillig '.endif' 251 1.19 rillig # expect: Parse_PushInput: variable-assign-indirect.tmp:1 252 1.9 rillig # expect: Skipping 'variable-assign-indirect.tmp' because 'VARIABLE_ASSIGN_INDIRECT' is defined 253 1.1 rillig 254 1.10 rillig # The time at which the guard variable is defined doesn't matter, as long as 255 1.10 rillig # it is defined at the point where the file is included the next time. 256 1.14 rillig CASES+= variable-assign-late 257 1.9 rillig LINES.variable-assign-late= \ 258 1.9 rillig '.ifndef VARIABLE_ASSIGN_LATE' \ 259 1.9 rillig 'VARIABLE_ASSIGN_LATE_OTHER=' \ 260 1.9 rillig 'VARIABLE_ASSIGN_LATE=' \ 261 1.1 rillig '.endif' 262 1.19 rillig # expect: Parse_PushInput: variable-assign-late.tmp:1 263 1.9 rillig # expect: Skipping 'variable-assign-late.tmp' because 'VARIABLE_ASSIGN_LATE' is defined 264 1.1 rillig 265 1.10 rillig # The time at which the guard variable is defined doesn't matter, as long as 266 1.10 rillig # it is defined at the point where the file is included the next time. 267 1.14 rillig CASES+= variable-assign-nested 268 1.9 rillig LINES.variable-assign-nested= \ 269 1.9 rillig '.ifndef VARIABLE_ASSIGN_NESTED' \ 270 1.2 rillig '. if 1' \ 271 1.9 rillig '. for i in once' \ 272 1.9 rillig 'VARIABLE_ASSIGN_NESTED=' \ 273 1.9 rillig '. endfor' \ 274 1.1 rillig '. endif' \ 275 1.1 rillig '.endif' 276 1.19 rillig # expect: Parse_PushInput: variable-assign-nested.tmp:1 277 1.9 rillig # expect: Skipping 'variable-assign-nested.tmp' because 'VARIABLE_ASSIGN_NESTED' is defined 278 1.1 rillig 279 1.7 rillig # If the guard variable is defined before the file is included for the first 280 1.10 rillig # time, the file is considered guarded as well. In such a case, the parser 281 1.10 rillig # skips almost all lines, as they are irrelevant, but the structure of the 282 1.10 rillig # top-level '.if/.endif' conditional can be determined reliably enough to 283 1.10 rillig # decide whether the file is guarded. 284 1.14 rillig CASES+= variable-already-defined 285 1.9 rillig LINES.variable-already-defined= \ 286 1.9 rillig '.ifndef VARIABLE_ALREADY_DEFINED' \ 287 1.9 rillig 'VARIABLE_ALREADY_DEFINED=' \ 288 1.9 rillig '.endif' 289 1.9 rillig VARIABLE_ALREADY_DEFINED= 290 1.19 rillig # expect: Parse_PushInput: variable-already-defined.tmp:1 291 1.10 rillig # expect: Skipping 'variable-already-defined.tmp' because 'VARIABLE_ALREADY_DEFINED' is defined 292 1.10 rillig 293 1.10 rillig # If the guard variable is defined before the file is included the first time, 294 1.10 rillig # the file is processed but its content is skipped. If that same guard 295 1.10 rillig # variable is undefined when the file is included the second time, the file is 296 1.10 rillig # processed as usual. 297 1.14 rillig CASES+= variable-defined-then-undefined 298 1.10 rillig LINES.variable-defined-then-undefined= \ 299 1.10 rillig '.ifndef VARIABLE_DEFINED_THEN_UNDEFINED' \ 300 1.10 rillig '.endif' 301 1.10 rillig VARIABLE_DEFINED_THEN_UNDEFINED= 302 1.10 rillig UNDEF_BETWEEN.variable-defined-then-undefined= \ 303 1.10 rillig VARIABLE_DEFINED_THEN_UNDEFINED 304 1.19 rillig # expect: Parse_PushInput: variable-defined-then-undefined.tmp:1 305 1.19 rillig # expect: Parse_PushInput: variable-defined-then-undefined.tmp:1 306 1.1 rillig 307 1.1 rillig # The whole file content must be guarded by a single '.if' conditional, not by 308 1.12 rillig # several, as each of these conditionals would require its separate guard. 309 1.12 rillig # This case is not expected to occur in practice, as the two parts would 310 1.12 rillig # rather be split into separate files. 311 1.14 rillig CASES+= variable-two-times 312 1.9 rillig LINES.variable-two-times= \ 313 1.9 rillig '.ifndef VARIABLE_TWO_TIMES_1' \ 314 1.9 rillig 'VARIABLE_TWO_TIMES_1=' \ 315 1.1 rillig '.endif' \ 316 1.9 rillig '.ifndef VARIABLE_TWO_TIMES_2' \ 317 1.9 rillig 'VARIABLE_TWO_TIMES_2=' \ 318 1.1 rillig '.endif' 319 1.19 rillig # expect: Parse_PushInput: variable-two-times.tmp:1 320 1.19 rillig # expect: Parse_PushInput: variable-two-times.tmp:1 321 1.1 rillig 322 1.9 rillig # When multiple files use the same guard variable name, the optimization of 323 1.10 rillig # skipping the file affects each of these files. 324 1.10 rillig # 325 1.9 rillig # Choosing unique guard names is the responsibility of the makefile authors. 326 1.9 rillig # A typical pattern of guard variable names is '${PROJECT}_${DIR}_${FILE}_MK'. 327 1.9 rillig # System-provided files typically start the guard names with '_'. 328 1.14 rillig CASES+= variable-clash 329 1.9 rillig LINES.variable-clash= \ 330 1.9 rillig ${LINES.variable-if} 331 1.19 rillig # expect: Parse_PushInput: variable-clash.tmp:1 332 1.10 rillig # expect: Skipping 'variable-clash.tmp' because 'VARIABLE_IF' is defined 333 1.1 rillig 334 1.1 rillig # The conditional must come before the assignment, otherwise the conditional 335 1.1 rillig # is useless, as it always evaluates to false. 336 1.14 rillig CASES+= variable-swapped 337 1.9 rillig LINES.variable-swapped= \ 338 1.1 rillig 'SWAPPED=' \ 339 1.1 rillig '.ifndef SWAPPED' \ 340 1.9 rillig '. error' \ 341 1.1 rillig '.endif' 342 1.19 rillig # expect: Parse_PushInput: variable-swapped.tmp:1 343 1.19 rillig # expect: Parse_PushInput: variable-swapped.tmp:1 344 1.1 rillig 345 1.9 rillig # If the guard variable is undefined between the first and the second time the 346 1.9 rillig # file is included, the guarded file is included again. 347 1.14 rillig CASES+= variable-undef-between 348 1.9 rillig LINES.variable-undef-between= \ 349 1.9 rillig '.ifndef VARIABLE_UNDEF_BETWEEN' \ 350 1.9 rillig 'VARIABLE_UNDEF_BETWEEN=' \ 351 1.9 rillig '.endif' 352 1.9 rillig UNDEF_BETWEEN.variable-undef-between= \ 353 1.9 rillig VARIABLE_UNDEF_BETWEEN 354 1.19 rillig # expect: Parse_PushInput: variable-undef-between.tmp:1 355 1.19 rillig # expect: Parse_PushInput: variable-undef-between.tmp:1 356 1.9 rillig 357 1.9 rillig # If the guard variable is undefined while the file is included the first 358 1.9 rillig # time, the guard does not have an effect, and the file is included again. 359 1.14 rillig CASES+= variable-undef-inside 360 1.9 rillig LINES.variable-undef-inside= \ 361 1.9 rillig '.ifndef VARIABLE_UNDEF_INSIDE' \ 362 1.9 rillig 'VARIABLE_UNDEF_INSIDE=' \ 363 1.9 rillig '.undef VARIABLE_UNDEF_INSIDE' \ 364 1.9 rillig '.endif' 365 1.19 rillig # expect: Parse_PushInput: variable-undef-inside.tmp:1 366 1.19 rillig # expect: Parse_PushInput: variable-undef-inside.tmp:1 367 1.9 rillig 368 1.9 rillig # If the file does not define the guard variable, the guard does not have an 369 1.9 rillig # effect, and the file is included again. 370 1.14 rillig CASES+= variable-not-defined 371 1.9 rillig LINES.variable-not-defined= \ 372 1.9 rillig '.ifndef VARIABLE_NOT_DEFINED' \ 373 1.2 rillig '.endif' 374 1.19 rillig # expect: Parse_PushInput: variable-not-defined.tmp:1 375 1.19 rillig # expect: Parse_PushInput: variable-not-defined.tmp:1 376 1.2 rillig 377 1.2 rillig # The outermost '.if' must not have an '.elif' branch. 378 1.14 rillig CASES+= elif 379 1.12 rillig LINES.elif= \ 380 1.12 rillig '.ifndef ELIF' \ 381 1.12 rillig 'ELIF=' \ 382 1.2 rillig '.elif 1' \ 383 1.2 rillig '.endif' 384 1.19 rillig # expect: Parse_PushInput: elif.tmp:1 385 1.19 rillig # expect: Parse_PushInput: elif.tmp:1 386 1.2 rillig 387 1.10 rillig # When a file with an '.if/.elif/.endif' conditional at the top level is 388 1.10 rillig # included, it is never optimized, as one of its branches is taken. 389 1.14 rillig CASES+= elif-reuse 390 1.12 rillig LINES.elif-reuse= \ 391 1.12 rillig '.ifndef ELIF' \ 392 1.10 rillig 'syntax error' \ 393 1.10 rillig '.elif 1' \ 394 1.10 rillig '.endif' 395 1.19 rillig # expect: Parse_PushInput: elif-reuse.tmp:1 396 1.19 rillig # expect: Parse_PushInput: elif-reuse.tmp:1 397 1.10 rillig 398 1.2 rillig # The outermost '.if' must not have an '.else' branch. 399 1.14 rillig CASES+= else 400 1.12 rillig LINES.else= \ 401 1.12 rillig '.ifndef ELSE' \ 402 1.12 rillig 'ELSE=' \ 403 1.2 rillig '.else' \ 404 1.2 rillig '.endif' 405 1.19 rillig # expect: Parse_PushInput: else.tmp:1 406 1.19 rillig # expect: Parse_PushInput: else.tmp:1 407 1.2 rillig 408 1.10 rillig # When a file with an '.if/.else/.endif' conditional at the top level is 409 1.10 rillig # included, it is never optimized, as one of its branches is taken. 410 1.14 rillig CASES+= else-reuse 411 1.12 rillig LINES.else-reuse= \ 412 1.12 rillig '.ifndef ELSE' \ 413 1.10 rillig 'syntax error' \ 414 1.10 rillig '.else' \ 415 1.10 rillig '.endif' 416 1.19 rillig # expect: Parse_PushInput: else-reuse.tmp:1 417 1.19 rillig # expect: Parse_PushInput: else-reuse.tmp:1 418 1.10 rillig 419 1.9 rillig # The inner '.if' directives may have an '.elif' or '.else', and it doesn't 420 1.9 rillig # matter which of their branches are taken. 421 1.14 rillig CASES+= inner-if-elif-else 422 1.10 rillig LINES.inner-if-elif-else= \ 423 1.2 rillig '.ifndef INNER_IF_ELIF_ELSE' \ 424 1.2 rillig 'INNER_IF_ELIF_ELSE=' \ 425 1.2 rillig '. if 0' \ 426 1.2 rillig '. elif 0' \ 427 1.2 rillig '. else' \ 428 1.2 rillig '. endif' \ 429 1.2 rillig '. if 0' \ 430 1.2 rillig '. elif 1' \ 431 1.2 rillig '. else' \ 432 1.2 rillig '. endif' \ 433 1.2 rillig '. if 1' \ 434 1.2 rillig '. elif 1' \ 435 1.2 rillig '. else' \ 436 1.2 rillig '. endif' \ 437 1.2 rillig '.endif' 438 1.19 rillig # expect: Parse_PushInput: inner-if-elif-else.tmp:1 439 1.7 rillig # expect: Skipping 'inner-if-elif-else.tmp' because 'INNER_IF_ELIF_ELSE' is defined 440 1.7 rillig 441 1.9 rillig # The guard can also be a target instead of a variable. Using a target as a 442 1.9 rillig # guard has the benefit that a target cannot be undefined once it is defined. 443 1.9 rillig # The target should be declared '.NOTMAIN'. Since the target names are 444 1.9 rillig # usually chosen according to a pattern that doesn't interfere with real 445 1.9 rillig # target names, they don't need to be declared '.PHONY' as they don't generate 446 1.9 rillig # filesystem operations. 447 1.14 rillig CASES+= target 448 1.7 rillig LINES.target= \ 449 1.7 rillig '.if !target(__target.tmp__)' \ 450 1.9 rillig '__target.tmp__: .NOTMAIN' \ 451 1.7 rillig '.endif' 452 1.19 rillig # expect: Parse_PushInput: target.tmp:1 453 1.7 rillig # expect: Skipping 'target.tmp' because '__target.tmp__' is defined 454 1.7 rillig 455 1.9 rillig # When used for system files, the target name may include '<' and '>', for 456 1.9 rillig # symmetry with the '.include <sys.mk>' directive. The characters '<' and '>' 457 1.9 rillig # are ordinary characters. 458 1.14 rillig CASES+= target-sys 459 1.7 rillig LINES.target-sys= \ 460 1.7 rillig '.if !target(__<target-sys.tmp>__)' \ 461 1.9 rillig '__<target-sys.tmp>__: .NOTMAIN' \ 462 1.7 rillig '.endif' 463 1.19 rillig # expect: Parse_PushInput: target-sys.tmp:1 464 1.7 rillig # expect: Skipping 'target-sys.tmp' because '__<target-sys.tmp>__' is defined 465 1.7 rillig 466 1.9 rillig # The target name may include variable references. These references are 467 1.9 rillig # expanded as usual. Due to the current implementation, the expressions are 468 1.9 rillig # evaluated twice: Once for checking whether the condition evaluates to true, 469 1.9 rillig # and once for determining the guard name. This double evaluation should not 470 1.9 rillig # matter in practice, as guard expressions are expected to be simple, 471 1.9 rillig # deterministic and without side effects. 472 1.14 rillig CASES+= target-indirect 473 1.7 rillig LINES.target-indirect= \ 474 1.7 rillig '.if !target($${target-indirect.tmp:L})' \ 475 1.9 rillig 'target-indirect.tmp: .NOTMAIN' \ 476 1.7 rillig '.endif' 477 1.19 rillig # expect: Parse_PushInput: target-indirect.tmp:1 478 1.8 sjg # expect: Skipping 'target-indirect.tmp' because 'target-indirect.tmp' is defined 479 1.8 sjg 480 1.9 rillig # A common form of guard target is __${.PARSEFILE}__. This form can only be 481 1.9 rillig # used if all files using this form have unique basenames. To get a robust 482 1.9 rillig # pattern based on the same idea, use __${.PARSEDIR}/${.PARSEFILE}__ instead. 483 1.9 rillig # This form does not work when the basename contains whitespace characters, as 484 1.9 rillig # it is not possible to define a target with whitespace, not even by cheating. 485 1.14 rillig CASES+= target-indirect-PARSEFILE 486 1.8 sjg LINES.target-indirect-PARSEFILE= \ 487 1.8 sjg '.if !target(__$${.PARSEFILE}__)' \ 488 1.8 sjg '__$${.PARSEFILE}__: .NOTMAIN' \ 489 1.8 sjg '.endif' 490 1.19 rillig # expect: Parse_PushInput: target-indirect-PARSEFILE.tmp:1 491 1.8 sjg # expect: Skipping 'target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined 492 1.8 sjg 493 1.9 rillig # Two files with different basenames can both use the same syntactic pattern 494 1.9 rillig # for the target guard name, as the expressions expand to different strings. 495 1.14 rillig CASES+= target-indirect-PARSEFILE2 496 1.8 sjg LINES.target-indirect-PARSEFILE2= \ 497 1.8 sjg '.if !target(__$${.PARSEFILE}__)' \ 498 1.8 sjg '__$${.PARSEFILE}__: .NOTMAIN' \ 499 1.8 sjg '.endif' 500 1.19 rillig # expect: Parse_PushInput: target-indirect-PARSEFILE2.tmp:1 501 1.8 sjg # expect: Skipping 'target-indirect-PARSEFILE2.tmp' because '__target-indirect-PARSEFILE2.tmp__' is defined 502 1.8 sjg 503 1.9 rillig # Using plain .PARSEFILE without .PARSEDIR leads to name clashes. The include 504 1.10 rillig # guard is the same as in the test case 'target-indirect-PARSEFILE', as the 505 1.12 rillig # guard name only contains the basename but not the directory name. So even 506 1.15 rillig # without defining the guard target, the file is considered guarded. 507 1.14 rillig CASES+= subdir/target-indirect-PARSEFILE 508 1.9 rillig LINES.subdir/target-indirect-PARSEFILE= \ 509 1.9 rillig '.if !target(__$${.PARSEFILE}__)' \ 510 1.9 rillig '.endif' 511 1.19 rillig # expect: Parse_PushInput: subdir/target-indirect-PARSEFILE.tmp:1 512 1.10 rillig # expect: Skipping 'subdir/target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined 513 1.9 rillig 514 1.11 sjg # Another common form of guard target is __${.PARSEDIR}/${.PARSEFILE}__ 515 1.12 rillig # or __${.PARSEDIR:tA}/${.PARSEFILE}__ to be truly unique. 516 1.14 rillig CASES+= target-indirect-PARSEDIR-PARSEFILE 517 1.9 rillig LINES.target-indirect-PARSEDIR-PARSEFILE= \ 518 1.9 rillig '.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \ 519 1.9 rillig '__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \ 520 1.9 rillig '.endif' 521 1.19 rillig # expect: Parse_PushInput: target-indirect-PARSEDIR-PARSEFILE.tmp:1 522 1.9 rillig # expect: Skipping 'target-indirect-PARSEDIR-PARSEFILE.tmp' because '__target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined 523 1.9 rillig # The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the 524 1.9 rillig # string '${.OBJDIR}/' gets stripped in post processing. 525 1.9 rillig 526 1.9 rillig # Using the combination of '.PARSEDIR' and '.PARSEFILE', a file in a 527 1.9 rillig # subdirectory gets a different guard target name than the previous one. 528 1.14 rillig CASES+= subdir/target-indirect-PARSEDIR-PARSEFILE 529 1.9 rillig LINES.subdir/target-indirect-PARSEDIR-PARSEFILE= \ 530 1.9 rillig '.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \ 531 1.9 rillig '__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \ 532 1.9 rillig '.endif' 533 1.19 rillig # expect: Parse_PushInput: subdir/target-indirect-PARSEDIR-PARSEFILE.tmp:1 534 1.9 rillig # expect: Skipping 'subdir/target-indirect-PARSEDIR-PARSEFILE.tmp' because '__subdir/target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined 535 1.9 rillig # The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the 536 1.9 rillig # string '${.OBJDIR}/' gets stripped in post processing. 537 1.9 rillig 538 1.9 rillig # If the guard target is not defined when including the file the next time, 539 1.9 rillig # the file is processed again. 540 1.14 rillig CASES+= target-unguarded 541 1.7 rillig LINES.target-unguarded= \ 542 1.7 rillig '.if !target(target-unguarded)' \ 543 1.7 rillig '.endif' 544 1.19 rillig # expect: Parse_PushInput: target-unguarded.tmp:1 545 1.19 rillig # expect: Parse_PushInput: target-unguarded.tmp:1 546 1.7 rillig 547 1.7 rillig # The guard condition must consist of only the guard target, nothing else. 548 1.14 rillig CASES+= target-plus 549 1.7 rillig LINES.target-plus= \ 550 1.7 rillig '.if !target(target-plus) && 1' \ 551 1.9 rillig 'target-plus: .NOTMAIN' \ 552 1.7 rillig '.endif' 553 1.19 rillig # expect: Parse_PushInput: target-plus.tmp:1 554 1.19 rillig # expect: Parse_PushInput: target-plus.tmp:1 555 1.7 rillig 556 1.9 rillig # If the guard target is defined before the file is included the first time, 557 1.12 rillig # the file is read once and then considered guarded. 558 1.14 rillig CASES+= target-already-defined 559 1.10 rillig LINES.target-already-defined= \ 560 1.10 rillig '.if !target(target-already-defined)' \ 561 1.10 rillig 'target-already-defined: .NOTMAIN' \ 562 1.10 rillig '.endif' 563 1.10 rillig target-already-defined: .NOTMAIN 564 1.19 rillig # expect: Parse_PushInput: target-already-defined.tmp:1 565 1.10 rillig # expect: Skipping 'target-already-defined.tmp' because 'target-already-defined' is defined 566 1.5 rillig 567 1.9 rillig # A target name cannot contain the character '!'. In the condition, the '!' 568 1.9 rillig # is syntactically valid, but in the dependency declaration line, the '!' is 569 1.9 rillig # interpreted as the '!' dependency operator, no matter whether it occurs at 570 1.9 rillig # the beginning or in the middle of a target name. Escaping it as '${:U!}' 571 1.9 rillig # doesn't work, as the whole line is first expanded and then scanned for the 572 1.9 rillig # dependency operator. Escaping it as '\!' doesn't work either, even though 573 1.9 rillig # the '\' escapes the '!' from being a dependency operator, but when reading 574 1.9 rillig # the target name, the '\' is kept, resulting in the target name 575 1.9 rillig # '\!target-name-exclamation' instead of '!target-name-exclamation'. 576 1.14 rillig CASES+= target-name-exclamation 577 1.9 rillig LINES.target-name-exclamation= \ 578 1.9 rillig '.if !target(!target-name-exclamation)' \ 579 1.9 rillig '\!target-name-exclamation: .NOTMAIN' \ 580 1.9 rillig '.endif' 581 1.19 rillig # expect: Parse_PushInput: target-name-exclamation.tmp:1 582 1.19 rillig # expect: Parse_PushInput: target-name-exclamation.tmp:1 583 1.9 rillig 584 1.18 rillig # If the guard target name has leading spaces, it does not have an effect, 585 1.15 rillig # as that form is not common in practice. 586 1.17 rillig CASES+= target-name-leading-space 587 1.17 rillig LINES.target-name-leading-space= \ 588 1.17 rillig '.if !target( target-name-leading-space)' \ 589 1.17 rillig 'target-name-leading-space: .NOTMAIN' \ 590 1.17 rillig '.endif' 591 1.19 rillig # expect: Parse_PushInput: target-name-leading-space.tmp:1 592 1.19 rillig # expect: Parse_PushInput: target-name-leading-space.tmp:1 593 1.17 rillig 594 1.18 rillig # If the guard target name has trailing spaces, it does not have an effect, 595 1.18 rillig # as that form is not common in practice. 596 1.17 rillig CASES+= target-name-trailing-space 597 1.17 rillig LINES.target-name-trailing-space= \ 598 1.17 rillig '.if !target(target-name-trailing-space )' \ 599 1.17 rillig 'target-name-trailing-space: .NOTMAIN' \ 600 1.15 rillig '.endif' 601 1.19 rillig # expect: Parse_PushInput: target-name-trailing-space.tmp:1 602 1.19 rillig # expect: Parse_PushInput: target-name-trailing-space.tmp:1 603 1.15 rillig 604 1.15 rillig # If the guard target condition is enclosed in parentheses, it does not have 605 1.15 rillig # an effect, as that form is not common in practice. 606 1.15 rillig CASES+= target-call-parenthesized 607 1.15 rillig LINES.target-call-parenthesized= \ 608 1.15 rillig '.if (!target(target-call-parenthesized))' \ 609 1.15 rillig 'target-call-parenthesized: .NOTMAIN' \ 610 1.15 rillig '.endif' 611 1.19 rillig # expect: Parse_PushInput: target-call-parenthesized.tmp:1 612 1.19 rillig # expect: Parse_PushInput: target-call-parenthesized.tmp:1 613 1.15 rillig 614 1.16 rillig # If the '.if' or '.ifndef' directive spans more than a single line, it is 615 1.16 rillig # still recognized as a guard condition. This case is entirely uncommon, but 616 1.16 rillig # at the point where the guard condition is checked, line continuations have 617 1.16 rillig # already been converted to spaces. 618 1.16 rillig CASES+= multiline 619 1.16 rillig LINES.multiline= \ 620 1.16 rillig '.\' \ 621 1.16 rillig ' ifndef \' \ 622 1.16 rillig ' MULTILINE' \ 623 1.16 rillig 'MULTILINE=' \ 624 1.16 rillig '.endif' 625 1.19 rillig # expect: Parse_PushInput: multiline.tmp:1 626 1.16 rillig # expect: Skipping 'multiline.tmp' because 'MULTILINE' is defined 627 1.16 rillig 628 1.12 rillig 629 1.9 rillig # Now run all test cases by including each of the files twice and looking at 630 1.9 rillig # the debug output. The files that properly guard against multiple inclusion 631 1.9 rillig # generate a 'Skipping' line, the others repeat the 'Parse_PushInput' line. 632 1.1 rillig # 633 1.1 rillig # Some debug output lines are suppressed in the .exp file, see ./Makefile. 634 1.14 rillig .for i in ${CASES} 635 1.4 rillig . for fname in $i.tmp 636 1.9 rillig _:= ${fname:H:N.:@dir@${:!mkdir -p ${dir}!}@} 637 1.1 rillig _!= printf '%s\n' ${LINES.$i} > ${fname} 638 1.1 rillig .MAKEFLAGS: -dp 639 1.1 rillig .include "${.CURDIR}/${fname}" 640 1.9 rillig .undef ${UNDEF_BETWEEN.$i:U} 641 1.1 rillig .include "${.CURDIR}/${fname}" 642 1.1 rillig .MAKEFLAGS: -d0 643 1.1 rillig _!= rm ${fname} 644 1.9 rillig _:= ${fname:H:N.:@dir@${:!rmdir ${dir}!}@} 645 1.1 rillig . endfor 646 1.1 rillig .endfor 647 1.1 rillig 648 1.1 rillig all: 649