(root)/
glibc-2.38/
conform/
conformtest.py
       1  #!/usr/bin/python3
       2  # Check header contents against the given standard.
       3  # Copyright (C) 2018-2023 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  
      20  import argparse
      21  import fnmatch
      22  import os.path
      23  import re
      24  import subprocess
      25  import sys
      26  import tempfile
      27  
      28  import glibcconform
      29  
      30  
      31  class ESC[4;38;5;81mCompileSubTest(ESC[4;38;5;149mobject):
      32      """A compilation subtest."""
      33  
      34      def __init__(self, name, text):
      35          """Initialize a CompileSubTest object."""
      36          self.run_early = False
      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  
      45  class ESC[4;38;5;81mExecuteSubTest(ESC[4;38;5;149mobject):
      46      """An execution subtest."""
      47  
      48      def __init__(self, name, text):
      49          """Initialize an ExecuteSubTest object."""
      50          self.run_early = False
      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  
      59  class ESC[4;38;5;81mElementTest(ESC[4;38;5;149mobject):
      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  
      70      def gen_subtests(self):
      71          """Generate subtests for an ElementTest."""
      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'
      81                  '}\n'
      82                  % vars(self))
      83          self.subtests.append(CompileSubTest(
      84              'Availability of member %s' % self.member_name,
      85              text))
      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))
      90          self.subtests.append(CompileSubTest(
      91              'Type of member %s' % self.member_name,
      92              text))
      93  
      94  
      95  class ESC[4;38;5;81mConstantTest(ESC[4;38;5;149mobject):
      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  
     114      def gen_subtests(self):
     115          """Generate subtests for a ConstantTest."""
     116          if 'macro' in self.symbol_type:
     117              text = ('#ifndef %(symbol)s\n'
     118                      '# error "Macro %(symbol)s not defined"\n'
     119                      '#endif\n'
     120                      % vars(self))
     121              self.subtests.append(CompileSubTest(
     122                  'Availability of macro %s' % self.symbol,
     123                  text))
     124          if 'constant' in self.symbol_type:
     125              text = ('__typeof__ (%(symbol)s) a_%(num)d = %(symbol)s;\n'
     126                      % vars(self))
     127              self.subtests.append(CompileSubTest(
     128                  'Availability of constant %s' % self.symbol,
     129                  text))
     130          if self.symbol_type == 'macro-int-constant':
     131              sym_bits_def_neg = ''.join(
     132                  '# if %s & (1LL << %d)\n'
     133                  '#  define conformtest_%d_bit_%d 0LL\n'
     134                  '# else\n'
     135                  '#  define conformtest_%d_bit_%d (1LL << %d)\n'
     136                  '# endif\n'
     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)
     140                                         for i in range(63))
     141              sym_bits_def_pos = ''.join(
     142                  '# if %s & (1ULL << %d)\n'
     143                  '#  define conformtest_%d_bit_%d (1ULL << %d)\n'
     144                  '# else\n'
     145                  '#  define conformtest_%d_bit_%d 0ULL\n'
     146                  '# endif\n'
     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)
     150                                         for i in range(64))
     151              text = ('#if %s < 0\n'
     152                      '# define conformtest_%d_negative 1\n'
     153                      '%s'
     154                      '# define conformtest_%d_value ~(%s)\n'
     155                      '#else\n'
     156                      '# define conformtest_%d_negative 0\n'
     157                      '%s'
     158                      '# define conformtest_%d_value (%s)\n'
     159                      '#endif\n'
     160                      '_Static_assert (((%s < 0) == conformtest_%d_negative) '
     161                      '&& (%s == conformtest_%d_value), '
     162                      '"value match inside and outside #if");\n'
     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))
     167              self.subtests.append(CompileSubTest(
     168                  '#if usability of symbol %s'% self.symbol,
     169                  text))
     170          if self.c_type is not None:
     171              if self.c_type.startswith('promoted:'):
     172                  c_type = self.c_type[len('promoted:'):]
     173                  text = ('__typeof__ ((%s) 0 + (%s) 0) a2_%d;\n'
     174                          % (c_type, c_type, self.num))
     175              else:
     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)
     178              self.subtests.append(CompileSubTest(
     179                  'Type of symbol %s' % self.symbol,
     180                  text))
     181          if self.op is not None:
     182              text = ('_Static_assert (%(symbol)s %(op)s %(value)s, '
     183                      '"value constraint");\n'
     184                      % vars(self))
     185              self.subtests.append(CompileSubTest(
     186                  'Value of symbol %s' % self.symbol,
     187                  text))
     188  
     189  
     190  class ESC[4;38;5;81mSymbolTest(ESC[4;38;5;149mobject):
     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  
     199      def gen_subtests(self):
     200          """Generate subtests for a SymbolTest."""
     201          text = ('void foobarbaz_%(num)d (void) {\n'
     202                  '__typeof__ (%(symbol)s) a_%(num)d = %(symbol)s;\n'
     203                  '}\n'
     204                  % vars(self))
     205          self.subtests.append(CompileSubTest(
     206              'Availability of symbol %s' % self.symbol,
     207              text))
     208          if self.value is not None:
     209              text = ('int main (void) { return %(symbol)s != %(symbol)s; }\n'
     210                      % vars(self))
     211              self.subtests.append(ExecuteSubTest(
     212                  'Value of symbol %s' % self.symbol,
     213                  text))
     214  
     215  
     216  class ESC[4;38;5;81mTypeTest(ESC[4;38;5;149mobject):
     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  
     232      def gen_subtests(self):
     233          """Generate subtests for a TypeTest."""
     234          text = ('%s %sa_%d;\n'
     235                  % (self.type_name, '*' if self.maybe_opaque else '', self.num))
     236          self.subtests.append(CompileSubTest(
     237              'Availability of type %s' % self.type_name,
     238              text))
     239  
     240  
     241  class ESC[4;38;5;81mTagTest(ESC[4;38;5;149mobject):
     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  
     254      def gen_subtests(self):
     255          """Generate subtests for a TagTest."""
     256          # If the tag is not declared, these function prototypes have
     257          # incompatible types.
     258          text = ('void foo_%(num)d (%(type_name)s *);\n'
     259                  'void foo_%(num)d (%(type_name)s *);\n'
     260                  % vars(self))
     261          self.subtests.append(CompileSubTest(
     262              'Availability of tag %s' % self.type_name,
     263              text))
     264  
     265  
     266  class ESC[4;38;5;81mFunctionTest(ESC[4;38;5;149mobject):
     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  
     282      def gen_subtests(self):
     283          """Generate subtests for a FunctionTest."""
     284          text = ('%(return_type)s (*foobarbaz_%(num)d) %(args)s '
     285                  '= %(function_name)s;\n'
     286                  % vars(self))
     287          self.subtests.append(CompileSubTest(
     288              'Availability of function %s' % self.function_name,
     289              text))
     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))
     293          self.subtests.append(CompileSubTest(
     294              'Type of function %s' % self.function_name,
     295              text))
     296  
     297  
     298  class ESC[4;38;5;81mVariableTest(ESC[4;38;5;149mobject):
     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  
     308      def gen_subtests(self):
     309          """Generate subtests for a VariableTest."""
     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))
     313          self.subtests.append(CompileSubTest(
     314              'Availability of variable %s' % self.var_name,
     315              text))
     316          text = ('extern %(var_type)s %(var_name)s%(rest)s;\n'
     317                  % vars(self))
     318          self.subtests.append(CompileSubTest(
     319              'Type of variable %s' % self.var_name,
     320              text))
     321  
     322  
     323  class ESC[4;38;5;81mMacroFunctionTest(ESC[4;38;5;149mobject):
     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  
     333      def gen_subtests(self):
     334          """Generate subtests for a MacroFunctionTest."""
     335          text = ('#ifndef %(function_name)s\n'
     336                  '%(return_type)s (*foobarbaz_%(num)d) %(args)s '
     337                  '= %(function_name)s;\n'
     338                  '#endif\n'
     339                  % vars(self))
     340          self.subtests.append(CompileSubTest(
     341              'Availability of macro %s' % self.function_name,
     342              text))
     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'
     346                  '#endif\n'
     347                  % vars(self))
     348          self.subtests.append(CompileSubTest(
     349              'Type of macro %s' % self.function_name,
     350              text))
     351  
     352  
     353  class ESC[4;38;5;81mMacroStrTest(ESC[4;38;5;149mobject):
     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  
     362      def gen_subtests(self):
     363          """Generate subtests for a MacroStrTest."""
     364          text = ('#ifndef %(macro_name)s\n'
     365                  '# error "Macro %(macro_name)s not defined"\n'
     366                  '#endif\n'
     367                  % vars(self))
     368          self.subtests.append(CompileSubTest(
     369              'Availability of macro %s' % self.macro_name,
     370              text))
     371          # We can't include <string.h> here.
     372          text = ('extern int (strcmp)(const char *, const char *);\n'
     373                  'int main (void) { return (strcmp) (%(macro_name)s, '
     374                  '%(value)s) != 0; }\n'
     375                  % vars(self))
     376          self.subtests.append(ExecuteSubTest(
     377              'Value of macro %s' % self.macro_name,
     378              text))
     379  
     380  
     381  class ESC[4;38;5;81mHeaderTests(ESC[4;38;5;149mobject):
     382      """The set of tests run for a header."""
     383  
     384      def __init__(self, header, standard, cc, flags, ldflags, libs,
     385                   run_program_prefix, cross, xfail):
     386          """Initialize a HeaderTests object."""
     387          self.header = header
     388          self.standard = standard
     389          self.cc = cc
     390          self.flags = flags
     391          self.ldflags = ldflags
     392          self.libs = libs
     393          self.run_program_prefix = run_program_prefix
     394          self.cross = cross
     395          self.xfail_str = xfail
     396          self.cflags_namespace = ('%s -fno-builtin %s -D_ISOMAC'
     397                                   % (flags, glibcconform.CFLAGS[standard]))
     398          # When compiling the conformance test programs, use of
     399          # __attribute__ in headers is disabled because of attributes
     400          # that affect the types of functions as seen by typeof.
     401          self.cflags = "%s '-D__attribute__(x)='" % self.cflags_namespace
     402          self.tests = []
     403          self.allow = set()
     404          self.allow_fnmatch = set()
     405          self.headers_handled = set()
     406          self.num_tests = 0
     407          self.total = 0
     408          self.skipped = 0
     409          self.errors = 0
     410          self.xerrors = 0
     411  
     412      def add_allow(self, name, pattern_ok):
     413          """Add an identifier as an allowed token for this header.
     414  
     415          If pattern_ok, fnmatch patterns are OK as well as
     416          identifiers.
     417  
     418          """
     419          if re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]*', name):
     420              self.allow.add(name)
     421          elif pattern_ok:
     422              self.allow_fnmatch.add(name)
     423          else:
     424              raise ValueError('bad identifier: %s' % name)
     425  
     426      def check_token(self, bad_tokens, token):
     427          """Check whether an identifier token is allowed, and record it in
     428          bad_tokens if not.
     429  
     430          """
     431          if token.startswith('_'):
     432              return
     433          if token in glibcconform.KEYWORDS[self.standard]:
     434              return
     435          if token in self.allow:
     436              return
     437          for pattern in self.allow_fnmatch:
     438              if fnmatch.fnmatch(token, pattern):
     439                  return
     440          bad_tokens.add(token)
     441  
     442      def handle_test_line(self, line, allow):
     443          """Handle a single line in the test data.
     444  
     445          If allow is true, the header is one specified in allow-header
     446          and so tests are marked as allowed for namespace purposes but
     447          otherwise ignored.
     448  
     449          """
     450          orig_line = line
     451          xfail = False
     452          if line.startswith('xfail-'):
     453              xfail = True
     454              line = line[len('xfail-'):]
     455          else:
     456              match = re.match(r'xfail\[(.*?)\]-(.*)', line)
     457              if match:
     458                  xfail_cond = match.group(1)
     459                  line = match.group(2)
     460                  # "xfail[cond]-" or "xfail[cond1|cond2|...]-" means a
     461                  # failure of the test is allowed if any of the listed
     462                  # conditions are in the --xfail command-line option
     463                  # argument.
     464                  if self.xfail_str and re.search(r'\b(%s)\b' % xfail_cond,
     465                                                  self.xfail_str):
     466                      xfail = True
     467          optional = False
     468          if line.startswith('optional-'):
     469              optional = True
     470              line = line[len('optional-'):]
     471          # Tokens in test data are space-separated, except for {...}
     472          # tokens that may contain spaces.
     473          tokens = []
     474          while line:
     475              match = re.match(r'\{(.*?)\}(.*)', line)
     476              if match:
     477                  tokens.append(match.group(1))
     478                  line = match.group(2)
     479              else:
     480                  match = re.match(r'([^ ]*)(.*)', line)
     481                  tokens.append(match.group(1))
     482                  line = match.group(2)
     483              line = line.strip()
     484          if tokens[0] == 'allow-header':
     485              if len(tokens) != 2 or xfail or optional:
     486                  raise ValueError('bad allow-header line: %s' % orig_line)
     487              if tokens[1] not in self.headers_handled:
     488                  self.load_tests(tokens[1], True)
     489              return
     490          if tokens[0] == 'allow':
     491              if len(tokens) != 2 or xfail or optional:
     492                  raise ValueError('bad allow line: %s' % orig_line)
     493              self.add_allow(tokens[1], True)
     494              return
     495          test_classes = {'element': ElementTest,
     496                          'macro': ConstantTest,
     497                          'constant': ConstantTest,
     498                          'macro-constant': ConstantTest,
     499                          'macro-int-constant': ConstantTest,
     500                          'symbol': SymbolTest,
     501                          'type': TypeTest,
     502                          'tag': TagTest,
     503                          'function': FunctionTest,
     504                          'variable': VariableTest,
     505                          'macro-function': MacroFunctionTest,
     506                          'macro-str': MacroStrTest}
     507          test = test_classes[tokens[0]](*tokens)
     508          test.xfail = xfail
     509          test.optional = optional
     510          test.num = self.num_tests
     511          test.subtests = []
     512          self.num_tests += 1
     513          self.add_allow(test.allow_name, False)
     514          if not allow:
     515              test.gen_subtests()
     516              self.tests.append(test)
     517  
     518      def load_tests(self, header, allow):
     519          """Load tests of a header.
     520  
     521          If allow is true, the header is one specified in allow-header
     522          and so tests are marked as allowed for namespace purposes but
     523          otherwise ignored.
     524  
     525          """
     526          self.headers_handled.add(header)
     527          header_s = header.replace('/', '_')
     528          temp_file = os.path.join(self.temp_dir, 'header-data-%s' % header_s)
     529          cmd = ('%s -E -D%s -std=c99 -x c data/%s-data > %s'
     530                 % (self.cc, self.standard, header, temp_file))
     531          subprocess.check_call(cmd, shell=True)
     532          with open(temp_file, 'r') as tests:
     533              for line in tests:
     534                  line = line.strip()
     535                  if line == '' or line.startswith('#'):
     536                      continue
     537                  self.handle_test_line(line, allow)
     538  
     539      def note_error(self, name, xfail):
     540          """Note a failing test."""
     541          if xfail:
     542              print('XFAIL: %s' % name)
     543              self.xerrors += 1
     544          else:
     545              print('FAIL: %s' % name)
     546              self.errors += 1
     547          sys.stdout.flush()
     548  
     549      def note_skip(self, name):
     550          """Note a skipped test."""
     551          print('SKIP: %s' % name)
     552          self.skipped += 1
     553          sys.stdout.flush()
     554  
     555      def compile_test(self, name, text):
     556          """Run a compilation test; return True if it passes."""
     557          self.total += 1
     558          if self.group_ignore:
     559              return False
     560          optional = self.group_optional
     561          self.group_optional = False
     562          if self.group_skip:
     563              self.note_skip(name)
     564              return False
     565          c_file = os.path.join(self.temp_dir, 'test.c')
     566          o_file = os.path.join(self.temp_dir, 'test.o')
     567          with open(c_file, 'w') as c_file_out:
     568              c_file_out.write('#include <%s>\n%s' % (self.header, text))
     569          cmd = ('%s %s -c %s -o %s' % (self.cc, self.cflags, c_file, o_file))
     570          try:
     571              subprocess.check_call(cmd, shell=True)
     572          except subprocess.CalledProcessError:
     573              if optional:
     574                  print('MISSING: %s' % name)
     575                  sys.stdout.flush()
     576                  self.group_ignore = True
     577              else:
     578                  self.note_error(name, self.group_xfail)
     579                  self.group_skip = True
     580              return False
     581          print('PASS: %s' % name)
     582          sys.stdout.flush()
     583          return True
     584  
     585      def execute_test(self, name, text):
     586          """Run an execution test."""
     587          self.total += 1
     588          if self.group_ignore:
     589              return False
     590          if self.group_skip:
     591              self.note_skip(name)
     592              return
     593          c_file = os.path.join(self.temp_dir, 'test.c')
     594          exe_file = os.path.join(self.temp_dir, 'test')
     595          with open(c_file, 'w') as c_file_out:
     596              c_file_out.write('#include <%s>\n%s' % (self.header, text))
     597          cmd = ('%s %s %s %s %s -o %s' % (self.cc, self.cflags, self.ldflags,
     598                                           c_file, self.libs, exe_file))
     599          try:
     600              subprocess.check_call(cmd, shell=True)
     601          except subprocess.CalledProcessError:
     602              self.note_error(name, self.group_xfail)
     603              return
     604          if self.cross:
     605              self.note_skip(name)
     606              return
     607          try:
     608              subprocess.check_call('%s %s' % (self.run_program_prefix,
     609                                               exe_file),
     610                                    shell=True)
     611          except subprocess.CalledProcessError:
     612              self.note_error(name, self.group_xfail)
     613              return
     614          print('PASS: %s' % name)
     615          sys.stdout.flush()
     616  
     617      def check_namespace(self, name):
     618          """Check the namespace of a header."""
     619          c_file = os.path.join(self.temp_dir, 'namespace.c')
     620          out_file = os.path.join(self.temp_dir, 'namespace-out')
     621          with open(c_file, 'w') as c_file_out:
     622              c_file_out.write('#include <%s>\n' % self.header)
     623          cmd = ('%s %s -E %s -P -Wp,-dN > %s'
     624                 % (self.cc, self.cflags_namespace, c_file, out_file))
     625          subprocess.check_call(cmd, shell=True)
     626          bad_tokens = set()
     627          with open(out_file, 'r') as content:
     628              for line in content:
     629                  line = line.strip()
     630                  if not line:
     631                      continue
     632                  if re.match(r'# [1-9]', line):
     633                      continue
     634                  if line.startswith('#pragma GCC '):
     635                      # No GCC pragma uses macro expansion, so no
     636                      # namespace issues arise from such pragmas.  (Some
     637                      # pragmas not in the GCC namespace do macro-expand
     638                      # their arguments and so could be affected by
     639                      # macros defined by user code including the
     640                      # header.)
     641                      continue
     642                  match = re.match(r'#define (.*)', line)
     643                  if match:
     644                      self.check_token(bad_tokens, match.group(1))
     645                      continue
     646                  match = re.match(r'#undef (.*)', line)
     647                  if match:
     648                      bad_tokens.discard(match.group(1))
     649                      continue
     650                  # Tokenize the line and check identifiers found.  The
     651                  # handling of strings and character constants does not
     652                  # allow for escaped quotes, and hex floats may be
     653                  # wrongly split into tokens including identifiers, but
     654                  # this is sufficient in practice.
     655                  line = re.sub(r'(?:\bL)?(?:"[^"]*"|\'[^\']*\')', '', line)
     656                  line = line.strip()
     657                  for token in re.split(r'[^A-Za-z0-9_]+', line):
     658                      if re.match(r'[A-Za-z_]', token):
     659                          self.check_token(bad_tokens, token)
     660          if bad_tokens:
     661              for token in sorted(bad_tokens):
     662                  print('    Namespace violation: "%s"' % token)
     663              self.note_error(name, False)
     664          else:
     665              print('PASS: %s' % name)
     666              sys.stdout.flush()
     667  
     668      def run(self):
     669          """Load and run tests of a header."""
     670          with tempfile.TemporaryDirectory() as self.temp_dir:
     671              self.load_tests(self.header, False)
     672              self.group_optional = False
     673              self.group_xfail = False
     674              self.group_ignore = False
     675              self.group_skip = False
     676              available = self.compile_test('Availability of <%s>' % self.header,
     677                                            '')
     678              if available:
     679                  # As an optimization, try running all non-optional,
     680                  # non-XFAILed compilation tests in a single execution
     681                  # of the compiler.
     682                  combined_list = []
     683                  for test in self.tests:
     684                      if not test.optional and not test.xfail:
     685                          for subtest in test.subtests:
     686                              if isinstance(subtest, CompileSubTest):
     687                                  combined_list.append(subtest.text)
     688                                  subtest.run_early = True
     689                  combined_ok = self.compile_test('Combined <%s> test'
     690                                                  % self.header,
     691                                                  '\n'.join(combined_list))
     692                  # Now run the other tests, or all tests if the
     693                  # combined test failed.
     694                  for test in self.tests:
     695                      # A test may run more than one subtest.  If the
     696                      # initial subtest for an optional symbol fails,
     697                      # others are not run at all; if the initial
     698                      # subtest for an optional symbol succeeds, others
     699                      # are run and are not considered optional; if the
     700                      # initial subtest for a required symbol fails,
     701                      # others are skipped.
     702                      self.group_optional = test.optional
     703                      self.group_xfail = test.xfail
     704                      self.group_ignore = False
     705                      self.group_skip = False
     706                      for subtest in test.subtests:
     707                          if combined_ok and subtest.run_early:
     708                              self.total += 1
     709                              print('PASSCOMBINED: %s' % subtest.name)
     710                              sys.stdout.flush()
     711                          else:
     712                              subtest.run(self)
     713              namespace_name = 'Namespace of <%s>' % self.header
     714              if available:
     715                  self.check_namespace(namespace_name)
     716              else:
     717                  self.note_skip(namespace_name)
     718          print('-' * 76)
     719          print('  Total number of tests   : %4d' % self.total)
     720          print('  Number of failed tests  : %4d' % self.errors)
     721          print('  Number of xfailed tests : %4d' % self.xerrors)
     722          print('  Number of skipped tests : %4d' % self.skipped)
     723          sys.exit(1 if self.errors else 0)
     724  
     725  
     726  def main():
     727      """The main entry point."""
     728      parser = argparse.ArgumentParser(description='Check header contents.')
     729      parser.add_argument('--header', metavar='HEADER',
     730                          help='name of header')
     731      parser.add_argument('--standard', metavar='STD',
     732                          help='standard to use when processing header')
     733      parser.add_argument('--cc', metavar='CC',
     734                          help='C compiler to use')
     735      parser.add_argument('--flags', metavar='CFLAGS',
     736                          help='Compiler flags to use with CC')
     737      parser.add_argument('--ldflags', metavar='LDFLAGS',
     738                          help='Compiler arguments for linking before inputs')
     739      parser.add_argument('--libs', metavar='LIBS',
     740                          help='Compiler arguments for linking after inputs')
     741      parser.add_argument('--run-program-prefix', metavar='RUN-PROGRAM-PREFIX',
     742                          help='Wrapper for running newly built program')
     743      parser.add_argument('--cross', action='store_true',
     744                          help='Do not run compiled test programs')
     745      parser.add_argument('--xfail', metavar='COND',
     746                          help='Name of condition for XFAILs')
     747      args = parser.parse_args()
     748      tests = HeaderTests(args.header, args.standard, args.cc, args.flags,
     749                          args.ldflags, args.libs, args.run_program_prefix,
     750                          args.cross, args.xfail)
     751      tests.run()
     752  
     753  
     754  if __name__ == '__main__':
     755      main()