[v3,0/7] Fix some libm static issues
[platform/upstream/glibc.git] / 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-2024 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()