]> git.ipfire.org Git - thirdparty/glibc.git/blame - conform/conformtest.py
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / conform / conformtest.py
CommitLineData
c3ec0972
JM
1#!/usr/bin/python3
2# Check header contents against the given standard.
04277e02 3# Copyright (C) 2018-2019 Free Software Foundation, Inc.
c3ec0972
JM
4# This file is part of the GNU C Library.
5#
6# The GNU C Library is free software; you can redistribute it and/or
7# modify it under the terms of the GNU Lesser General Public
8# License as published by the Free Software Foundation; either
9# version 2.1 of the License, or (at your option) any later version.
10#
11# The GNU C Library is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14# Lesser General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public
17# License along with the GNU C Library; if not, see
5a82c748 18# <https://www.gnu.org/licenses/>.
c3ec0972
JM
19
20import argparse
21import fnmatch
22import os.path
23import re
24import subprocess
25import sys
26import tempfile
27
28import glibcconform
29
30
cc6c89fa
JM
31class CompileSubTest(object):
32 """A compilation subtest."""
33
34 def __init__(self, name, text):
35 """Initialize a CompileSubTest object."""
95edd05c 36 self.run_early = False
cc6c89fa
JM
37 self.name = name
38 self.text = text
39
40 def run(self, header_tests):
41 """Run a compilation subtest."""
42 header_tests.compile_test(self.name, self.text)
43
44
45class ExecuteSubTest(object):
46 """An execution subtest."""
47
48 def __init__(self, name, text):
49 """Initialize an ExecuteSubTest object."""
95edd05c 50 self.run_early = False
cc6c89fa
JM
51 self.name = name
52 self.text = text
53
54 def run(self, header_tests):
55 """Run an execution subtest."""
56 header_tests.execute_test(self.name, self.text)
57
58
c3ec0972
JM
59class ElementTest(object):
60 """Test for an element of a structure or union type."""
61
62 def __init__(self, dummy, type_name, member_type, member_name, *rest):
63 """Initialize an ElementTest object."""
64 self.type_name = type_name
65 self.member_type = member_type
66 self.member_name = member_name
67 self.rest = ' '.join(rest)
68 self.allow_name = self.member_name
69
cc6c89fa
JM
70 def gen_subtests(self):
71 """Generate subtests for an ElementTest."""
9a62a939
JM
72 text = ('%(type_name)s a_%(num)d;\n'
73 '%(type_name)s b_%(num)d;\n'
74 'extern void xyzzy_%(num)d '
75 '(__typeof__ (&b_%(num)d.%(member_name)s), '
76 '__typeof__ (&a_%(num)d.%(member_name)s), unsigned);\n'
77 'void foobarbaz_%(num)d (void) {\n'
78 'xyzzy_%(num)d (&a_%(num)d.%(member_name)s, '
79 '&b_%(num)d.%(member_name)s, '
80 'sizeof (a_%(num)d.%(member_name)s));\n'
c3ec0972 81 '}\n'
9a62a939 82 % vars(self))
cc6c89fa
JM
83 self.subtests.append(CompileSubTest(
84 'Availability of member %s' % self.member_name,
85 text))
9a62a939
JM
86 text = ('%(type_name)s a2_%(num)d;\n'
87 'extern %(member_type)s b2_%(num)d%(rest)s;\n'
88 'extern __typeof__ (a2_%(num)d.%(member_name)s) b2_%(num)d;\n'
89 % vars(self))
cc6c89fa
JM
90 self.subtests.append(CompileSubTest(
91 'Type of member %s' % self.member_name,
92 text))
c3ec0972
JM
93
94
95class ConstantTest(object):
96 """Test for a macro or constant."""
97
98 def __init__(self, symbol_type, symbol, extra1=None, extra2=None,
99 extra3=None):
100 """Initialize a ConstantTest object."""
101 self.symbol_type = symbol_type
102 self.symbol = symbol
103 # A comparison operation may be specified without a type.
104 if extra2 is not None and extra3 is None:
105 self.c_type = None
106 self.op = extra1
107 self.value = extra2
108 else:
109 self.c_type = extra1
110 self.op = extra2
111 self.value = extra3
112 self.allow_name = self.symbol
113
cc6c89fa
JM
114 def gen_subtests(self):
115 """Generate subtests for a ConstantTest."""
c3ec0972 116 if 'macro' in self.symbol_type:
9a62a939
JM
117 text = ('#ifndef %(symbol)s\n'
118 '# error "Macro %(symbol)s not defined"\n'
c3ec0972 119 '#endif\n'
9a62a939 120 % vars(self))
cc6c89fa
JM
121 self.subtests.append(CompileSubTest(
122 'Availability of macro %s' % self.symbol,
123 text))
c3ec0972 124 if 'constant' in self.symbol_type:
9a62a939
JM
125 text = ('__typeof__ (%(symbol)s) a_%(num)d = %(symbol)s;\n'
126 % vars(self))
cc6c89fa
JM
127 self.subtests.append(CompileSubTest(
128 'Availability of constant %s' % self.symbol,
129 text))
c3ec0972
JM
130 if self.symbol_type == 'macro-int-constant':
131 sym_bits_def_neg = ''.join(
132 '# if %s & (1LL << %d)\n'
9a62a939 133 '# define conformtest_%d_bit_%d 0LL\n'
c3ec0972 134 '# else\n'
9a62a939 135 '# define conformtest_%d_bit_%d (1LL << %d)\n'
c3ec0972 136 '# endif\n'
9a62a939
JM
137 % (self.symbol, i, self.num, i, self.num, i, i)
138 for i in range(63))
139 sym_bits_or_neg = '|'.join('conformtest_%d_bit_%d' % (self.num, i)
c3ec0972
JM
140 for i in range(63))
141 sym_bits_def_pos = ''.join(
142 '# if %s & (1ULL << %d)\n'
9a62a939 143 '# define conformtest_%d_bit_%d (1ULL << %d)\n'
c3ec0972 144 '# else\n'
9a62a939 145 '# define conformtest_%d_bit_%d 0ULL\n'
c3ec0972 146 '# endif\n'
9a62a939
JM
147 % (self.symbol, i, self.num, i, i, self.num, i)
148 for i in range(64))
149 sym_bits_or_pos = '|'.join('conformtest_%d_bit_%d' % (self.num, i)
c3ec0972
JM
150 for i in range(64))
151 text = ('#if %s < 0\n'
9a62a939 152 '# define conformtest_%d_negative 1\n'
c3ec0972 153 '%s'
9a62a939 154 '# define conformtest_%d_value ~(%s)\n'
c3ec0972 155 '#else\n'
9a62a939 156 '# define conformtest_%d_negative 0\n'
c3ec0972 157 '%s'
9a62a939 158 '# define conformtest_%d_value (%s)\n'
c3ec0972 159 '#endif\n'
9a62a939
JM
160 '_Static_assert (((%s < 0) == conformtest_%d_negative) '
161 '&& (%s == conformtest_%d_value), '
c3ec0972 162 '"value match inside and outside #if");\n'
9a62a939
JM
163 % (self.symbol, self.num, sym_bits_def_neg, self.num,
164 sym_bits_or_neg, self.num, sym_bits_def_pos, self.num,
165 sym_bits_or_pos, self.symbol, self.num, self.symbol,
166 self.num))
cc6c89fa
JM
167 self.subtests.append(CompileSubTest(
168 '#if usability of symbol %s'% self.symbol,
169 text))
c3ec0972
JM
170 if self.c_type is not None:
171 if self.c_type.startswith('promoted:'):
172 c_type = self.c_type[len('promoted:'):]
9a62a939
JM
173 text = ('__typeof__ ((%s) 0 + (%s) 0) a2_%d;\n'
174 % (c_type, c_type, self.num))
c3ec0972 175 else:
9a62a939
JM
176 text = '__typeof__ ((%s) 0) a2_%d;\n' % (self.c_type, self.num)
177 text += 'extern __typeof__ (%s) a2_%d;\n' % (self.symbol, self.num)
cc6c89fa
JM
178 self.subtests.append(CompileSubTest(
179 'Type of symbol %s' % self.symbol,
180 text))
c3ec0972 181 if self.op is not None:
9a62a939
JM
182 text = ('_Static_assert (%(symbol)s %(op)s %(value)s, '
183 '"value constraint");\n'
184 % vars(self))
cc6c89fa
JM
185 self.subtests.append(CompileSubTest(
186 'Value of symbol %s' % self.symbol,
187 text))
c3ec0972
JM
188
189
190class SymbolTest(object):
191 """Test for a symbol (not a compile-time constant)."""
192
193 def __init__(self, dummy, symbol, value=None):
194 """Initialize a SymbolTest object."""
195 self.symbol = symbol
196 self.value = value
197 self.allow_name = self.symbol
198
cc6c89fa
JM
199 def gen_subtests(self):
200 """Generate subtests for a SymbolTest."""
9a62a939
JM
201 text = ('void foobarbaz_%(num)d (void) {\n'
202 '__typeof__ (%(symbol)s) a_%(num)d = %(symbol)s;\n'
c3ec0972 203 '}\n'
9a62a939 204 % vars(self))
cc6c89fa
JM
205 self.subtests.append(CompileSubTest(
206 'Availability of symbol %s' % self.symbol,
207 text))
c3ec0972 208 if self.value is not None:
9a62a939
JM
209 text = ('int main (void) { return %(symbol)s != %(symbol)s; }\n'
210 % vars(self))
cc6c89fa
JM
211 self.subtests.append(ExecuteSubTest(
212 'Value of symbol %s' % self.symbol,
213 text))
c3ec0972
JM
214
215
216class TypeTest(object):
217 """Test for a type name."""
218
219 def __init__(self, dummy, type_name):
220 """Initialize a TypeTest object."""
221 self.type_name = type_name
222 if type_name.startswith('struct '):
223 self.allow_name = type_name[len('struct '):]
224 self.maybe_opaque = False
225 elif type_name.startswith('union '):
226 self.allow_name = type_name[len('union '):]
227 self.maybe_opaque = False
228 else:
229 self.allow_name = type_name
230 self.maybe_opaque = True
231
cc6c89fa
JM
232 def gen_subtests(self):
233 """Generate subtests for a TypeTest."""
9a62a939
JM
234 text = ('%s %sa_%d;\n'
235 % (self.type_name, '*' if self.maybe_opaque else '', self.num))
cc6c89fa
JM
236 self.subtests.append(CompileSubTest(
237 'Availability of type %s' % self.type_name,
238 text))
c3ec0972
JM
239
240
241class TagTest(object):
242 """Test for a tag name."""
243
244 def __init__(self, dummy, type_name):
245 """Initialize a TagTest object."""
246 self.type_name = type_name
247 if type_name.startswith('struct '):
248 self.allow_name = type_name[len('struct '):]
249 elif type_name.startswith('union '):
250 self.allow_name = type_name[len('union '):]
251 else:
252 raise ValueError('unexpected kind of tag: %s' % type_name)
253
cc6c89fa
JM
254 def gen_subtests(self):
255 """Generate subtests for a TagTest."""
c3ec0972
JM
256 # If the tag is not declared, these function prototypes have
257 # incompatible types.
9a62a939
JM
258 text = ('void foo_%(num)d (%(type_name)s *);\n'
259 'void foo_%(num)d (%(type_name)s *);\n'
260 % vars(self))
cc6c89fa
JM
261 self.subtests.append(CompileSubTest(
262 'Availability of tag %s' % self.type_name,
263 text))
c3ec0972
JM
264
265
266class FunctionTest(object):
267 """Test for a function."""
268
269 def __init__(self, dummy, return_type, function_name, *args):
270 """Initialize a FunctionTest object."""
271 self.function_name_full = function_name
272 self.args = ' '.join(args)
273 if function_name.startswith('(*'):
274 # Function returning a pointer to function.
275 self.return_type = '%s (*' % return_type
276 self.function_name = function_name[len('(*'):]
277 else:
278 self.return_type = return_type
279 self.function_name = function_name
280 self.allow_name = self.function_name
281
cc6c89fa
JM
282 def gen_subtests(self):
283 """Generate subtests for a FunctionTest."""
9a62a939
JM
284 text = ('%(return_type)s (*foobarbaz_%(num)d) %(args)s '
285 '= %(function_name)s;\n'
286 % vars(self))
cc6c89fa
JM
287 self.subtests.append(CompileSubTest(
288 'Availability of function %s' % self.function_name,
289 text))
9a62a939
JM
290 text = ('extern %(return_type)s (*foobarbaz2_%(num)d) %(args)s;\n'
291 'extern __typeof__ (&%(function_name)s) foobarbaz2_%(num)d;\n'
292 % vars(self))
cc6c89fa
JM
293 self.subtests.append(CompileSubTest(
294 'Type of function %s' % self.function_name,
295 text))
c3ec0972
JM
296
297
298class VariableTest(object):
299 """Test for a variable."""
300
301 def __init__(self, dummy, var_type, var_name, *rest):
302 """Initialize a VariableTest object."""
303 self.var_type = var_type
304 self.var_name = var_name
305 self.rest = ' '.join(rest)
306 self.allow_name = var_name
307
cc6c89fa
JM
308 def gen_subtests(self):
309 """Generate subtests for a VariableTest."""
9a62a939
JM
310 text = ('typedef %(var_type)s xyzzy_%(num)d%(rest)s;\n'
311 'xyzzy_%(num)d *foobarbaz_%(num)d = &%(var_name)s;\n'
312 % vars(self))
cc6c89fa
JM
313 self.subtests.append(CompileSubTest(
314 'Availability of variable %s' % self.var_name,
315 text))
9a62a939
JM
316 text = ('extern %(var_type)s %(var_name)s%(rest)s;\n'
317 % vars(self))
cc6c89fa
JM
318 self.subtests.append(CompileSubTest(
319 'Type of variable %s' % self.var_name,
320 text))
c3ec0972
JM
321
322
323class MacroFunctionTest(object):
324 """Test for a possibly macro-only function."""
325
326 def __init__(self, dummy, return_type, function_name, *args):
327 """Initialize a MacroFunctionTest object."""
328 self.return_type = return_type
329 self.function_name = function_name
330 self.args = ' '.join(args)
331 self.allow_name = function_name
332
cc6c89fa
JM
333 def gen_subtests(self):
334 """Generate subtests for a MacroFunctionTest."""
9a62a939
JM
335 text = ('#ifndef %(function_name)s\n'
336 '%(return_type)s (*foobarbaz_%(num)d) %(args)s '
337 '= %(function_name)s;\n'
c3ec0972 338 '#endif\n'
9a62a939 339 % vars(self))
cc6c89fa
JM
340 self.subtests.append(CompileSubTest(
341 'Availability of macro %s' % self.function_name,
342 text))
9a62a939
JM
343 text = ('#ifndef %(function_name)s\n'
344 'extern %(return_type)s (*foobarbaz2_%(num)d) %(args)s;\n'
345 'extern __typeof__ (&%(function_name)s) foobarbaz2_%(num)d;\n'
c3ec0972 346 '#endif\n'
9a62a939 347 % vars(self))
cc6c89fa
JM
348 self.subtests.append(CompileSubTest(
349 'Type of macro %s' % self.function_name,
350 text))
c3ec0972
JM
351
352
353class MacroStrTest(object):
354 """Test for a string-valued macro."""
355
356 def __init__(self, dummy, macro_name, value):
357 """Initialize a MacroStrTest object."""
358 self.macro_name = macro_name
359 self.value = value
360 self.allow_name = macro_name
361
cc6c89fa
JM
362 def gen_subtests(self):
363 """Generate subtests for a MacroStrTest."""
9a62a939
JM
364 text = ('#ifndef %(macro_name)s\n'
365 '# error "Macro %(macro_name)s not defined"\n'
c3ec0972 366 '#endif\n'
9a62a939 367 % vars(self))
cc6c89fa
JM
368 self.subtests.append(CompileSubTest(
369 'Availability of macro %s' % self.macro_name,
370 text))
c3ec0972
JM
371 # We can't include <string.h> here.
372 text = ('extern int (strcmp)(const char *, const char *);\n'
9a62a939
JM
373 'int main (void) { return (strcmp) (%(macro_name)s, '
374 '%(value)s) != 0; }\n'
375 % vars(self))
cc6c89fa
JM
376 self.subtests.append(ExecuteSubTest(
377 'Value of macro %s' % self.macro_name,
378 text))
c3ec0972
JM
379
380
381class HeaderTests(object):
382 """The set of tests run for a header."""
383
384 def __init__(self, header, standard, cc, flags, cross, xfail):
385 """Initialize a HeaderTests object."""
386 self.header = header
387 self.standard = standard
388 self.cc = cc
389 self.flags = flags
390 self.cross = cross
391 self.xfail_str = xfail
392 self.cflags_namespace = ('%s -fno-builtin %s -D_ISOMAC'
393 % (flags, glibcconform.CFLAGS[standard]))
394 # When compiling the conformance test programs, use of
395 # __attribute__ in headers is disabled because of attributes
396 # that affect the types of functions as seen by typeof.
397 self.cflags = "%s '-D__attribute__(x)='" % self.cflags_namespace
398 self.tests = []
399 self.allow = set()
400 self.allow_fnmatch = set()
401 self.headers_handled = set()
9a62a939 402 self.num_tests = 0
c3ec0972
JM
403 self.total = 0
404 self.skipped = 0
405 self.errors = 0
406 self.xerrors = 0
407
408 def add_allow(self, name, pattern_ok):
409 """Add an identifier as an allowed token for this header.
410
411 If pattern_ok, fnmatch patterns are OK as well as
412 identifiers.
413
414 """
415 if re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]*', name):
416 self.allow.add(name)
417 elif pattern_ok:
418 self.allow_fnmatch.add(name)
419 else:
420 raise ValueError('bad identifier: %s' % name)
421
422 def check_token(self, bad_tokens, token):
423 """Check whether an identifier token is allowed, and record it in
424 bad_tokens if not.
425
426 """
427 if token.startswith('_'):
428 return
429 if token in glibcconform.KEYWORDS[self.standard]:
430 return
431 if token in self.allow:
432 return
433 for pattern in self.allow_fnmatch:
434 if fnmatch.fnmatch(token, pattern):
435 return
436 bad_tokens.add(token)
437
438 def handle_test_line(self, line, allow):
439 """Handle a single line in the test data.
440
441 If allow is true, the header is one specified in allow-header
442 and so tests are marked as allowed for namespace purposes but
443 otherwise ignored.
444
445 """
446 orig_line = line
447 xfail = False
448 if line.startswith('xfail-'):
449 xfail = True
450 line = line[len('xfail-'):]
451 else:
452 match = re.match(r'xfail\[(.*?)\]-(.*)', line)
453 if match:
454 xfail_cond = match.group(1)
455 line = match.group(2)
456 # "xfail[cond]-" or "xfail[cond1|cond2|...]-" means a
457 # failure of the test is allowed if any of the listed
458 # conditions are in the --xfail command-line option
459 # argument.
460 if self.xfail_str and re.search(r'\b(%s)\b' % xfail_cond,
461 self.xfail_str):
462 xfail = True
463 optional = False
464 if line.startswith('optional-'):
465 optional = True
466 line = line[len('optional-'):]
467 # Tokens in test data are space-separated, except for {...}
468 # tokens that may contain spaces.
469 tokens = []
470 while line:
471 match = re.match(r'\{(.*?)\}(.*)', line)
472 if match:
473 tokens.append(match.group(1))
474 line = match.group(2)
475 else:
476 match = re.match(r'([^ ]*)(.*)', line)
477 tokens.append(match.group(1))
478 line = match.group(2)
479 line = line.strip()
480 if tokens[0] == 'allow-header':
481 if len(tokens) != 2 or xfail or optional:
482 raise ValueError('bad allow-header line: %s' % orig_line)
483 if tokens[1] not in self.headers_handled:
484 self.load_tests(tokens[1], True)
485 return
486 if tokens[0] == 'allow':
487 if len(tokens) != 2 or xfail or optional:
488 raise ValueError('bad allow line: %s' % orig_line)
489 self.add_allow(tokens[1], True)
490 return
491 test_classes = {'element': ElementTest,
492 'macro': ConstantTest,
493 'constant': ConstantTest,
494 'macro-constant': ConstantTest,
495 'macro-int-constant': ConstantTest,
496 'symbol': SymbolTest,
497 'type': TypeTest,
498 'tag': TagTest,
499 'function': FunctionTest,
500 'variable': VariableTest,
501 'macro-function': MacroFunctionTest,
502 'macro-str': MacroStrTest}
503 test = test_classes[tokens[0]](*tokens)
504 test.xfail = xfail
505 test.optional = optional
9a62a939 506 test.num = self.num_tests
cc6c89fa 507 test.subtests = []
9a62a939 508 self.num_tests += 1
c3ec0972
JM
509 self.add_allow(test.allow_name, False)
510 if not allow:
cc6c89fa 511 test.gen_subtests()
c3ec0972
JM
512 self.tests.append(test)
513
514 def load_tests(self, header, allow):
515 """Load tests of a header.
516
517 If allow is true, the header is one specified in allow-header
518 and so tests are marked as allowed for namespace purposes but
519 otherwise ignored.
520
521 """
522 self.headers_handled.add(header)
523 header_s = header.replace('/', '_')
524 temp_file = os.path.join(self.temp_dir, 'header-data-%s' % header_s)
525 cmd = ('%s -E -D%s -std=c99 -x c data/%s-data > %s'
526 % (self.cc, self.standard, header, temp_file))
527 subprocess.check_call(cmd, shell=True)
528 with open(temp_file, 'r') as tests:
529 for line in tests:
530 line = line.strip()
531 if line == '' or line.startswith('#'):
532 continue
533 self.handle_test_line(line, allow)
534
535 def note_error(self, name, xfail):
536 """Note a failing test."""
537 if xfail:
538 print('XFAIL: %s' % name)
539 self.xerrors += 1
540 else:
541 print('FAIL: %s' % name)
542 self.errors += 1
543 sys.stdout.flush()
544
545 def note_skip(self, name):
546 """Note a skipped test."""
547 print('SKIP: %s' % name)
548 self.skipped += 1
549 sys.stdout.flush()
550
551 def compile_test(self, name, text):
552 """Run a compilation test; return True if it passes."""
553 self.total += 1
554 if self.group_ignore:
555 return False
556 optional = self.group_optional
557 self.group_optional = False
558 if self.group_skip:
559 self.note_skip(name)
560 return False
561 c_file = os.path.join(self.temp_dir, 'test.c')
562 o_file = os.path.join(self.temp_dir, 'test.o')
563 with open(c_file, 'w') as c_file_out:
564 c_file_out.write('#include <%s>\n%s' % (self.header, text))
565 cmd = ('%s %s -c %s -o %s' % (self.cc, self.cflags, c_file, o_file))
566 try:
567 subprocess.check_call(cmd, shell=True)
568 except subprocess.CalledProcessError:
569 if optional:
570 print('MISSING: %s' % name)
571 sys.stdout.flush()
572 self.group_ignore = True
573 else:
574 self.note_error(name, self.group_xfail)
575 self.group_skip = True
576 return False
577 print('PASS: %s' % name)
578 sys.stdout.flush()
579 return True
580
581 def execute_test(self, name, text):
582 """Run an execution test."""
583 self.total += 1
584 if self.group_ignore:
585 return False
586 if self.group_skip:
587 self.note_skip(name)
588 return
589 c_file = os.path.join(self.temp_dir, 'test.c')
590 exe_file = os.path.join(self.temp_dir, 'test')
591 with open(c_file, 'w') as c_file_out:
592 c_file_out.write('#include <%s>\n%s' % (self.header, text))
593 cmd = ('%s %s %s -o %s' % (self.cc, self.cflags, c_file, exe_file))
594 try:
595 subprocess.check_call(cmd, shell=True)
596 except subprocess.CalledProcessError:
597 self.note_error(name, self.group_xfail)
598 return
599 if self.cross:
600 self.note_skip(name)
601 return
602 try:
603 subprocess.check_call(exe_file, shell=True)
604 except subprocess.CalledProcessError:
605 self.note_error(name, self.group_xfail)
606 return
607 print('PASS: %s' % name)
608 sys.stdout.flush()
609
610 def check_namespace(self, name):
611 """Check the namespace of a header."""
612 c_file = os.path.join(self.temp_dir, 'namespace.c')
613 out_file = os.path.join(self.temp_dir, 'namespace-out')
614 with open(c_file, 'w') as c_file_out:
615 c_file_out.write('#include <%s>\n' % self.header)
616 cmd = ('%s %s -E %s -P -Wp,-dN > %s'
617 % (self.cc, self.cflags_namespace, c_file, out_file))
618 subprocess.check_call(cmd, shell=True)
619 bad_tokens = set()
620 with open(out_file, 'r') as content:
621 for line in content:
622 line = line.strip()
623 if not line:
624 continue
625 if re.match(r'# [1-9]', line):
626 continue
627 match = re.match(r'#define (.*)', line)
628 if match:
629 self.check_token(bad_tokens, match.group(1))
630 continue
631 match = re.match(r'#undef (.*)', line)
632 if match:
633 bad_tokens.discard(match.group(1))
634 continue
635 # Tokenize the line and check identifiers found. The
636 # handling of strings does not allow for escaped
637 # quotes, no allowance is made for character
638 # constants, and hex floats may be wrongly split into
639 # tokens including identifiers, but this is sufficient
640 # in practice and matches the old perl script.
641 line = re.sub(r'"[^"]*"', '', line)
642 line = line.strip()
643 for token in re.split(r'[^A-Za-z0-9_]+', line):
644 if re.match(r'[A-Za-z_]', token):
645 self.check_token(bad_tokens, token)
646 if bad_tokens:
647 for token in sorted(bad_tokens):
648 print(' Namespace violation: "%s"' % token)
649 self.note_error(name, False)
650 else:
651 print('PASS: %s' % name)
652 sys.stdout.flush()
653
654 def run(self):
655 """Load and run tests of a header."""
656 with tempfile.TemporaryDirectory() as self.temp_dir:
657 self.load_tests(self.header, False)
658 self.group_optional = False
659 self.group_xfail = False
660 self.group_ignore = False
661 self.group_skip = False
662 available = self.compile_test('Availability of <%s>' % self.header,
663 '')
664 if available:
95edd05c
JM
665 # As an optimization, try running all non-optional,
666 # non-XFAILed compilation tests in a single execution
667 # of the compiler.
668 combined_list = []
669 for test in self.tests:
670 if not test.optional and not test.xfail:
671 for subtest in test.subtests:
672 if isinstance(subtest, CompileSubTest):
673 combined_list.append(subtest.text)
674 subtest.run_early = True
675 combined_ok = self.compile_test('Combined <%s> test'
676 % self.header,
677 '\n'.join(combined_list))
678 # Now run the other tests, or all tests if the
679 # combined test failed.
c3ec0972
JM
680 for test in self.tests:
681 # A test may run more than one subtest. If the
682 # initial subtest for an optional symbol fails,
683 # others are not run at all; if the initial
684 # subtest for an optional symbol succeeds, others
685 # are run and are not considered optional; if the
686 # initial subtest for a required symbol fails,
687 # others are skipped.
688 self.group_optional = test.optional
689 self.group_xfail = test.xfail
690 self.group_ignore = False
691 self.group_skip = False
cc6c89fa 692 for subtest in test.subtests:
95edd05c
JM
693 if combined_ok and subtest.run_early:
694 self.total += 1
695 print('PASSCOMBINED: %s' % subtest.name)
696 sys.stdout.flush()
697 else:
698 subtest.run(self)
c3ec0972
JM
699 namespace_name = 'Namespace of <%s>' % self.header
700 if available:
701 self.check_namespace(namespace_name)
702 else:
703 self.note_skip(namespace_name)
704 print('-' * 76)
705 print(' Total number of tests : %4d' % self.total)
706 print(' Number of failed tests : %4d' % self.errors)
707 print(' Number of xfailed tests : %4d' % self.xerrors)
708 print(' Number of skipped tests : %4d' % self.skipped)
709 sys.exit(1 if self.errors else 0)
710
711
712def main():
713 """The main entry point."""
714 parser = argparse.ArgumentParser(description='Check header contents.')
715 parser.add_argument('--header', metavar='HEADER',
716 help='name of header')
717 parser.add_argument('--standard', metavar='STD',
718 help='standard to use when processing header')
719 parser.add_argument('--cc', metavar='CC',
720 help='C compiler to use')
721 parser.add_argument('--flags', metavar='CFLAGS',
722 help='Compiler flags to use with CC')
723 parser.add_argument('--cross', action='store_true',
724 help='Do not run compiled test programs')
725 parser.add_argument('--xfail', metavar='COND',
726 help='Name of condition for XFAILs')
727 args = parser.parse_args()
728 tests = HeaderTests(args.header, args.standard, args.cc, args.flags,
729 args.cross, args.xfail)
730 tests.run()
731
732
733if __name__ == '__main__':
734 main()