]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - scripts/source-maintenance.sh
Source Format Enforcement (#763)
[thirdparty/squid.git] / scripts / source-maintenance.sh
index fd30e2dd82c141f3e8f67a9510ddd1471057ebff..1c5addc692f531619d81eb7014a6142803471388 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-## Copyright (C) 1996-2014 The Squid Software Foundation and contributors
+## Copyright (C) 1996-2021 The Squid Software Foundation and contributors
 ##
 ## Squid software is distributed under GPLv2+ license and includes
 ## contributions from numerous individuals and organizations.
 # If code alteration takes place the process is halted for manual intervention.
 #
 
+# whether to continue execution after a failure
+# TODO: Expand the subset of failures covered by this feature; see run_().
+KeepGoing="no"
+# the actual name of the directive that enabled keep-going mode
+KeepGoingDirective=""
+#
+# The script checks that the version of astyle is TargetAstyleVersion.
+# if it isn't, the default behaviour is to not perform the formatting stage
+# in order to avoid unexpected massive changes if the behaviour of astyle
+# has changed in different releases.
+# if --with-astyle /path/to/astyle is used, the check is still performed
+# and a warning is printed, but the sources are reformatted
+TargetAstyleVersion="2.04"
+ASTYLE='astyle'
+
+# whether to check and, if necessary, update boilerplate copyright years
+CheckAndUpdateCopyright=yes
+
+printUsage () {
+    echo "Usage: $0 [option...]"
+    echo "options:"
+    echo "    --keep-going|-k"
+    echo "    --check-and-update-copyright <yes|no>"
+    echo "    --with-astyle </path/to/astyle/executable>"
+}
+
+# command-line options
+while [ $# -ge 1 ]; do
+    case "$1" in
+    --keep-going|-k)
+        KeepGoing=yes
+        KeepGoingDirective=$1
+        shift
+        ;;
+    --check-and-update-copyright)
+        if test "x$2" != xyes -a "x$2" != xno
+        then
+            printUsage
+            echo "Error: Option $1 expects a yes or no argument but got $2"
+            exit 1;
+        fi
+        CheckAndUpdateCopyright=$2
+        shift 2
+        ;;
+    --help|-h)
+        printUsage
+        exit 0;
+        ;;
+    --with-astyle)
+        ASTYLE=$2
+        export ASTYLE
+        shift 2
+        ;;
+    *)
+        printUsage
+        echo "Unsupported command-line option: $1"
+        exit 1;
+        ;;
+    esac
+done
+
+# an error code seen by a KeepGoing-aware command (or zero)
+SeenErrors=0
+
+
+if ! git diff --quiet; then
+       echo "There are unstaged changes. This script may modify sources."
+       echo "Stage changes to avoid permanent losses when things go bad."
+       exit 1
+fi
+
 # On squid-cache.org we have to use the python scripted md5sum
 HOST=`hostname`
 if test "$HOST" = "squid-cache.org" ; then
@@ -29,34 +100,128 @@ else
        MD5="md5sum"
 fi
 
-ROOT=`bzr root`
-
-ASVER=`astyle --version 2>&1 | grep -o -E "[0-9.]+"`
-if test "${ASVER}" != "2.03" ; then
-       echo "Astyle version problem. You have ${ASVER} instead of 2.03"
-       ASVER=""
+${ASTYLE} --version >/dev/null 2>/dev/null
+result=$?
+if test $result -gt 0 ; then
+       echo "ERROR: cannot run ${ASTYLE}"
+       exit 1
+fi
+ASVER=`${ASTYLE} --version 2>&1 | grep -o -E "[0-9.]+"`
+if test "${ASVER}" != "${TargetAstyleVersion}" ; then
+       if test "${ASTYLE}" = "astyle" ; then
+               echo "Astyle version problem. You have ${ASVER} instead of ${TargetAstyleVersion}"
+               echo "Formatting step skipped due to version mismatch"
+               ASVER=""
+       else
+               echo "WARNING: ${ASTYLE} is version ${ASVER} instead of ${TargetAstyleVersion}"
+               echo "Formatting anyway, please double check output before submitting"
+       fi
 else
        echo "Found astyle ${ASVER}. Formatting..."
 fi
 
