diff mbox

[rdma-core,7/7] Check that published headers do not rely on internal headers

Message ID 20180209030904.22370-8-jgg@ziepe.ca (mailing list archive)
State Not Applicable
Headers show

Commit Message

Jason Gunthorpe Feb. 9, 2018, 3:09 a.m. UTC
From: Jason Gunthorpe <jgg@mellanox.com>

We already check that the published headers can be compiled, but that
test is done with our build directory in the search path, and can
see a full set of kernel headers.

This new CI test ensures that only glibc headers or two allowed kernel
headers are being #include'd by the public headers. This prevents
a very easy mistake to use a kernel ABI or private header in
the wrong place.

Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
---
 buildlib/check-build | 86 +++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 69 insertions(+), 17 deletions(-)
diff mbox

Patch

diff --git a/buildlib/check-build b/buildlib/check-build
index bfd53b21b789a4..766db7ae46259f 100755
--- a/buildlib/check-build
+++ b/buildlib/check-build
@@ -10,6 +10,7 @@  import shutil
 import subprocess
 import tempfile
 import sys
+import copy
 from contextlib import contextmanager;
 
 def get_src_dir():
@@ -204,31 +205,82 @@  def is_fixup(fn):
         return "buildlib/fixup-include/" in os.readlink(fn);
     return False;
 
-def test_public_headers(args):
-    """Test that every header file can be included on its own, and has no obvious
-    implicit dependencies. This is mainly intended to check the public
-    headers, but this sweeps in published internal headers too."""
-    incdir = os.path.abspath(os.path.join(args.BUILD,"include"));
+def get_headers(incdir):
     includes = set();
     for root,dirs,files in os.walk(incdir):
         for I in files:
             if I.endswith(".h"):
                 includes.add(os.path.join(root,I));
+    return includes;
+
+def compile_test_headers(tmpd,incdir,includes):
+    with open(os.path.join(tmpd,"build.ninja"),"wt") as F:
+        print >> F,"rule comp";
+        print >> F," command = %s -Werror -c -I %s $in -o $out"%(args.CC,incdir);
+        print >> F," description=Header check for $in";
+        count = 0;
+        for I in sorted(includes):
+            if is_obsolete(I) or is_fixup(I):
+                continue;
+            print >> F,"build %s : comp %s"%("out%d.o"%(count),I);
+            print >> F,"default %s"%("out%d.o"%(count));
+            count = count + 1;
+    subprocess.check_call(["ninja"],cwd=tmpd);
+
+def test_published_headers(args):
+    """Test that every header file can be included on its own, and has no obvious
+    implicit dependencies. This is intended as a first pass check of the public
+    installed API headers"""
+    incdir = os.path.abspath(os.path.join(args.BUILD,"include"));
+    includes = get_headers(incdir);
 
     # Make a little ninja file to compile each header
     with private_tmp() as tmpd:
-        with open(os.path.join(tmpd,"build.ninja"),"wt") as F:
-            print >> F,"rule comp";
-            print >> F," command = %s -Werror -c -I %s $in -o $out"%(args.CC,incdir);
-            print >> F," description=Header check for $in";
-            count = 0;
-            for I in sorted(includes):
-                if is_obsolete(I) or is_fixup(I):
-                    continue;
-                print >> F,"build %s : comp %s"%("out%d.o"%(count),I);
-                print >> F,"default %s"%("out%d.o"%(count));
-                count = count + 1;
-        subprocess.check_call(["ninja"],cwd=tmpd);
+        compile_test_headers(tmpd,incdir,includes);
+
+# -------------------------------------------------------------------------
+
+allowed_uapi_headers = {
+    # This header is installed in all supported distributions
+    "rdma/ib_user_sa.h",
+    "rdma/ib_user_verbs.h",
+}
+
+def test_installed_headers(args):
+    """This test also checks that the public headers can be compiled on their own,
+    but goes further and confirms that the public headers do not depend on any
+    internal headers, or kernel kAPI headers."""
+    with private_tmp() as tmpd:
+        env = copy.deepcopy(os.environ);
+        env["DESTDIR"] = tmpd;
+        subprocess.check_output(["ninja","install"],env=env);
+
+        includes = get_headers(tmpd);
+        incdir = os.path.commonprefix(includes);
+        rincludes = {I[len(incdir):] for I in includes};
+
+        bincdir = os.path.abspath(os.path.join(args.BUILD,"include"));
+        all_includes = set();
+        for I in get_headers(bincdir):
+            if not is_fixup(I) and not is_obsolete(I):
+                all_includes.add(I[len(bincdir)+1:]);
+
+        # Drop error includes for any include file that is internal, this way
+        # when we compile the public headers any include of an internal header
+        # will fail.
+        for I in sorted(all_includes - rincludes):
+            if I in allowed_uapi_headers:
+                continue;
+
+            I = os.path.join(incdir,I)
+            dfn = os.path.dirname(I);
+            if not os.path.isdir(dfn):
+                os.makedirs(dfn);
+            assert not os.path.exists(I);
+            with open(I,"w") as F:
+                print >> F,'#error "Private internal header"';
+
+        compile_test_headers(tmpd,incdir,includes);
 
 # -------------------------------------------------------------------------