1#!/usr/bin/env bash 2# SPDX-License-Identifier: LGPL-2.0-or-later 3# SPDX-FileCopyrightText: 2021 Marco Trevisan <marco.trevisan@canonical.com> 4set -e 5 6if [ -n "$SELFTEST" ]; then 7 unset SELFTEST 8 set -x 9 self="$(realpath "$0")" 10 test_paths=() 11 trap 'rm -rf -- "${test_paths[@]}"' EXIT 12 13 test_env() { 14 local code_path="$(mktemp -t -d "check-pch-XXXXXX")" 15 test_paths+=("$code_path") 16 cd "$code_path" 17 mkdir gjs gi 18 echo "#include <stlib.h>" >> gjs/gjs_pch.hh 19 } 20 21 expect_success() { 22 "$self" || exit 1 23 } 24 expect_failure() { 25 "$self" && exit 1 || true 26 } 27 28 test_env 29 echo "#include <stlib.h>" >> gi/code.c 30 expect_success 31 32 test_env 33 echo "#include <stlib.h>" >> gi/code.c 34 echo "#include <stdio.h>" >> gi/code.c 35 expect_failure 36 37 test_env 38 echo "#include <stlib.h>" >> gi/code.c 39 echo "#include <invalid1.h>" >> gi/code1.cpp 40 echo "#include <invalid2.h>" >> gi/code1.c 41 expect_failure 42 43 test_env 44 echo "#include <stlib.h>" >> gi/code.c 45 echo "#include <invalid.h> // check-pch: ignore" >> gi/other-code.c 46 expect_success 47 48 test_env 49 echo "#include <stlib.h>" >> gi/code.c 50 echo "#include <invalid1.h> // NOcheck-pch: ignore" >> gi/code.c 51 echo "#include <invalid2.h> // check-pch: ignoreNO" >> gi/code.c 52 echo "#include <invalid3.h> // check-pch: ignore, yes" >> gi/other-code.c 53 expect_failure 54 55 test_env 56 echo "#include <invalid.h>" >> gjs/gjs_pch.hh 57 echo "#include <stlib.h>" >> gi/code.c 58 expect_failure 59 60 test_env 61 echo "#include <invalid.h> // check-pch: ignore, yes" >> gjs/gjs_pch.hh 62 echo "#include <stlib.h>" >> gi/code.c 63 expect_success 64 65 test_env 66 echo "#include <invalid.h>" >> gi/ignored-file.hh 67 echo "#include <stlib.h>" >> gi/code.c 68 expect_success 69 70 test_env 71 echo '# include <stlib.h>' >> gi/code.c 72 echo '# include "local/header.h"' >> gi/code.c 73 expect_success 74 75 test_env 76 echo "#include <stlib.h>" >> gi/code.c 77 echo '#include "local/header.h"' >> gjs/gjs_pch.hh 78 expect_failure 79 80 test_env 81 echo "# include <stlib.h>" >> gi/code.c 82 echo "# include <other/include.h>" >> gi/code.c 83 echo " # include <other/include.h>" >> gi/other-file.c 84 echo "# include <other/include.h>" >> gjs/gjs_pch.hh 85 expect_success 86 87 test_env 88 echo "# include <stlib.h>" >> gi/code.c 89 echo "# include <invalid.h>/*comment*/" >> gi/invalid-file.c 90 expect_failure 91 92 test_env 93 echo "# include <stlib.h>" >> gi/code.c 94 echo " # include <other/include.h>" >> gi/other-file.c 95 expect_failure 96 97 test_env 98 echo "#include <stlib.h>" >> gi/code.c 99 echo "//#include <invalid.h>" >> gi/invalid-file.c 100 echo "// #include <invalid.h>" >> gi/invalid-file.c 101 echo "//#include <invalid.h>" >> gjs/gjs_pch.hh 102 expect_success 103 104 test_env 105 echo "#include <stlib.h>" >> gi/code.c 106 echo "/*comment*/#include <invalid.h>/*comment*/" >> gi/invalid-file.c 107 # This is not supported: expect_failure 108 109 test_env 110 echo "#include <stlib.h>" >> gi/code.c 111 echo "# /*FIXME */ include /*Why should you do it?*/ <invalid.h>" >> gi/invalid-file.c 112 # This is not supported: expect_failure 113 114 exit 0 115fi 116 117PCH_FILES=(gjs/gjs_pch.hh) 118IGNORE_COMMENT="check-pch: ignore" 119 120CODE_PATHS=(gjs gi) 121INCLUDED_FILES=( 122 \*.c 123 \*.cpp 124 \*.h 125) 126 127grep_include_lines() { 128 grep -h '^\s*#\s*include\s*[<"][^>"]\+[>"]' "$@" | uniq 129} 130 131grep_header_file() { 132 local header_file="${1//./\\.}" 133 shift 134 grep -qs "^\s*#\s*include\s*[<\"]${header_file}[>\"]" "$@" 135} 136 137# List all the included headers 138mapfile -t includes < <(grep_include_lines \ 139 -r \ 140 $(printf -- "--include %s\n" "${INCLUDED_FILES[@]}") \ 141 "${CODE_PATHS[@]}" \ 142 | grep -vw "$IGNORE_COMMENT") 143 144missing=() 145for h in "${includes[@]}"; do 146 if [[ "$h" =~ \#[[:space:]]*include[[:space:]]*\<([^\>]+)\> ]]; then 147 header_file="${BASH_REMATCH[1]}" 148 if ! grep_header_file "$header_file" "${PCH_FILES[@]}"; then 149 echo "Header <$header_file> not added to PCH file" 150 missing+=("$header_file") 151 fi 152 fi 153done 154 155if [ "${#missing[@]}" -gt 0 ]; then 156 echo 157 echo "Headers not added to the PCH file found, please add to ${PCH_FILES[*]}" 158 echo "Otherwise you can ignore them with a leading comment such as" 159 echo " #include <${missing[0]}> // $IGNORE_COMMENT" 160 exit 1 161fi 162 163# And now, the other way around... 164mapfile -t pch_includes < <(grep_include_lines \ 165 "${PCH_FILES[@]}" \ 166 | grep -vw "$IGNORE_COMMENT") 167 168unneeded=() 169for h in "${pch_includes[@]}"; do 170 if [[ "$h" =~ \#[[:space:]]*include[[:space:]]*[\<\"]([^\>\"]+)[\>\"] ]]; then 171 header_file="${BASH_REMATCH[1]}" 172 if ! grep_header_file "$header_file" -r \ 173 $(printf -- "--include %s\n" "${INCLUDED_FILES[@]}") \ 174 "${CODE_PATHS[@]}"; then 175 echo "Header <$header_file> included in one PCH is not used in code" 176 unneeded+=("$header_file") 177 fi 178 fi 179done 180 181if [ "${#unneeded[@]}" -gt 0 ]; then 182 echo 183 echo "Unneeded headers added to the PCH file found, remove from ${PCH_FILES[*]}" 184 echo "Otherwise you can ignore them with a leading comment such as" 185 echo " #include <${unneeded[0]}> // $IGNORE_COMMENT" 186 exit 1 187fi 188