#!/usr/bin/python3
# Check that a wrapper header exist for each non-sysdeps header.
# Copyright (C) 2019-2024 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
#
# The GNU C Library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# The GNU C Library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with the GNU C Library; if not, see
# .
# Non-sysdeps subdirectories are not on the C include path, so
# installed headers need to have a sysdep wrapper header.
#
# usage: scripts/checl-wrapper-headers.py \
# --root=$(..) --subdir=$(subdir) $(headers) \
# [--generated $(common-generated)]
#
# If invoked with --root=., the script is invoked from the root of the
# source tree, so paths starting with "include/" are skipped (because
# those do not require wrappers).
import argparse
import os
import sys
# Some subdirectories are only compiled for essentially one target.
# In this case, we do not need to check for consistent wrapper
# headers. Hurd uses a custom way to Hurd-specific inject wrapper
# headers; see sysdeps/mach/Makefiles under "ifdef in-Makerules".
SINGLE_TARGET_SUBDIRS = frozenset(("hurd", "mach"))
# Name of the special subdirectory with the wrapper headers.
INCLUDE = "include"
def check_sysdeps_bits(args):
"""Check that the directory sysdeps/generic/bits does not exist."""
bits = os.path.join(args.root, 'sysdeps', 'generic', 'bits')
if os.path.exists(bits):
# See commit c72565e5f1124c2dc72573e83406fe999e56091f and
# .
print('error: directory {} has been added, use bits/ instead'.format(
os.path.relpath(os.path.realpath(bits), args.root)))
return False
return True
def check_headers_root(args):
"""Check headers located at the top level of the source tree."""
good = True
generated = frozenset(args.generated)
for header in args.headers:
if not (header.startswith('bits/')
or os.path.exists(os.path.join(args.root, INCLUDE, header))
or header in generated):
print('error: top-level header {} must be in bits/ or {}/'
.format(header, INCLUDE))
good = False
return good
def check_headers(args):
"""Check headers located in a subdirectory."""
good = True
for header in args.headers:
# Whitelist .x files, which never have include wrappers.
if header.endswith(".x"):
continue
is_nonsysdep_header = os.access(header, os.R_OK)
if is_nonsysdep_header:
# Skip Fortran header files.
if header.startswith("finclude/"):
continue
include_path = os.path.join(args.root, INCLUDE, header)
if not os.access(include_path, os.R_OK):
print('error: missing wrapper header {} for {}'.format(
os.path.join(INCLUDE, header),
os.path.relpath(os.path.realpath(header), args.root)))
good = False
return good
def main():
"""The main entry point."""
parser = argparse.ArgumentParser(
description='Check for missing wrapper headers in include/.')
parser.add_argument('--root', metavar='DIRECTORY', required=True,
help='Path to the top-level of the source tree')
parser.add_argument('--subdir', metavar='DIRECTORY', required=True,
help='Name of the subdirectory being processed')
parser.add_argument('--generated', metavar='FILE', default="", nargs="*",
help="Generated files (which are ignored)")
parser.add_argument('headers', help='Header files to process', nargs='+')
args = parser.parse_args()
good = (args.root == '.') == (args.subdir == '.')
if not good:
print('error: --root/--subdir disagree about top-of-tree location')
if args.subdir == '.':
good &= check_sysdeps_bits(args)
good &= check_headers_root(args)
elif args.subdir not in SINGLE_TARGET_SUBDIRS:
good &= check_headers(args)
if not good:
sys.exit(1)
if __name__ == '__main__':
main()