check-includes.sh revision 6b3e017e
1#!/bin/sh -e
2#
3#   BSD LICENSE
4#
5#   Copyright 2016 6WIND S.A.
6#
7#   Redistribution and use in source and binary forms, with or without
8#   modification, are permitted provided that the following conditions
9#   are met:
10#
11#     * Redistributions of source code must retain the above copyright
12#       notice, this list of conditions and the following disclaimer.
13#     * Redistributions in binary form must reproduce the above copyright
14#       notice, this list of conditions and the following disclaimer in
15#       the documentation and/or other materials provided with the
16#       distribution.
17#     * Neither the name of 6WIND S.A. nor the names of its
18#       contributors may be used to endorse or promote products derived
19#       from this software without specific prior written permission.
20#
21#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33# This script checks that header files in a given directory do not miss
34# dependencies when included on their own, do not conflict and accept being
35# compiled with the strictest possible flags.
36#
37# Files are looked up in the directory provided as the first argument,
38# otherwise build/include by default.
39#
40# Recognized environment variables:
41#
42# VERBOSE=1 is the same as -v.
43#
44# QUIET=1 is the same as -q.
45#
46# SUMMARY=1 is the same as -s.
47#
48# CC, CPPFLAGS, CFLAGS, EXTRA_CPPFLAGS, EXTRA_CFLAGS, CXX, CXXFLAGS and
49# EXTRA_CXXFLAGS are taken into account.
50#
51# PEDANTIC_CFLAGS, PEDANTIC_CXXFLAGS and PEDANTIC_CPPFLAGS provide strict
52# C/C++ compilation flags.
53#
54# IGNORE contains a list of shell patterns matching files (relative to the
55# include directory) to avoid. It is set by default to known DPDK headers
56# which must not be included on their own.
57#
58# IGNORE_CXX provides additional files for C++.
59
60while getopts hqvs arg; do
61	case $arg in
62	h)
63		cat <<EOF
64usage: $0 [-h] [-q] [-v] [-s] [DIR]
65
66This script checks that header files in a given directory do not miss
67dependencies when included on their own, do not conflict and accept being
68compiled with the strictest possible flags.
69
70  -h    display this help and exit
71  -q    quiet mode, disable normal output
72  -v    show command lines being executed
73  -s    show summary
74
75With no DIR, default to build/include.
76
77Any failed header check yields a nonzero exit status.
78EOF
79		exit
80		;;
81	q)
82		QUIET=1
83		;;
84	v)
85		VERBOSE=1
86		;;
87	s)
88		SUMMARY=1
89		;;
90	*)
91		exit 1
92		;;
93	esac
94done
95
96shift $(($OPTIND - 1))
97
98include_dir=${1:-build/include}
99
100: ${PEDANTIC_CFLAGS=-std=c99 -pedantic -Wall -Wextra -Werror}
101: ${PEDANTIC_CXXFLAGS=}
102: ${PEDANTIC_CPPFLAGS=-D_XOPEN_SOURCE=600}
103: ${CC:=cc}
104: ${CXX:=c++}
105: ${IGNORE= \
106	'rte_atomic_32.h' \
107	'rte_atomic_64.h' \
108	'rte_byteorder_32.h' \
109	'rte_byteorder_64.h' \
110	'generic/*' \
111	'exec-env/*' \
112}
113: ${IGNORE_CXX= \
114	'rte_eth_vhost.h' \
115	'rte_virtio_net.h' \
116}
117
118temp_cc=/tmp/${0##*/}.$$.c
119pass_cc=
120failures_cc=0
121
122temp_cxx=/tmp/${0##*/}.$$.cc
123pass_cxx=
124failures_cxx=0
125
126# Process output parameters.
127
128[ "$QUIET" = 1 ] &&
129exec 1> /dev/null
130
131[ "$VERBOSE" = 1 ] &&
132output ()
133{
134	local CCV
135	local CXXV
136
137	shift
138	CCV=$CC
139	CXXV=$CXX
140	CC="echo $CC" CXX="echo $CXX" "$@"
141	CC=$CCV
142	CXX=$CXXV
143
144	"$@"
145} ||
146output ()
147{
148
149	printf '  %s\n' "$1"
150	shift
151	"$@"
152}
153
154trap 'rm -f "$temp_cc" "$temp_cxx"' EXIT
155
156compile_cc ()
157{
158	${CC} -I"$include_dir" \
159		${PEDANTIC_CPPFLAGS} ${CPPFLAGS} ${EXTRA_CPPFLAGS} \
160		${PEDANTIC_CFLAGS} ${CFLAGS} ${EXTRA_CFLAGS} \
161		-c -o /dev/null "${temp_cc}"
162}
163
164compile_cxx ()
165{
166	${CXX} -I"$include_dir" \
167		${PEDANTIC_CPPFLAGS} ${CPPFLAGS} ${EXTRA_CPPFLAGS} \
168		${PEDANTIC_CXXFLAGS} ${CXXFLAGS} ${EXTRA_CXXFLAGS} \
169		-c -o /dev/null "${temp_cxx}"
170}
171
172ignore ()
173{
174	file="$1"
175	shift
176	while [ $# -ne 0 ]; do
177		case "$file" in
178		$1)
179			return 0
180			;;
181		esac
182		shift
183	done
184	return 1
185}
186
187# Check C/C++ compilation for each header file.
188
189while read -r path
190do
191	file=${path#$include_dir}
192	file=${file##/}
193	if ignore "$file" $IGNORE; then
194		output "SKIP $file" :
195		continue
196	fi
197	if printf "\
198#include <%s>
199
200int main(void)
201{
202	return 0;
203}
204" "$file" > "$temp_cc" &&
205		output "CC $file" compile_cc
206	then
207		pass_cc="$pass_cc $file"
208	else
209		failures_cc=$(($failures_cc + 1))
210	fi
211	if ignore "$file" $IGNORE_CXX; then
212		output "SKIP CXX $file" :
213		continue
214	fi
215	if printf "\
216#include <%s>
217
218int main()
219{
220}
221" "$file" > "$temp_cxx" &&
222		output "CXX $file" compile_cxx
223	then
224		pass_cxx="$pass_cxx $file"
225	else
226		failures_cxx=$(($failures_cxx + 1))
227	fi
228done <<EOF
229$(find "$include_dir" -name '*.h')
230EOF
231
232# Check C compilation with all includes.
233
234: > "$temp_cc" &&
235for file in $pass_cc; do
236	printf "\
237#include <%s>
238" "$file" >> $temp_cc
239done
240if printf "\
241int main(void)
242{
243	return 0;
244}
245" >> "$temp_cc" &&
246	output "CC (all includes that did not fail)" compile_cc
247then
248	:
249else
250	failures_cc=$(($failures_cc + 1))
251fi
252
253# Check C++ compilation with all includes.
254
255: > "$temp_cxx" &&
256for file in $pass_cxx; do
257	printf "\
258#include <%s>
259" "$file" >> $temp_cxx
260done
261if printf "\
262int main()
263{
264}
265" >> "$temp_cxx" &&
266	output "CXX (all includes that did not fail)" compile_cxx
267then
268	:
269else
270	failures_cxx=$(($failures_cxx + 1))
271fi
272
273# Report results.
274
275if [ "$SUMMARY" = 1 ]; then
276	printf "\
277Summary:
278 %u failure(s) for C using '%s'.
279 %u failure(s) for C++ using '%s'.
280" $failures_cc "$CC" $failures_cxx "$CXX" 1>&2
281fi
282
283# Exit with nonzero status if there are failures.
284
285[ $failures_cc -eq 0 ] &&
286[ $failures_cxx -eq 0 ]
287