[PATCH v4 02/79] scripts/headers_compile_test.sh: compile test script for exported headers

From: Mikko Rapeli
Date: Thu Oct 15 2015 - 01:58:12 EST


Users of kernel header files would be happier if they did not contain
kernel specific parts and would contain #include statements for all
other header files that they depend on, and in general would compile.

For each header file exported to userspace, this script creates
a simple .c file which just includes the header file. Then it
tries to compile it together with minimal header files from GCC
and libc, and reports results.

Default libc and GCC header file locations are parsed from compiler
configuration.

Kernel headers depend on GCC headers so their path is included in
the test compiler command line.

Some gcc and kernel headers depend on libc headers which are made available
by copying from the compiler default location to a temporary location and
removing possibly existing kernel headers from this directory. This is a bit
of a hack but seems to work in multiple environments.

Tested natively on:

Debian unstable, i586-linux-gnu and gcc 4.9.2
Raspbian Wheezy, arm-linux-gnueabihf and gcc 4.6.3
Ubuntu 12.04 LTS, x86_64-linux-gnu and gcc 4.6.3

Tested cross compilation using standard CROSS_COMPILE=/path/to/gcc with:

arm-linux-gnueabihf-gcc (crosstool-NG linaro-1.13.1-4.9-2014.09 - Linaro GCC 4.9-2014.09) 4.9.2 20140904 (prerelease)

Execute the script in the directory where kernel header files are installed.
For example:

$ make headers_install
$ cd usr/include
$ ../../scripts/headers_compile_test.sh

Example statistics from 3.18.4 kernel:

130 files failed the compile test.
609 files passed the compile test.

Example error types from 3.18.4 kernel:

$ ../../scripts/headers_compile_test.sh 2>&1 | \
grep error: | sed -e 's/.*error://g' | sort | uniq -c | sort -rn

