about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--Makefile9
-rw-r--r--Rules11
-rw-r--r--scripts/check-wrapper-headers.py113
4 files changed, 138 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index f3368b7bcb..25985141e2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2019-02-15  Florian Weimer  <fweimer@redhat.com>
 
+	Check that non-sysdeps headers have wrapper headers.
+	* scripts/check-wrapper-headers.py: New file.
+	* Makefile (tests-special): Add check-wrapper-headers.out.
+	(check-wrapper-headers.out): New target.
+	* Rules (tests-special): Add check-wrapper-headers.out.
+	(check-wrapper-headers.out): New target.
+
+2019-02-15  Florian Weimer  <fweimer@redhat.com>
+
 	Add missing header wrappers under include/.
 	* include/ar.h: New file.
 	* include/bits/mqueue2.h: Likewise.
diff --git a/Makefile b/Makefile
index 1bfe7fefe1..dc5de7aa6b 100644
--- a/Makefile
+++ b/Makefile
@@ -331,8 +331,13 @@ $(objpfx)check-installed-headers-cxx.out: \
 	  "$(CXX) $(filter-out -std=%,$(CXXFLAGS)) -D_ISOMAC $(+includes)" \
 	  $(headers) > $@; \
 	$(evaluate-test)
-endif
-endif
+endif # $(CXX)
+
+tests-special += $(objpfx)check-wrapper-headers.out
+$(objpfx)check-wrapper-headers.out: scripts/check-wrapper-headers.py $(headers)
+	$(PYTHON) $< --root=. --subdir=. $(headers) \
+	  --generated $(common-generated) > $@; $(evaluate-test)
+endif # $(headers)
 
 define summarize-tests
 @egrep -v '^(PASS|XFAIL):' $(objpfx)$1 || true
diff --git a/Rules b/Rules
index 1562f2ce6d..e08a28d9f3 100644
--- a/Rules
+++ b/Rules
@@ -101,8 +101,15 @@ $(objpfx)check-installed-headers-cxx.out: \
 	  "$(CXX) $(filter-out -std=%,$(CXXFLAGS)) -D_ISOMAC $(+includes)" \
 	  $(headers) > $@; \
 	$(evaluate-test)
-endif
-endif
+endif # $(CXX)
+
+tests-special += $(objpfx)check-wrapper-headers.out
+$(objpfx)check-wrapper-headers.out: \
+  $(..)scripts/check-wrapper-headers.py $(headers)
+	$(PYTHON) $< --root=$(..) --subdir=$(subdir) $(headers) > $@; \
+	  $(evaluate-test)
+
+endif # $(headers)
 
 # This makes all the auxiliary and test programs.
 
diff --git a/scripts/check-wrapper-headers.py b/scripts/check-wrapper-headers.py
new file mode 100644
index 0000000000..094faa3ced
--- /dev/null
+++ b/scripts/check-wrapper-headers.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python3
+# Check that a wrapper header exist for each non-sysdeps header.
+# Copyright (C) 2019 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
+# <http://www.gnu.org/licenses/>.
+
+# 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
+        # <https://sourceware.org/ml/libc-alpha/2016-05/msg00189.html>.
+        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:
+            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()