-srcformat ()
+if test $CheckAndUpdateCopyright = yes
+then
+    COPYRIGHT_YEARS=`date +"1996-%Y"`
+    echo "s/1996-2[0-9]+ The Squid Software Foundation and contributors/${COPYRIGHT_YEARS} The Squid Software Foundation and contributors/g" >> boilerplate_fix.sed
+fi
+
+# executes the specified command
+# in KeepGoing mode, remembers errors and hides them from callers
+run_ ()
+{
+        "$@" && return; # return on success
+        error=$?
+
+        if test $KeepGoing = no; then
+                return $error
+        fi
+
+        echo "ERROR: Continuing after a failure ($error) due to $KeepGoingDirective"
+        SeenErrors=$error # TODO: Remember the _first_ error instead
+        return 0 # hide error from the caller
+}
+
+updateIfChanged ()
 {
-PWD=`pwd`
-#echo "FORMAT: ${PWD}..."
+       original="$1"
+       updated="$2"
+       message="$3"
+
+       if ! cmp -s "${original}" "${updated}"; then
+               echo "NOTICE: File ${original} changed: ${message}"
+               run_ mv "${updated}" "${original}" || return
+       else
+               run_ rm -f "${updated}" || exit $?
+       fi
+}
 
+# uses the given script to update the given source file
+applyPlugin ()
+{
+        script="$1"
+        source="$2"
+
+        new="$source.new"
+        $script "$source" > "$new" &&
+                updateIfChanged "$source" "$new" "by $script"
+}
+
+# updates the given source file using the given script(s)
+applyPluginsTo ()
+{
+        source="$1"
+        shift
+
+        for script in `git ls-files "$@"`; do
+                run_ applyPlugin $script $source || return
+        done
+}
+
+# succeeds if all MakeNamedErrorDetail() names are unique
+checkMakeNamedErrorDetails ()
+{
+    problems=1 # assume there are problems until proven otherwise
+
+    options='-h --only-matching --extended-regexp'
+    git grep $options 'MakeNamedErrorDetail[(]".*?"[)]' src |
+        sort |
+        uniq --count > \
+        MakeNamedErrorDetail.tmp
+
+    if grep --quiet --word-regexp 1 MakeNamedErrorDetail.tmp; then
+        if grep --invert-match --word-regexp 1 MakeNamedErrorDetail.tmp; then
+            echo "ERROR: Duplicated MakeNamedErrorDetail names (see above)."
+        else
+            problems=0
+        fi
+    else
+        echo "ERROR: Cannot find or process MakeNamedErrorDetail calls."
+    fi
+
+    rm MakeNamedErrorDetail.tmp # ignore (unexpected) cleanup failures
+    return $problems
+}
+
+srcFormat ()
+{
 #
 # Scan for incorrect use of #ifdef/#ifndef
 #
-bzr grep --no-recursive "ifn?def .*_SQUID_" |
+git grep "ifn?def .*_SQUID_" |
     grep -v -E "_H$" |
-    while read f; do echo "PROBLEM?: ${PWD} ${f}"; done
+    grep -v "scripts/source-maintenance.sh" |
+    while read f; do echo "PROBLEM?: ${f}"; done
 
 #
 # Scan for file-specific actions
 #
-for FILENAME in `bzr ls --versioned`; do
+for FILENAME in `git ls-files`; do
     skip_copyright_check=""
 
+    # skip subdirectories, git ls-files is recursive
+    test -d $FILENAME && continue
+
     case ${FILENAME} in
 
     *.h|*.c|*.cc|*.cci)
@@ -64,59 +229,63 @@ for FILENAME in `bzr ls --versioned`; do
        #
        # Code Style formatting maintenance
        #
-        if test "${ASVER}"; then
-               ${ROOT}/scripts/formater.pl ${FILENAME}
+       applyPluginsTo ${FILENAME} scripts/maintenance/ || return
+       if test "${ASVER}"; then
+               ./scripts/formater.pl ${FILENAME}
                if test -e $FILENAME -a -e "$FILENAME.astylebak"; then
                        md51=`cat  $FILENAME| tr -d "\n \t\r" | $MD5`;
                        md52=`cat  $FILENAME.astylebak| tr -d "\n \t\r" | $MD5`;
 
                        if test "$md51" != "$md52"; then
