3 ## Copyright (C) 1996-2025 The Squid Software Foundation and contributors
5 ## Squid software is distributed under GPLv2+ license and includes
6 ## contributions from numerous individuals and organizations.
7 ## Please see the COPYING and CONTRIBUTORS files for details.
11 # This script contains the code run to perform automatic source maintenance
15 # whether to continue execution after a failure
16 # TODO: Expand the subset of failures covered by this feature; see run_().
18 # the actual name of the directive that enabled keep-going mode
22 # The script checks that the version of astyle is TargetAstyleVersion.
23 # if it isn't, the default behaviour is to not perform the formatting stage
24 # in order to avoid unexpected massive changes if the behaviour of astyle
25 # has changed in different releases.
26 # if --with-astyle /path/to/astyle is used, the check is still performed
27 # and a warning is printed, but the sources are reformatted
28 TargetAstyleVersion
="3.1"
31 # whether to check and, if necessary, update boilerplate copyright years
32 CheckAndUpdateCopyright
=yes
34 # How to sync CONTRIBUTORS with the current git branch commits:
35 # * never: Do not update CONTRIBUTORS at all.
36 # * auto: Check commits added since the last similar update.
37 # * SHA1/etc: Check commits added after the specified git commit.
38 UpdateContributorsSince
=auto
40 # --only-changed-since point
47 --check-and-update-copyright <yes|no> (default: yes)
49 --keep-going|-k (default: stop on error)
50 --only-changed-since <fork|commit> (default: apply to all files)
51 --update-contributors-since <never|auto|revision> (default: auto)
52 --with-astyle </path/to/astyle/executable> (default: astyle-${TargetAstyleVersion} or astyle)
60 This script applies Squid mandatory code style guidelines and generates
61 various files derived from Squid sources.
69 Print this information and exit.
71 --only-changed-since <"fork"|commit>
73 When specifieid, the script only examines for formatting changes those
74 files that have changed since the specified git reference point. The
75 argument is either a git commit (fed to "git diff") or a special keyword
76 "fork". Common commit values include HEAD^, master, origin/master, and the
77 branch the current one was forked off. When "fork" is specified, the
78 script will look for files changed since the current branch was forked off
79 upstream/master (according to "git merge-base --fork-point").
81 This option does not disable some repository-wide file generation and
82 repository-wide non-formatting checks/adjustments.
84 --update-contributors-since <never|auto|revision>
86 Configures how to sync CONTRIBUTORS with the current git branch commits:
87 * never: Do not update CONTRIBUTORS at all.
88 * auto: Check commits added since the last similar update.
89 * SHA1/etc: Check commits added after the specified git commit.
91 --with-astyle </path/to/astyle/executable>
93 Squid code style guidelines require astyle version $TargetAstyleVersion.
94 The path to the astyle binary can be specified using this command line
95 option or by exporting the ASTYLE environment variable. If both are
96 specified, the command-line option wins.
98 External dependencies:
100 * Astyle. See the --with-astyle command line option above.
101 * gperf (if you modify certain source files)
106 # command-line options
107 while [ $# -ge 1 ]; do
111 KeepGoingDirective
=$1
114 --check-and-update-copyright)
115 if test "x$2" != xyes
-a "x$2" != xno
118 echo "Error: Option $1 expects a yes or no argument but got $2"
121 CheckAndUpdateCopyright
=$2
124 --update-contributors-since)
128 echo "Error: Option $1 expects an argument."
131 UpdateContributorsSince
="$2"
142 --only-changed-since)
143 OnlyChangedSince
="$2"
148 echo "Unsupported command-line option: $1"
154 # an error code seen by a KeepGoing-aware command (or zero)
157 if ! git
diff --quiet; then
158 echo "There are unstaged changes. This script may modify sources."
159 echo "Stage changes to avoid permanent losses when things go bad."
163 # usage: <well-known program name> <program argument(s)> <candidate name>...
164 # Finds the first working program among the given candidate program names.
165 # The found program name is returned via the $FoundProgram global:
176 if "$candidate" $options < /dev
/null
> /dev
/null
2> /dev
/null
178 echo "Found ${wellKnown}-like program: $candidate"
179 FoundProgram
="$candidate"
184 echo "ERROR: Failed to find a ${wellKnown}-like program; tried: $*"
189 made
="generated" # a hack: prevents $GeneratedByMe searches matching this file
190 GeneratedByMe
="This file is $made by scripts/source-maintenance.sh."
192 if [ "x$ASTYLE" != "x" ] ; then
193 if ! "${ASTYLE}" --version > /dev
/null
2> /dev
/null
; then
194 echo "ERROR: Cannot run user-supplied astyle: ${ASTYLE}"
198 findProgram astyle
--version astyle-
${TargetAstyleVersion} astyle ||
exit $?
202 ASVER
=`"${ASTYLE}" --version 2>&1 | grep -o -E "[0-9.]+"`
203 if test "${ASVER}" != "${TargetAstyleVersion}" ; then
204 if test "${ASTYLE}" = "astyle" ; then
205 echo "Astyle version problem. You have ${ASVER} instead of ${TargetAstyleVersion}"
206 echo "Formatting step skipped due to version mismatch"
209 echo "WARNING: ${ASTYLE} is version ${ASVER} instead of ${TargetAstyleVersion}"
210 echo "Formatting anyway, please double check output before submitting"
213 echo "Detected expected astyle version: ${ASVER}"
216 if test "${ASVER}"; then
217 CppFormatter
="./scripts/format-cpp.pl --with-astyle ${ASTYLE}"
220 if test "x$OnlyChangedSince" = "xfork" ; then
221 ForkPoint
=`git merge-base --fork-point upstream/master`
222 if test "x$ForkPoint" = "x" ; then
223 echo "Could not identify fork point - sometimes it happens"
224 echo "Please specify commit-id explicitly"
227 OnlyChangedSince
="$ForkPoint"
230 if test $CheckAndUpdateCopyright = yes
232 COPYRIGHT_YEARS
=`date +"1996-%Y"`
233 echo "s/1996-2[0-9]+ The Squid Software Foundation and contributors/${COPYRIGHT_YEARS} The Squid Software Foundation and contributors/g" >> boilerplate_fix.
sed
236 # executes the specified command
237 # in KeepGoing mode, remembers errors and hides them from callers
240 "$@" && return 0 # return on success
243 if test $KeepGoing = no
; then
247 echo "ERROR: Continuing after a failure ($error) due to $KeepGoingDirective"
248 SeenErrors
=$error # TODO: Remember the _first_ error instead
249 return 0 # hide error from the caller
258 if ! cmp -s "${original}" "${updated}"; then
259 echo "NOTICE: File ${original} changed: ${message}"
260 run_
mv "${updated}" "${original}" ||
return
262 run_
rm -f "${updated}" ||
exit $?
266 # uses the given script to update the given source file
273 $script "$source" > "$new" &&
274 updateIfChanged
"$source" "$new" "by $script"
277 # updates the given source file using the given script(s)
283 for script in `git ls-files "$@"`; do
284 run_ applyPlugin
$script $source ||
return
288 # succeeds if all MakeNamedErrorDetail() names are unique
289 checkMakeNamedErrorDetails
()
291 problems
=1 # assume there are problems until proven otherwise
293 options
='-h --only-matching --extended-regexp'
294 git
grep $options 'MakeNamedErrorDetail[(]"[^"]*"[)]' src |
297 MakeNamedErrorDetail.tmp
299 if grep --quiet --word-regexp 1 MakeNamedErrorDetail.tmp
; then
300 if grep --invert-match --word-regexp 1 MakeNamedErrorDetail.tmp
; then
301 echo "ERROR: Duplicated MakeNamedErrorDetail names (see above)."
306 echo "ERROR: Cannot find or process MakeNamedErrorDetail calls."
309 rm MakeNamedErrorDetail.tmp
# ignore (unexpected) cleanup failures
313 # extract IDs and gists of cache_log_message debugs() in the given source file
314 collectDebugMessagesFrom
()
317 destination
="doc/debug-messages.tmp"
319 if test "x$OnlyChangedSince" != "x"; then
320 # Skipping collection due to --only-changed-since.
321 # processDebugMessages() will warn.
325 # Merge multi-line debugs() into one-liners and remove '//...' comments.
326 awk 'BEGIN { found=0; dbgLine=""; } {
327 if ($0 ~ /[ \t]debugs[ \t]*\(/)
330 commented = match($0, /\);[ \t]*\/\//);
332 $0 = substr($0, 1, RSTART+1);
333 dbgLine = dbgLine $0;
342 }' $source > doc
/debug-messages.tmp2
345 # - replace debugs() prefix with the message ID contained in it
346 # - remove simple parenthesized non-"string" items like (a ? b : c)
347 # - replace any remaining non-"string" items with ...
348 # - remove quotes around "strings"
349 # - remove excessive whitespace
350 # - remove debugs() statement termination sugar
351 grep -o -E '\bdebugs[^,]*,[^,]*(Critical|Important)[(][0-9]+.*' doc
/debug-messages.tmp2 | \
353 -e 's/.*(Critical|Important)[(]([0-9]+)[)][^,]*,\s*/\2 /' \
354 -e 's/<<\s*[(].*[)]\s*(<<|[)];)/<< ... \1/g' \
355 -e 's/<<\s*[^"]*/.../g' \
356 -e 's@([^\\])"@\1@g' \
361 rm -f doc
/debug-messages.tmp2
364 # make doc/debug-messages.dox from aggregate collectDebugMessagesFrom results
365 processDebugMessages
()
367 source="doc/debug-messages.tmp"
368 destination
="doc/debug-messages.dox"
370 if test "x$OnlyChangedSince" != "x"; then
371 echo "WARNING: Skipping update of $destination due to --only-changed-since"
375 if test '!' -s "$source"; then
376 echo "ERROR: Failed to find debugs() message IDs"
380 repeatedIds
=`awk '{print $1}' $source | sort -n | uniq -d`
381 if test "x$repeatedIds" != "x"; then
382 echo "ERROR: Repeated debugs() message IDs:"
388 repeatedGists
=`awk '{$1=""; print substr($0,2)}' $source | sort | uniq -d`
389 if test "x$repeatedGists" != "x"; then
390 echo "ERROR: Repeated debugs() message gists:"
391 echo "$repeatedGists"
396 cat scripts
/boilerplate.h
> $destination
397 printf '/**\n' >> $destination
398 printf '\\page ControlledCacheLogMessages Message IDs and gists for cache_log_message\n' >> $destination
399 printf '\\verbatim\n' >> $destination
400 printf 'ID Message gist\n' >> $destination
401 printf '== ============\n' >> $destination
402 sort -n < $source >> $destination
403 printf '\\endverbatim\n' >> $destination
404 printf '*/\n' >> $destination
409 # make doc/debug-sections.txt from aggregated by srcFormat extracts
410 processDebugSections
()
412 destination
="doc/debug-sections.txt"
414 if test "x$OnlyChangedSince" != "x"; then
415 echo "WARNING: Skipping update of $destination due to --only-changed-since"
419 LC_ALL
=C
sort -u < doc
/debug-sections.tmp
> doc
/debug-sections.tmp2
421 cat scripts
/boilerplate.h
> $destination
422 echo "" >> $destination
423 cat doc
/debug-sections.tmp2
>> $destination
425 rm -f doc
/debug-sections.tmp
*
430 # remove stale temporary files that accumulate info extracted below
431 rm -f doc
/debug-messages.tmp
*
432 rm -f doc
/debug-sections.tmp
*
435 # Scan for incorrect use of #ifdef/#ifndef
437 git
grep "ifn?def .*_SQUID_" |
439 grep -v "scripts/source-maintenance.sh" |
440 while read f
; do echo "PROBLEM?: ${f}"; done
443 # Scan for file-specific actions
446 # The two git commands below will also list any files modified during the
447 # current run (e.g., src/http/RegisteredHeadersHash.cci or icons/icon.am).
449 if test "x$OnlyChangedSince" != "x" ; then
450 FilesToOperateOn
=`git diff --name-only $OnlyChangedSince`
452 if test $gitResult -ne 0 ; then
453 echo "ERROR: Cannot use --only-changed-since reference point: $OnlyChangedSince"
454 echo "Consider using a git commit SHA (from git log) instead"
458 FilesToOperateOn
=`git ls-files`
460 # a bit paranoid but protects the empty $FilesToOperateOn check below
461 if test $gitResult -ne 0 ; then
462 echo "ERROR: Cannot find source code file names"
466 if test "x$FilesToOperateOn" = "x"; then
467 echo "WARNING: No files to scan and format"
471 for FILENAME
in $FilesToOperateOn; do
472 skip_copyright_check
=""
474 # skip subdirectories, git ls-files is recursive
475 test -d $FILENAME && continue
477 # generated files are formatted during their generation
478 if grep -q -F "$GeneratedByMe" ${FILENAME}; then
487 # Code Style formatting maintenance
489 applyPluginsTo
${FILENAME} scripts
/maintenance
/ ||
return
490 if test "$CppFormatter"; then
491 if $CppFormatter $FILENAME > $FILENAME.new
; then
492 updateIfChanged
$FILENAME $FILENAME.new
'by astyle'
499 # REQUIRE squid.h first #include
503 # ignore, this is a build tool.
506 FI
=`grep "#include" ${FILENAME} | head -1`;
507 if test "${FI}" != "#include \"squid.h\"" -a "${FILENAME}" != "cf_gen.cc"; then
508 echo "ERROR: ${FILENAME} does not include squid.h first!"
512 FI
=`grep "#include \"squid.h\"" ${FILENAME}`;
513 if test "x${FI}" != "x" ; then
514 echo "ERROR: ${FILENAME} duplicate include of squid.h"
520 # If a file includes openssl headers, then it must include compat/openssl.h
522 if test "${FILENAME}" != "compat/openssl.h"; then
523 FA
=`grep "#include.*openssl/" "${FILENAME}" 2>/dev/null | head -1`;
524 FB
=`grep '#include.*compat/openssl[.]h' "${FILENAME}" 2>/dev/null | head -1`;
525 if test "x${FA}" != "x" -a "x${FB}" = "x"; then
526 echo "ERROR: ${FILENAME} includes openssl headers without including \"compat/openssl.h\""
531 # forward.h means different things to Squid code depending on the path
532 # require the full path is explicit for every include
534 FI
=`grep "#include \"forward.h\"" ${FILENAME}`;
535 if test "x${FI}" != "x" ; then
536 echo "ERROR: ${FILENAME} contains reference to forward.h without path"
540 # detect functions unsafe for use within Squid.
541 # strdup() - only allowed in compat/xstring.h which defines a safe replacement.
542 # sprintf() - not allowed anywhere.
544 STRDUP
=`grep -e "[^x]strdup(" ${FILENAME}`;
545 if test "x${STRDUP}" != "x" -a "${FILENAME}" != "compat/xstring.h"; then
546 echo "ERROR: ${FILENAME} contains unprotected use of strdup()"
548 SPRINTF
=`grep -e "[^v]sprintf(" ${FILENAME}`;
549 if test "x${SPRINTF}" != "x" ; then
550 echo "ERROR: ${FILENAME} contains unsafe use of sprintf()"
553 collectDebugMessagesFrom
${FILENAME}
556 # DEBUG Section list maintenance
558 grep " DEBUG: section" <${FILENAME} |
sed -e 's/ \* DEBUG: //' -e 's%/\* DEBUG: %%' -e 's% \*/%%' >> doc
/debug-sections.tmp
561 # File permissions maintenance.
563 chmod 644 ${FILENAME}
568 # File permissions maintenance.
570 chmod 755 ${FILENAME}
574 applyPluginsTo
${FILENAME} scripts
/format-makefile-am.pl ||
return
577 ChangeLog|CREDITS|CONTRIBUTORS|COPYING|
*.png|
*.po|
*.pot|rfcs
/|
*.txt|test-suite
/squidconf
/empty|.bzrignore
)
578 # we do not enforce copyright blurbs in:
580 # Squid Project contributor attribution file
581 # third-party copyright attribution file
584 # license documentation files
585 # (imported) plain-text documentation files and ChangeLogs
588 skip_copyright_check
=1
592 # check for Foundation copyright blurb
593 if test $CheckAndUpdateCopyright = yes -a -f ${FILENAME} -a "x$skip_copyright_check" = "x"; then
594 BLURB
=`grep -o "${COPYRIGHT_YEARS} The Squid Software Foundation and contributors" ${FILENAME}`;
595 if test "x${BLURB}" = "x"; then
596 BOILER
=`grep -o -E "1996-2[0-9]+ The Squid Software Foundation and contributors" ${FILENAME}`;
597 if test "x${BOILER}" != "x"; then
598 echo "UPDATE COPYRIGHT for ${FILENAME}"
599 sed --in-place -r -f boilerplate_fix.
sed ${FILENAME}
601 echo "CHECK COPYRIGHT for ${FILENAME}"
608 run_ processDebugSections ||
return
609 run_ processDebugMessages ||
return
614 sed -e 's%\ \*%##%; s%/\*%##%; s%##/%##%' < scripts
/boilerplate.h
616 echo "## $GeneratedByMe"
620 # Only some files are formed from *.po filenames, but all such files
621 # should list *.lang filenames instead.
622 git ls-files
$2$3 |
sed -e s
%$2%%g
-e 's%\.po%\.lang%g' |
while read f
; do
623 printf ' \\\n\t%s' "${f}"
633 # format immediately/here instead of in srcFormat to avoid misleading
634 # "NOTICE: File ... changed by scripts/format-makefile-am.pl" in srcFormat
635 printRawAmFile
"$@" |
scripts
/format-makefile-am.pl
> $amFile.new
637 # Distinguishing generation-only changes from formatting-only changes is
638 # difficult, so we only check/report cumulative changes. Most interesting
639 # changes are triggered by printRawAmFile() finding new entries.
640 updateIfChanged
$amFile $amFile.new
'by generateAmFile()'
643 # Build icons install include from current icons available
644 generateAmFile icons
/icon.am ICONS
"icons/" "silk/*"
646 # Build templates install include from current templates available
647 generateAmFile errors
/template.am ERROR_TEMPLATES
"errors/" "templates/ERR_*"
649 # Build errors translation install include from current .PO available
650 generateAmFile errors
/language.am LANGUAGE_FILES
"errors/" "*.po"
652 # Build manuals translation install include from current .PO available
653 generateAmFile
doc
/manuals
/language.am LANGUAGE_FILES
"doc/manuals/" "*.po"
655 # Build STUB framework include from current stub_* available
656 generateAmFile src
/tests
/Stub.am STUB_SOURCE
"src/" "tests/stub_*.cc"
658 generateRawGperfFile
()
662 echo "/* $GeneratedByMe */"
665 if test `gperf --version | head -1 | cut -d ' ' -f 3 | cut -d. -f '-2' | sed -e 's/\.//'` -lt 32 ; then
666 # We configure C++ compilers to complain about missing '[[fallthrough]]' attribute
667 # where old gperf versions use a '/*FALLTHROUGH*/' code comment.
668 (cd `dirname $gperfFile` && gperf
-m 100000 `basename $gperfFile`) | \
669 sed -e 's@/[*]FALLTHROUGH[*]/@[[fallthrough]];@g'
671 # gperf 3.2+ provide fallthrough attributes
672 (cd `dirname $gperfFile` && gperf
-m 100000 `basename $gperfFile`)
679 cciFile
=`echo $gperfFile | sed 's/[.]gperf$/.cci/'`
681 if test $gperfFile -ot $cciFile; then
685 generateRawGperfFile
$gperfFile > $cciFile.unformatted ||
return
687 if test "$CppFormatter"; then
688 # generateAmFile() explains why we format immediately/here
689 $CppFormatter $cciFile.unformatted
> $cciFile.new ||
return
690 rm $cciFile.unformatted
692 echo "ERROR: Source code formatting disabled, but regenerated $cciFile needs formatting"
693 mv $cciFile.unformatted
$cciFile.new ||
return
696 # generateAmFile() explains why we only check/report cumulative changes
697 updateIfChanged
$cciFile $cciFile.new
'by generateGperfFile()'
700 run_ generateGperfFile src
/http
/RegisteredHeadersHash.gperf ||
exit 1
702 run_ checkMakeNamedErrorDetails ||
exit 1
704 # This function updates CONTRIBUTORS based on the recent[1] branch commit log.
705 # Fresh contributor entries are filtered using the latest vetted CONTRIBOTORS
706 # file on the current branch. The following CONTRIBUTORS commits are
709 # * authored (in "git log --author" sense) by squidadm,
710 # * matching (in "git log --grep" sense) $vettedCommitPhraseRegex set below.
712 # A human authoring an official GitHub pull request containing a new
713 # CONTRIBUTORS version (that they want to be used as a new vetting point)
714 # should add a phrase matching $vettedCommitPhraseRegex to the PR description.
716 # [1] As defined by the --update-contributors-since script parameter.
719 if test "x$UpdateContributorsSince" = xnever
721 return 0 # successfully did nothing, as requested
724 vettedCommitPhraseRegex
='[Rr]eference point for automated CONTRIBUTORS updates'
726 since
="$UpdateContributorsSince"
727 if test "x$UpdateContributorsSince" = xauto
729 # find the last CONTRIBUTORS commit vetted by a human
730 humanSha
=`git log -n1 --format='%H' --grep="$vettedCommitPhraseRegex" CONTRIBUTORS`
731 # find the last CONTRIBUTORS commit attributed to this script
732 botSha
=`git log -n1 --format='%H' --author=squidadm CONTRIBUTORS`
733 if test "x$humanSha" = x
&& test "x$botSha" = x
735 echo "ERROR: Unable to determine the commit to start contributors extraction from"
739 # find the latest commit among the above one or two commits
740 if test "x$humanSha" = x
743 elif test "x$botSha" = x
746 elif git merge-base
--is-ancestor $humanSha $botSha
752 echo "Collecting contributors since $since"
756 # We add four leading spaces below to mimic CONTRIBUTORS entry style.
757 # add commit authors:
758 git log
--format=' %an <%ae>' $range > authors.tmp
759 # add commit co-authors:
761 grep -Ei '^[[:space:]]*Co-authored-by:' | \
762 sed -r 's/^\s*Co-authored-by:\s*/ /i' >> authors.tmp
763 # but do not add committers (--format=' %cn <%ce>').
765 # add collected new (co-)authors, if any, to CONTRIBUTORS
766 if .
/scripts
/update-contributors.pl
--quiet < authors.tmp
> CONTRIBUTORS.new
768 updateIfChanged CONTRIBUTORS CONTRIBUTORS.new \
769 "A human PR description should match: $vettedCommitPhraseRegex"
777 # Update CONTRIBUTORS content
778 run_ collectAuthors ||
exit 1
783 test -e boilerplate_fix.
sed && rm -f boilerplate_fix.
sed