[gjs: 3/6] test: Add a CI job that ensures that PCH file is populated with all headers




commit 67a9563a133eb2bafccfacd1a96f18f49c8de03c
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Thu May 13 14:00:54 2021 +0200

    test: Add a CI job that ensures that PCH file is populated with all headers
    
    ...And the other way around, so that we won't include more than needed
    both in code and pch headers
    
    As per this we need to install actual bash and grep on alpine as this
    script needs those features (which are not provided by buysbox).

 .gitlab-ci.yml    |  29 ++++++++-
 test/check-pch.sh | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 214 insertions(+), 2 deletions(-)
---
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5a9c2308..e1788ee3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -14,7 +14,7 @@ stages:
 
 .gjs-alpine:
   variables:
-    FDO_DISTRIBUTION_TAG: '2021-04-02.0'
+    FDO_DISTRIBUTION_TAG: '2021-05-13.0'
     FDO_UPSTREAM_REPO: GNOME/gjs
 
 build-alpine-image:
@@ -23,7 +23,7 @@ build-alpine-image:
     - .gjs-alpine
   stage: prepare
   variables:
-    FDO_DISTRIBUTION_PACKAGES: cppcheck git python3 yarn
+    FDO_DISTRIBUTION_PACKAGES: cppcheck git python3 yarn bash grep
     FDO_DISTRIBUTION_EXEC: |
       python3 -m ensurepip &&
       rm -r /usr/lib/python*/ensurepip &&
@@ -199,6 +199,7 @@ cppcheck:
       - '**/*.c'
       - '**/*.cpp'
       - '**/*.h'
+      - '**/*.hh'
 
 cpplint:
   when: on_success
@@ -221,6 +222,7 @@ cpplint:
       - '**/*.c'
       - '**/*.cpp'
       - '**/*.h'
+      - '**/*.hh'
 
 eslint:
   when: on_success
@@ -243,6 +245,29 @@ eslint:
       - .eslintrc.yml
       - '**/.eslintrc.yml'
 
+pch_check:
+  when: on_success
+  stage: source_check
+  extends:
+    - .fdo.distribution-image@alpine
+    - .gjs-alpine
+  script:
+    - env SELFTEST=1 test/check-pch.sh
+    - test/check-pch.sh
+  except:
+    refs:
+      - schedules
+      - tags
+    variables:
+      - $CI_COMMIT_MESSAGE =~ /\[skip pch_check\]/
+  only:
+    changes:
+      - test/check-pch.sh
+      - '**/*.c'
+      - '**/*.cpp'
+      - '**/*.h'
+      - '**/*.hh'
+
 iwyu:
   when: on_success
   stage: source_check