-                               echo "ERROR: File $PWD/$FILENAME not formating well";
+                               echo "ERROR: File $FILENAME not formatting well";
                                mv $FILENAME $FILENAME.astylebad
                                mv $FILENAME.astylebak $FILENAME
-                               bzr revert ${FILENAME}
+                               git checkout -- ${FILENAME}
                        else
                                rm -f $FILENAME.astylebak
                        fi
                fi
        fi
 
-       ${ROOT}/scripts/sort-includes.pl ${FILENAME} >${FILENAME}.sorted
-       if test -e ${FILENAME} -a -e "${FILENAME}.sorted"; then
-               md51=`cat  ${FILENAME}| tr -d "\n \t\r" | $MD5`;
-               md52=`cat  ${FILENAME}.sorted| tr -d "\n \t\r" | $MD5`;
-
-               if test "$md51" != "$md52" ; then
-                       echo "NOTICE: File $PWD/${FILENAME} changed #include order"
-               fi
-               mv ${FILENAME}.sorted ${FILENAME}
-       fi
-
        #
        # REQUIRE squid.h first #include
        #
        case ${FILENAME} in
+       src/cf_gen.cc)
+               # ignore, this is a build tool.
+               ;;
        *.c|*.cc)
                FI=`grep "#include" ${FILENAME} | head -1`;
                if test "${FI}" != "#include \"squid.h\"" -a "${FILENAME}" != "cf_gen.cc"; then
-                       echo "ERROR: ${PWD}/${FILENAME} does not include squid.h first!"
+                       echo "ERROR: ${FILENAME} does not include squid.h first!"
                fi
                ;;
        *.h|*.cci)
                FI=`grep "#include \"squid.h\"" ${FILENAME}`;
                if test "x${FI}" != "x" ; then
-                       echo "ERROR: ${PWD}/${FILENAME} duplicate include of squid.h"
+                       echo "ERROR: ${FILENAME} duplicate include of squid.h"
                fi
                ;;
        esac
 
+       #
+       # If a file includes openssl headers, then it must include compat/openssl.h
+       #
+       if test "${FILENAME}" != "compat/openssl.h"; then
+               FA=`grep "#include.*openssl/" "${FILENAME}" 2>/dev/null | head -1`;
+               FB=`grep '#include.*compat/openssl[.]h' "${FILENAME}" 2>/dev/null | head -1`;
+               if test "x${FA}" != "x" -a "x${FB}" = "x"; then
+                       echo "ERROR: ${FILENAME} includes openssl headers without including \"compat/openssl.h\""
+               fi
+       fi
+
        #
        # forward.h means different things to Squid code depending on the path
        # require the full path is explicit for every include
        #
        FI=`grep "#include \"forward.h\"" ${FILENAME}`;
        if test "x${FI}" != "x" ; then
-               echo "ERROR: ${PWD}/${FILENAME} contains reference to forward.h without path"
+               echo "ERROR: ${FILENAME} contains reference to forward.h without path"
        fi
 
        #
@@ -125,18 +294,18 @@ for FILENAME in `bzr ls --versioned`; do
        # sprintf() - not allowed anywhere.
        #
        STRDUP=`grep -e "[^x]strdup(" ${FILENAME}`;
-       if test "x${STRDUP}" != "x" -a "${FILENAME}" != "xstring.h"; then
-               echo "ERROR: ${PWD}/${FILENAME} contains unprotected use of strdup()"
+       if test "x${STRDUP}" != "x" -a "${FILENAME}" != "compat/xstring.h"; then
+               echo "ERROR: ${FILENAME} contains unprotected use of strdup()"
        fi
        SPRINTF=`grep -e "[^v]sprintf(" ${FILENAME}`;
        if test "x${SPRINTF}" != "x" ; then
-               echo "ERROR: ${PWD}/${FILENAME} contains unsafe use of sprintf()"
+               echo "ERROR: ${FILENAME} contains unsafe use of sprintf()"
        fi
 
        #
        # DEBUG Section list maintenance
        #
-       grep " DEBUG: section" <${FILENAME} | sed -e 's/ \* DEBUG: //' -e 's%/\* DEBUG: %%' -e 's% \*/%%' | sort -u >>${ROOT}/doc/debug-sections.tmp
+       grep " DEBUG: section" <${FILENAME} | sed -e 's/ \* DEBUG: //' -e 's%/\* DEBUG: %%' -e 's% \*/%%' | sort -u >>doc/debug-sections.tmp
 
        #
        # File permissions maintenance.
