(root)/
glibc-2.38/
scripts/
check-wrapper-headers.py
       1  #!/usr/bin/python3
       2  # Check that a wrapper header exist for each non-sysdeps header.
       3  # Copyright (C) 2019-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  # Non-sysdeps subdirectories are not on the C include path, so
      21  # installed headers need to have a sysdep wrapper header.
      22  #
      23  # usage: scripts/checl-wrapper-headers.py \
      24  #     --root=$(..) --subdir=$(subdir) $(headers) \
      25  #     [--generated $(common-generated)]
      26  #
      27  # If invoked with --root=., the script is invoked from the root of the
      28  # source tree, so paths starting with "include/" are skipped (because
      29  # those do not require wrappers).
      30  
      31  import argparse
      32  import os
      33  import sys
      34  
      35  # Some subdirectories are only compiled for essentially one target.
      36  # In this case, we do not need to check for consistent wrapper
      37  # headers.  Hurd uses a custom way to Hurd-specific inject wrapper
      38  # headers; see sysdeps/mach/Makefiles under "ifdef in-Makerules".
      39  SINGLE_TARGET_SUBDIRS = frozenset(("hurd", "mach"))
      40  
      41  # Name of the special subdirectory with the wrapper headers.
      42  INCLUDE = "include"
      43  
      44  def check_sysdeps_bits(args):
      45      """Check that the directory sysdeps/generic/bits does not exist."""
      46      bits = os.path.join(args.root, 'sysdeps', 'generic', 'bits')
      47      if os.path.exists(bits):
      48          # See commit c72565e5f1124c2dc72573e83406fe999e56091f and
      49          # <https://sourceware.org/ml/libc-alpha/2016-05/msg00189.html>.
      50          print('error: directory {} has been added, use bits/ instead'.format(
      51              os.path.relpath(os.path.realpath(bits), args.root)))
      52          return False
      53      return True
      54  
      55  def check_headers_root(args):
      56      """Check headers located at the top level of the source tree."""
      57      good = True
      58      generated = frozenset(args.generated)
      59      for header in args.headers:
      60          if not (header.startswith('bits/')
      61                  or os.path.exists(os.path.join(args.root, INCLUDE, header))
      62                  or header in generated):
      63              print('error: top-level header {} must be in bits/ or {}/'
      64                    .format(header, INCLUDE))
      65              good = False
      66      return good
      67  
      68  def check_headers(args):
      69      """Check headers located in a subdirectory."""
      70      good = True
      71      for header in args.headers:
      72          # Whitelist .x files, which never have include wrappers.
      73          if header.endswith(".x"):
      74              continue
      75  
      76          is_nonsysdep_header = os.access(header, os.R_OK)
      77          if is_nonsysdep_header:
      78              # Skip Fortran header files.
      79              if header.startswith("finclude/"):
      80                  continue
      81  
      82              include_path = os.path.join(args.root, INCLUDE, header)
      83              if not os.access(include_path, os.R_OK):
      84                  print('error: missing wrapper header {} for {}'.format(
      85                      os.path.join(INCLUDE, header),
      86                      os.path.relpath(os.path.realpath(header), args.root)))
      87                  good = False
      88      return good
      89  
      90  def main():
      91      """The main entry point."""
      92      parser = argparse.ArgumentParser(
      93          description='Check for missing wrapper headers in include/.')
      94      parser.add_argument('--root', metavar='DIRECTORY', required=True,
      95                          help='Path to the top-level of the source tree')
      96      parser.add_argument('--subdir', metavar='DIRECTORY', required=True,
      97                          help='Name of the subdirectory being processed')
      98      parser.add_argument('--generated', metavar='FILE', default="", nargs="*",
      99                          help="Generated files (which are ignored)")
     100      parser.add_argument('headers', help='Header files to process', nargs='+')
     101      args = parser.parse_args()
     102  
     103      good = (args.root == '.') == (args.subdir == '.')
     104      if not good:
     105          print('error: --root/--subdir disagree about top-of-tree location')
     106  
     107      if args.subdir == '.':
     108          good &= check_sysdeps_bits(args)
     109          good &= check_headers_root(args)
     110      elif args.subdir not in SINGLE_TARGET_SUBDIRS:
     111          good &= check_headers(args)
     112  
     113      if not good:
     114          sys.exit(1)
     115  
     116  if __name__ == '__main__':
     117      main()