conformtest.py 30 KB

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