@@ -151,40 +320,39 @@ for FILENAME in `bzr ls --versioned`; do
        chmod 755 ${FILENAME}
        ;;
 
-    Makefile.am)
-
-       perl -p -e 's/@([A-Z0-9_]+)@/\$($1)/g' <${FILENAME} >${FILENAME}.styled
-       mv ${FILENAME}.styled ${FILENAME}
+    *.am)
+               applyPluginsTo ${FILENAME} scripts/format-makefile-am.pl || return
        ;;
 
-    */ChangeLog|*.list|*.png|*.po|*.pot|*.txt|*test-suite/squidconf/empty)
+    ChangeLog|CREDITS|CONTRIBUTORS|COPYING|*.png|*.po|*.pot|rfcs/|*.txt|test-suite/squidconf/empty|.bzrignore)
         # we do not enforce copyright blurbs in:
         #
+        #  Squid Project contributor attribution file
+        #  third-party copyright attribution file
         #  images,
         #  translation PO/POT
-        #  auto-generated .list files,
+        #  license documentation files
         #  (imported) plain-text documentation files and ChangeLogs
+        #  VCS internal files
         #
         skip_copyright_check=1
         ;;
     esac
 
     # check for Foundation copyright blurb
-    if test -f ${PWD}/${FILENAME} -a "x$skip_copyright_check" = "x"; then
-        BLURB=`grep "The Squid Software Foundation and contributors" ${FILENAME}`;
+    if test $CheckAndUpdateCopyright = yes -a -f ${FILENAME} -a "x$skip_copyright_check" = "x"; then
+        BLURB=`grep -o "${COPYRIGHT_YEARS} The Squid Software Foundation and contributors" ${FILENAME}`;
         if test "x${BLURB}" = "x"; then
-            echo "CHECK COPYRIGHT for ${PWD}/${FILENAME}"
+            BOILER=`grep -o -E "1996-2[0-9]+ The Squid Software Foundation and contributors" ${FILENAME}`;
+            if test "x${BOILER}" != "x"; then
+                echo "UPDATE COPYRIGHT for ${FILENAME}"
+                sed --in-place -r -f boilerplate_fix.sed ${FILENAME}
+            else
+                echo "CHECK COPYRIGHT for ${FILENAME}"
+            fi
         fi
     fi
 
-    if test "$FILENAME" = "libltdl/" ; then
-        :
-    elif test -d $FILENAME ; then
-       cd $FILENAME
-       srcformat ${ROOT} || exit 1
-       cd ..
-    fi
-
 done
 }
 
@@ -196,77 +364,53 @@ echo "#define _PROFILER_XPROF_TYPE_H_"
 echo "/* AUTO-GENERATED FILE */"
 echo "#if USE_XPROF_STATS"
 echo "typedef enum {"
