check-wrapper-headers.py 4.6 KB

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