352 unknown type name âuint32_tâ
134 unknown type name âuint64_tâ
103 unknown type name âsize_tâ
43 unknown type name â__kernel_ulong_tâ
38 unknown type name âuint8_tâ
24 unknown type name âint32_tâ
22 field âaddrâ has incomplete type
18 field âtstampâ has incomplete type
16 unknown type name â__kernel_time_tâ
16 field âinâ has incomplete type
16 field âin6â has incomplete type
14 unknown type name â__be16â
13 âIFNAMSIZâ undeclared here (not in a function)
12 unknown type name âuint16_tâ
9 field âifru_netmaskâ has incomplete type
9 field âifru_hwaddrâ has incomplete type
9 field âifru_dstaddrâ has incomplete type
9 field âifru_broadaddrâ has incomplete type
9 field âifru_addrâ has incomplete type
8 unknown type name â__kernel_pid_tâ
7 unknown type name âu_shortâ
7 unknown type name âpid_tâ
6 invalid application of âsizeofâ to incomplete type âstruct timespecâ
6 field âsrcâ has incomplete type
6 field âaudio_tstampâ has incomplete type
6 array type has incomplete element type
5 unknown type name â__kernel_long_tâ
5 requested alignment is not an integer constant
5 field âsmskâ has incomplete type
4 unknown type name â__kernel_uid32_tâ
4 unknown type name â__kernel_gid32_tâ
4 âETH_ALENâ undeclared here (not in a function)
3 unknown type name âint64_tâ
3 unknown type name âcaddr_tâ
3 âIPSET_ERR_TYPE_SPECIFICâ undeclared here (not in a function)
3 field âtrigger_tstampâ has incomplete type
3 field âsrc_addrâ has incomplete type
3 field âsin_addrâ has incomplete type
3 field âladdrâ has incomplete type
3 field âidâ has incomplete type
3 field âdmskâ has incomplete type
3 field âbssidâ has incomplete type
3 expected specifier-qualifier-list before âuint64_tâ
2 unknown type name âu_longâ
2 unknown type name âstack_tâ
2 unknown type name âsigset_tâ
2 unknown type name âsa_family_tâ
2 unknown type name â__kernel_mode_tâ
2 unknown type name â__kernel_key_tâ
2 unknown type name âelf_gregset_tâ
2 unknown type name âboolâ
2 âuint64_tâ undeclared here (not in a function)
2 âtrueâ undeclared (first use in this function)
2 âNAME_MAXâ undeclared here (not in a function)
2 â__kernel_mode_tâ undeclared here (not in a function)
2 invalid application of âsizeofâ to incomplete type âstruct sockaddrâ
2 field âuc_mcontextâ has incomplete type
2 field âtmskâ has incomplete type
2 field âtgtâ has incomplete type
2 field âshm_permâ has incomplete type
2 field âsem_permâ has incomplete type
2 field âraddrâ has incomplete type
2 field âmsg_permâ has incomplete type
2 field âgrpâ has incomplete type
2 field âdstâ has incomplete type
2 field âdst_addrâ has incomplete type
2 field âarp_paâ has incomplete type
2 field âarp_netmaskâ has incomplete type
2 field âarp_haâ has incomplete type
2 âfalseâ undeclared (first use in this function)
1 xen/interface/xen.h: No such file or directory
1 via_drmclient.h: No such file or directory
1 unknown type name âwait_queue_head_tâ
1 unknown type name âsnd_seq_client_type_tâ
1 unknown type name âint16_tâ
1 unknown type name âino_tâ
1 unknown type name âelf_greg_tâ
1 unknown type name âelf_fpxregset_tâ
1 unknown type name âelf_fpregset_tâ
1 unknown type name â__be32â
1 âSIOCDEVPRIVATEâ undeclared here (not in a function)
1 âsa_family_tâ undeclared here (not in a function)
1 âNULLâ undeclared (first use in this function)
1 âMSG_FINâ undeclared here (not in a function)
1 âMAX_IPOPTLENâ undeclared here (not in a function)
1 âMAX_ADDR_LENâ undeclared here (not in a function)
1 âIFHWADDRLENâ undeclared here (not in a function)
1 field âvmaskâ has incomplete type
1 field âvifc_rmt_addrâ has incomplete type
1 field âvifc_lcl_addrâ has incomplete type
1 field âvaddrâ has incomplete type
1 field âuc_chainâ has incomplete type
1 field âtgt_ipâ has incomplete type
1 field âtcpâ has incomplete type
1 field âsw_reservedâ has incomplete type
1 field âsspp_addrâ has incomplete type
1 field âssp_addrâ has incomplete type
1 field âsrc_maskâ has incomplete type
1 field âsrc_ipâ has incomplete type
1 field âspt_addressâ has incomplete type
1 field âspp_addressâ has incomplete type
1 field âspinfo_addressâ has incomplete type
1 field âspc_aaddrâ has incomplete type
1 field âsas_obs_rto_ipaddrâ has incomplete type
1 field âsaddrâ has incomplete type
1 field ârtmsg_srcâ has incomplete type
1 field ârtmsg_gatewayâ has incomplete type
1 field ârtmsg_dstâ has incomplete type
1 field ârt_genmaskâ has incomplete type
1 field ârt_gatewayâ has incomplete type
1 field ârt_dstâ has incomplete type
1 field ârealâ has incomplete type
1 field âprefixâ has incomplete type
1 field âobj_listâ has incomplete type
1 field âmfcc_originâ has incomplete type
1 field âmfcc_mcastgrpâ has incomplete type
1 field âmf6cc_originâ has incomplete type
1 field âmf6cc_mcastgrpâ has incomplete type
1 field âmaskâ has incomplete type
1 field âival2â has incomplete type
1 field âival1â has incomplete type
1 field âiphâ has incomplete type
1 field âipâ has incomplete type
1 field âim_srcâ has incomplete type
1 field âim_dstâ has incomplete type
1 field âim6_srcâ has incomplete type
1 field âim6_dstâ has incomplete type
1 field âgwâ has incomplete type
1 field âexpectedâ has incomplete type
1 field âdst_maskâ has incomplete type
1 field âdest_addrâ has incomplete type
1 field âdaddrâ has incomplete type
1 field âap_addrâ has incomplete type
1 field âa6â has incomplete type
1 field âa4â has incomplete type
1 expected specifier-qualifier-list before âDECLARE_BITMAPâ
1 expected â=â, â,â, â;â, âasmâ or â__attribute__â before âsnd_seq_client_type_tâ
1 #error "patchkey.h included directly"
1 âDLM_RESNAME_MAXLENâ undeclared here (not in a function)

Signed-off-by: Mikko Rapeli <mikko.rapeli@xxxxxx>
---
scripts/headers_compile_test.sh | 143 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 143 insertions(+)
create mode 100755 scripts/headers_compile_test.sh