-echo "  XPROF_PROF_UNACCOUNTED,"
-grep -R -h "PROF_start.*" ./* | grep -v probename | sed -e 's/ //g; s/PROF_start(/XPROF_/; s/);/,/' | sort -u
-echo "  XPROF_LAST } xprof_type;"
+echo "    XPROF_PROF_UNACCOUNTED,"
+grep -R -h "PROF_start.*" ./* | grep -v probename | sed -e 's/ //g; s/PROF_start(/    XPROF_/; s/);/,/;' | sort -u
+echo "    XPROF_LAST"
+echo "} xprof_type;"
 echo "#endif"
 echo "#endif"
-) >${ROOT}/lib/profiler/list
-mv ${ROOT}/lib/profiler/list ${ROOT}/lib/profiler/xprof_type.h
+echo ""
+) >lib/profiler/list
+mv lib/profiler/list lib/profiler/xprof_type.h
+
+printAmFile ()
+{
+    sed -e 's%\ \*%##%; s%/\*%##%; s%##/%##%' < scripts/boilerplate.h
+    echo -n "$1 ="
+    git ls-files $2$3 | sed -e s%$2%%g | sort -u | while read f; do
+        echo " \\"
+        echo -n "    ${f}"
+    done
+    echo ""
+}
 
 # Build icons install include from current icons available
-(
-sed -e 's%\ \*%##%' -e 's%/\*%##%' -e 's%##/%##%' <scripts/boilerplate.h
-echo -n "ICONS="
-for f in `ls -1 ${ROOT}/icons/silk/* | sort -u`
-do
-       echo " \\"
-       echo -n "    ${f}"
-done
-echo " "
-)| sed s%${ROOT}/icons/%%g >${ROOT}/icons/icon.list
+printAmFile ICONS "icons/" "silk/*" > icons/icon.am
 
 # Build templates install include from current templates available
-(
-sed -e 's%\ \*%##%' -e 's%/\*%##%' -e 's%##/%##%' <scripts/boilerplate.h
-echo -n "ERROR_TEMPLATES="
-for f in `ls -1 ${ROOT}/errors/templates/ERR_* | sort -u`
-do
-       echo " \\"
-       echo -n "    ${f}"
-done
-echo " "
-)| sed s%${ROOT}/errors/%%g >${ROOT}/errors/template.list
+printAmFile ERROR_TEMPLATES "errors/" "templates/ERR_*" > errors/template.am
 
 # Build errors translation install include from current .PO available
-(
-sed -e 's%\ \*%##%' -e 's%/\*%##%' -e 's%##/%##%' <scripts/boilerplate.h
-echo -n "TRANSLATE_LANGUAGES="
-for f in `ls -1 ${ROOT}/errors/*.po | sort -u`
-do
-       echo " \\"
-       echo -n "    ${f}"
-done
-echo " "
-)| sed s%${ROOT}/errors/%%g | sed s%\.po%\.lang%g >${ROOT}/errors/language.list
+printAmFile TRANSLATE_LANGUAGES "errors/" "*.po" | sed 's%\.po%\.lang%g' > errors/language.am
 
 # Build manuals translation install include from current .PO available
-(
-sed -e 's%\ \*%##%' -e 's%/\*%##%' -e 's%##/%##%' <scripts/boilerplate.h
-echo -n "TRANSLATE_LANGUAGES="
-for f in `ls -1 ${ROOT}/doc/manuals/*.po | sort -u`
-do
-       echo " \\"
-       echo -n "    ${f}"
-done
-echo " "
-)| sed s%${ROOT}/doc/manuals/%%g | sed s%\.po%\.lang%g >${ROOT}/doc/manuals/language.list
+printAmFile TRANSLATE_LANGUAGES "doc/manuals/" "*.po" | sed 's%\.po%\.lang%g' > doc/manuals/language.am
 
 # Build STUB framework include from current stub_* available
-(
-sed -e 's%\ \*%##%' -e 's%/\*%##%' -e 's%##/%##%' <scripts/boilerplate.h
-echo -n "STUB_SOURCE= tests/STUB.h"
-for f in `ls -1 ${ROOT}/src/tests/stub_*.cc | sort -u`
-do
-       echo " \\"
-       echo -n "       ${f}"
-done
-echo " "
-)| sed s%${ROOT}/src/%%g >${ROOT}/src/tests/Stub.list
-
-# Run formating
-echo "" >${ROOT}/doc/debug-sections.tmp
-srcformat || exit 1
-sort -u <${ROOT}/doc/debug-sections.tmp | sort -n >${ROOT}/doc/debug-sections.tmp2
-cat scripts/boilerplate.h ${ROOT}/doc/debug-sections.tmp2 >${ROOT}/doc/debug-sections.txt
-rm ${ROOT}/doc/debug-sections.tmp ${ROOT}/doc/debug-sections.tmp2
+printAmFile STUB_SOURCE "src/" "tests/stub_*.cc" > src/tests/Stub.am
+
+# Build the GPERF generated content
+make -C src/http gperf-files
+
+run_ checkMakeNamedErrorDetails || exit 1
+
+# Run formatting
+echo "" >doc/debug-sections.tmp
+srcFormat || exit 1
+sort -u <doc/debug-sections.tmp | sort -n >doc/debug-sections.tmp2
+cat scripts/boilerplate.h doc/debug-sections.tmp2 >doc/debug-sections.txt
+rm doc/debug-sections.tmp doc/debug-sections.tmp2
+rm -f boilerplate_fix.sed
+
+exit $SeenErrors