@@ -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);
# -------------------------------------------------------------------------