1#!/bin/sh -e 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright 2016 6WIND S.A. 4 5# This script checks that header files in a given directory do not miss 6# dependencies when included on their own, do not conflict and accept being 7# compiled with the strictest possible flags. 8# 9# Files are looked up in the directory provided as the first argument, 10# otherwise build/include by default. 11# 12# Recognized environment variables: 13# 14# VERBOSE=1 is the same as -v. 15# 16# QUIET=1 is the same as -q. 17# 18# SUMMARY=1 is the same as -s. 19# 20# CC, CPPFLAGS, CFLAGS, CXX, CXXFLAGS are taken into account. 21# 22# PEDANTIC_CFLAGS, PEDANTIC_CXXFLAGS and PEDANTIC_CPPFLAGS provide strict 23# C/C++ compilation flags. 24# 25# IGNORE contains a list of globbing patterns matching files (relative to the 26# include directory) to avoid. It is set by default to known DPDK headers 27# which must not be included on their own. 28# 29# IGNORE_CXX provides additional files for C++. 30 31while getopts hqvs arg; do 32 case $arg in 33 h) 34 cat <<EOF 35usage: $0 [-h] [-q] [-v] [-s] [DIR] 36 37This script checks that header files in a given directory do not miss 38dependencies when included on their own, do not conflict and accept being 39compiled with the strictest possible flags. 40 41 -h display this help and exit 42 -q quiet mode, disable normal output 43 -v show command lines being executed 44 -s show summary 45 46With no DIR, default to build/include. 47 48Any failed header check yields a nonzero exit status. 49EOF 50 exit 51 ;; 52 q) 53 QUIET=1 54 ;; 55 v) 56 VERBOSE=1 57 ;; 58 s) 59 SUMMARY=1 60 ;; 61 *) 62 exit 1 63 ;; 64 esac 65done 66 67shift $(($OPTIND - 1)) 68 69include_dir=${1:-build/include} 70 71: ${PEDANTIC_CFLAGS=-std=c99 -pedantic -Wall -Wextra -Werror} 72: ${PEDANTIC_CXXFLAGS=} 73: ${PEDANTIC_CPPFLAGS=-D_XOPEN_SOURCE=600} 74: ${CC:=cc} 75: ${CXX:=c++} 76: ${IGNORE= \ 77 'rte_atomic_32.h' \ 78 'rte_atomic_64.h' \ 79 'rte_byteorder_32.h' \ 80 'rte_byteorder_64.h' \ 81 'generic/*' \ 82 'rte_vhost.h' \ 83 'rte_eth_vhost.h' \ 84 'rte_eal_interrupts.h' \ 85} 86: ${IGNORE_CXX= \ 87 'rte_vhost.h' \ 88 'rte_eth_vhost.h' \ 89} 90 91temp_cc=$(mktemp -t dpdk.${0##*/}.XXX.c) 92pass_cc= 93failures_cc=0 94 95temp_cxx=$(mktemp -t dpdk.${0##*/}.XXX.cc) 96pass_cxx= 97failures_cxx=0 98 99# Process output parameters. 100 101[ "$QUIET" = 1 ] && 102exec 1> /dev/null 103 104[ "$VERBOSE" = 1 ] && 105output () 106{ 107 local CCV 108 local CXXV 109 110 shift 111 CCV=$CC 112 CXXV=$CXX 113 CC="echo $CC" CXX="echo $CXX" "$@" 114 CC=$CCV 115 CXX=$CXXV 116 117 "$@" 118} || 119output () 120{ 121 122 printf ' %s\n' "$1" 123 shift 124 "$@" 125} 126 127trap 'rm -f "$temp_cc" "$temp_cxx"' EXIT 128 129compile_cc () 130{ 131 ${CC} -I"$include_dir" \ 132 ${PEDANTIC_CPPFLAGS} ${CPPFLAGS} \ 133 ${PEDANTIC_CFLAGS} ${CFLAGS} \ 134 -c -o /dev/null "${temp_cc}" 135} 136 137compile_cxx () 138{ 139 ${CXX} -I"$include_dir" \ 140 ${PEDANTIC_CPPFLAGS} ${CPPFLAGS} \ 141 ${PEDANTIC_CXXFLAGS} ${CXXFLAGS} \ 142 -c -o /dev/null "${temp_cxx}" 143} 144 145ignore () 146{ 147 file="$1" 148 shift 149 while [ $# -ne 0 ]; do 150 case "$file" in 151 $1) 152 return 0 153 ;; 154 esac 155 shift 156 done 157 return 1 158} 159 160# Check C/C++ compilation for each header file. 161 162while read -r path 163do 164 file=${path#$include_dir} 165 file=${file##/} 166 if ignore "$file" $IGNORE; then 167 output "SKIP $file" : 168 continue 169 fi 170 if printf "\ 171#include <%s> 172 173int main(void) 174{ 175 return 0; 176} 177" "$file" > "$temp_cc" && 178 output "CC $file" compile_cc 179 then 180 pass_cc="$pass_cc $file" 181 else 182 failures_cc=$(($failures_cc + 1)) 183 fi 184 if ignore "$file" $IGNORE_CXX; then 185 output "SKIP CXX $file" : 186 continue 187 fi 188 if printf "\ 189#include <%s> 190 191int main() 192{ 193} 194" "$file" > "$temp_cxx" && 195 output "CXX $file" compile_cxx 196 then 197 pass_cxx="$pass_cxx $file" 198 else 199 failures_cxx=$(($failures_cxx + 1)) 200 fi 201done <<EOF 202$(find "$include_dir" -name '*.h') 203EOF 204 205# Check C compilation with all includes. 206 207: > "$temp_cc" && 208for file in $pass_cc; do 209 printf "\ 210#include <%s> 211" "$file" >> $temp_cc 212done 213if printf "\ 214int main(void) 215{ 216 return 0; 217} 218" >> "$temp_cc" && 219 output "CC (all includes that did not fail)" compile_cc 220then 221 : 222else 223 failures_cc=$(($failures_cc + 1)) 224fi 225 226# Check C++ compilation with all includes. 227 228: > "$temp_cxx" && 229for file in $pass_cxx; do 230 printf "\ 231#include <%s> 232" "$file" >> $temp_cxx 233done 234if printf "\ 235int main() 236{ 237} 238" >> "$temp_cxx" && 239 output "CXX (all includes that did not fail)" compile_cxx 240then 241 : 242else 243 failures_cxx=$(($failures_cxx + 1)) 244fi 245 246# Report results. 247 248if [ "$SUMMARY" = 1 ]; then 249 printf "\ 250Summary: 251 %u failure(s) for C using '%s'. 252 %u failure(s) for C++ using '%s'. 253" $failures_cc "$CC" $failures_cxx "$CXX" 1>&2 254fi 255 256# Exit with nonzero status if there are failures. 257 258[ $failures_cc -eq 0 ] && 259[ $failures_cxx -eq 0 ] 260