diff --git a/scripts/headers_compile_test.sh b/scripts/headers_compile_test.sh
new file mode 100755
index 0000000..9a73945
--- /dev/null
+++ b/scripts/headers_compile_test.sh
@@ -0,0 +1,143 @@
+#!/bin/bash
+# bash due to arithmetics and pipefail
+set -euo pipefail
+
+# Debugging
+#set -x
+
+echo Simple compile test for header files meant for userspace.
+
+# sanity test
+if [ ! -d ./linux ]; then
+ echo Sanity check error: ./linux directory not found
+ echo Should be called in usr/include after \'make headers_install\'.
+ echo Returns number of failed files, 0 if none.
+ exit 1
+fi
+
+# Support CC variable for compiler and ccache, and cross compiling.
+# CC is used without quotes to support CC="ccache gcc".
+set +u
+if [ "$CC"foobar == "foobar" ]; then
+ CC=cc
+fi
+
+if [ "$CROSS_COMPILE"foobar != "foobar" ]; then
+ # Using gcc name since some cross compiler tool chains don't provide
+ # the cc symlink
+ CC="$CROSS_COMPILE"gcc
+fi
+set -u
+
+# Kernel headers refer to some gcc and libc headers so make them available.
+set +u
+if [ "$ARCH_TRIPLET"foobar == "foobar" ]; then
+ # Taking triplet from gcc/cpp
+ ARCH_TRIPLET="$( $CC -v -x c -E - < /dev/null 2>&1 | \
+ grep Target | sed -e 's/Target: //' )"
+fi
+
+if [ "$LIBC"foobar == "foobar" ]; then
+ # trying to grep libc includes from gcc/cpp defaults
+ _TEMP="$( $CC -v -x c -E - < /dev/null 2>&1 | \
+ sed -n -e '/^#include <...> search starts here:$/,/^End of search list.$/{//!p}' | \
+ sed -e 's/^\ \//\//g' | \
+ grep '/usr/include' )"
+
+ # sanity check and prepare LIBC dirs
+ for d in $_TEMP; do
+ if [ ! -d "$d" ]; then
+ echo "$d not a directory"
+ exit 1
+ fi
+ LIBC="$LIBC $d"
+ done
+fi
+set -u
+
+# Copy libc include files to temp directory for the tests.
+
+COMPILE_TEST_INC=../headers_compile_test_include
+rm -rf "$COMPILE_TEST_INC"
+mkdir -p "$COMPILE_TEST_INC"
+
+for d in $LIBC; do
+ # check if last part of dir is the arch triplet, cross compile paths
+ # can have it also elsewhere so just the last one counts.
+ if !( echo "$d" | egrep "$ARCH_TRIPLET$" > /dev/null ); then
+ # hopefully just main libc dir, e.g. /usr/include
+ cp -a "$d"/* "$COMPILE_TEST_INC"/
+ elif ( echo "$d" | egrep "$ARCH_TRIPLET$" > /dev/null ); then
+ # hopefully the arch specific dir, e.g. /usr/include/x86_64-linux-gnu
+ cp -ar "$d"/* "$COMPILE_TEST_INC/"
+ else
+ echo "$d unexpected, bailing out"
+ exit 1
+ fi
+done
+
+# Simulate libc headers without kernel headers by removing
+# all known kernel header dirs from the copied libc ones.
+# This seems to magically work.
+_KERNEL_DIRS="$( find . -type d | grep -v '^\.$' )"
+( cd "$COMPILE_TEST_INC" && rm -rf $_KERNEL_DIRS )
+
+# GCC headers
+set +u
+if [ "$GCC_INC"foobar == "foobar" ]; then
+ # Take from $CC default system include paths, filter out
+ # /usr/local/include and /usr/include stuff first, then try to match
+ # for gcc.
+ _TEMP="$( $CC -v -x c -E - < /dev/null 2>&1 | \
+ sed -n -e '/^#include <...> search starts here:$/,/^End of search list.$/{//!p}' | \
+ sed -e 's/^\ \//\//g' | \
+ egrep -v '/usr/local/include' | \
+ egrep -v '/usr/include' | \
+ grep gcc | \
+ xargs )"
+
+ # merge and prepare for use with $CC
+ for d in $_TEMP; do
+ # sanity test
+ if [ ! -d "$d" ]; then
+ echo "$d: is not a directory"
+ exit 1
+ fi
+ GCC_INC="$GCC_INC -I $d"
+ done
+fi
+set -u
+
+# For each header file, create a .c which includes the header file
+# and try to compile it using only current directory for searching other
+# included header files.
+_FAILED=0
+_PASSED=0
+for f in $( find . -name "*\.h" | xargs ); do
+ _FAIL=0
+ CFILE="$( echo "$( dirname "$f" )"/"$( basename "$f" .h )".c )"
+
+ # create .c file
+ echo "#include <"$( echo "$f" | sed -e 's|^.\/||' )">" \
+ > "$(dirname "$f")"/"$(basename "$f" .h)".c
+
+ # compile test, CC not quoted to support ccache
+ echo $CC -Wall -c -nostdinc $GCC_INC -I . -I "$COMPILE_TEST_INC" -I "$COMPILE_TEST_INC/$ARCH_TRIPLET" "$CFILE"
+ $CC -Wall -c -nostdinc $GCC_INC -I . -I "$COMPILE_TEST_INC" -I "$COMPILE_TEST_INC/$ARCH_TRIPLET" "$CFILE" \
+ || _FAIL=1
+
+ # report errors
+ if [ "$_FAIL" -gt 0 ]; then
+ echo "FAILED: $f"
+ _FAILED="$(( $_FAILED + 1 ))"
+ else
+ echo "PASSED: $f"
+ _PASSED="$(( $_PASSED + 1))"
+ fi
+done
+
+echo Statistics:
+echo "$_FAILED files failed the compile test."
+echo "$_PASSED files passed the compile test."
+
+exit "$_FAILED"
--
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/