diff --git a/test/check-pch.sh b/test/check-pch.sh
new file mode 100755
index 00000000..fd27b10f
--- /dev/null
+++ b/test/check-pch.sh
@@ -0,0 +1,187 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.0-or-later
+# SPDX-FileCopyrightText: 2021 Marco Trevisan <marco trevisan canonical com>
+set -e
+
+if [ -n "$SELFTEST" ]; then
+    unset SELFTEST
+    set -x
+    self="$(realpath "$0")"
+    test_paths=()
+    trap 'rm -rf -- "${test_paths[@]}"' EXIT
+
+    test_env() {
+        local code_path="$(mktemp -t -d "check-pch-XXXXXX")"
+        test_paths+=("$code_path")
+        cd "$code_path"
+        mkdir gjs gi
+        echo "#include <stlib.h>" >> gjs/gjs_pch.hh
+    }
+
+    expect_success() {
+        "$self" || exit 1
+    }
+    expect_failure() {
+        "$self" && exit 1 || true
+    }
+
+    test_env
+    echo "#include <stlib.h>" >> gi/code.c
+    expect_success
+
+    test_env
+    echo "#include <stlib.h>" >> gi/code.c
+    echo "#include <stdio.h>" >> gi/code.c
+    expect_failure
+
+    test_env
+    echo "#include <stlib.h>" >> gi/code.c
+    echo "#include <invalid1.h>" >> gi/code1.cpp
+    echo "#include <invalid2.h>" >> gi/code1.c
+    expect_failure
+
+    test_env
+    echo "#include <stlib.h>" >> gi/code.c
+    echo "#include <invalid.h> // check-pch: ignore" >> gi/other-code.c
+    expect_success
+
+    test_env
+    echo "#include <stlib.h>" >> gi/code.c
+    echo "#include <invalid1.h> // NOcheck-pch: ignore" >> gi/code.c
+    echo "#include <invalid2.h> // check-pch: ignoreNO" >> gi/code.c
+    echo "#include <invalid3.h> // check-pch: ignore, yes" >> gi/other-code.c
+    expect_failure
+
+    test_env
+    echo "#include <invalid.h>" >> gjs/gjs_pch.hh
+    echo "#include <stlib.h>" >> gi/code.c
+    expect_failure
+
+    test_env
+    echo "#include <invalid.h> // check-pch: ignore, yes" >> gjs/gjs_pch.hh
+    echo "#include <stlib.h>" >> gi/code.c
+    expect_success
+
+    test_env
+    echo "#include <invalid.h>" >> gi/ignored-file.hh
+    echo "#include <stlib.h>" >> gi/code.c
+    expect_success
+
+    test_env
+    echo '#              include                 <stlib.h>' >> gi/code.c
+    echo '#              include                 "local/header.h"' >> gi/code.c
+    expect_success
+
+    test_env
+    echo "#include <stlib.h>" >> gi/code.c
+    echo '#include "local/header.h"' >> gjs/gjs_pch.hh
+    expect_failure
+
+    test_env
+    echo "#              include                 <stlib.h>" >> gi/code.c
+    echo "#              include                 <other/include.h>" >> gi/code.c
+    echo "        #              include                 <other/include.h>" >> gi/other-file.c
+    echo "# include <other/include.h>" >> gjs/gjs_pch.hh
+    expect_success
+
+    test_env
+    echo "#    include    <stlib.h>" >> gi/code.c
+    echo "#      include                    <invalid.h>/*comment*/" >> gi/invalid-file.c
+    expect_failure
+
+    test_env
+    echo "#    include    <stlib.h>" >> gi/code.c
+    echo "        #              include                 <other/include.h>" >> gi/other-file.c
+    expect_failure
+
+    test_env
+    echo "#include <stlib.h>" >> gi/code.c
+    echo "//#include <invalid.h>" >> gi/invalid-file.c
+    echo "// #include <invalid.h>" >> gi/invalid-file.c
+    echo "//#include <invalid.h>" >> gjs/gjs_pch.hh
+    expect_success
+
+    test_env
+    echo "#include <stlib.h>" >> gi/code.c
+    echo "/*comment*/#include <invalid.h>/*comment*/" >> gi/invalid-file.c
+    # This is not supported: expect_failure
+
+    test_env
+    echo "#include <stlib.h>" >> gi/code.c
+    echo "#   /*FIXME */  include  /*Why should you do it?*/  <invalid.h>" >> gi/invalid-file.c
+    # This is not supported: expect_failure
+
+    exit 0
+fi
+
+PCH_FILES=(gjs/gjs_pch.hh)
+IGNORE_COMMENT="check-pch: ignore"
+
+CODE_PATHS=(gjs gi)
+INCLUDED_FILES=(
+    \*.c
+    \*.cpp
+    \*.h
+)
+
+grep_include_lines() {
+    grep -h '^\s*#\s*include\s*[<"][^>"]\+[>"]' "$@" | uniq
+}
+
+grep_header_file() {
+    local header_file="${1//./\\.}"
+    shift
+    grep -qs "^\s*#\s*include\s*[<\"]${header_file}[>\"]" "$@"
+}
+
+# List all the included headers
+mapfile -t includes < <(grep_include_lines \
+    -r \
+    $(printf -- "--include %s\n" "${INCLUDED_FILES[@]}") \
+    "${CODE_PATHS[@]}" \
+    | grep -vw "$IGNORE_COMMENT")
+
+missing=()
+for h in "${includes[@]}"; do
+    if [[ "$h" =~ \#[[:space:]]*include[[:space:]]*\<([^\>]+)\> ]]; then
+        header_file="${BASH_REMATCH[1]}"
+        if ! grep_header_file "$header_file" "${PCH_FILES[@]}"; then
+            echo "Header <$header_file> not added to PCH file"
+            missing+=("$header_file")
+        fi
+    fi
+done
+
+if [ "${#missing[@]}" -gt 0 ]; then
+    echo
+    echo "Headers not added to the PCH file found, please add to ${PCH_FILES[*]}"
+    echo "Otherwise you can ignore them with a leading comment such as"
+    echo "  #include <${missing[0]}>  // $IGNORE_COMMENT"
+    exit 1
+fi
+
+# And now, the other way around...
+mapfile -t pch_includes < <(grep_include_lines \
+    "${PCH_FILES[@]}" \
+    | grep -vw "$IGNORE_COMMENT")
+
+unneeded=()
+for h in "${pch_includes[@]}"; do
+    if [[ "$h" =~ \#[[:space:]]*include[[:space:]]*[\<\"]([^\>\"]+)[\>\"] ]]; then
+        header_file="${BASH_REMATCH[1]}"
+        if ! grep_header_file "$header_file" -r \
+            $(printf -- "--include %s\n" "${INCLUDED_FILES[@]}") \
+            "${CODE_PATHS[@]}"; then
+            echo "Header <$header_file> included in one PCH is not used in code"
+            unneeded+=("$header_file")
+        fi
+    fi
+done
+
+if [ "${#unneeded[@]}" -gt 0 ]; then
+    echo
+    echo "Unneeded headers added to the PCH file found, remove from ${PCH_FILES[*]}"
+    echo "Otherwise you can ignore them with a leading comment such as"
+    echo "  #include <${unneeded[0]}>  // $IGNORE_COMMENT"
+    exit 1
+fi


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]