blob: 91bee981ec43e725e8d65548b690c89eb32bfb3e [file] [log] [blame]
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# SHELLDOC-IGNORE
add_test_type checkstyle
CHECKSTYLE_TIMER=0
CHECKSTYLE_GOAL_DEFAULT="checkstyle"
CHECKSTYLE_GOAL="${CHECKSTYLE_GOAL_DEFAULT}"
CHECKSTYLE_OPTIONS_DEFAULT="-Dcheckstyle.consoleOutput=true"
CHECKSTYLE_OPTIONS="${CHECKSTYLE_OPTIONS_DEFAULT}"
function checkstyle_filefilter
{
local filename=$1
if [[ ${BUILDTOOL} == maven
|| ${BUILDTOOL} == ant ]]; then
if [[ ${filename} =~ \.java$ ]]; then
add_test checkstyle
fi
fi
if [[ ${BUILDTOOL} == gradle ]]; then
if [[ ${filename} =~ \.java$
|| ${filename} =~ config/checkstyle/checkstyle.xml
|| ${filename} =~ config/checkstyle/suppressions.xml ]]; then
add_test checkstyle
fi
fi
}
## @description usage help for checkstyle
## @audience private
## @stability evolving
## @replaceable no
function checkstyle_usage
{
yetus_add_option "--checkstyle-goal=<goal>" "Checkstyle maven plugin goal to use, 'check' and 'checkstyle' supported. Defaults to '${CHECKSTYLE_GOAL_DEFAULT}'."
}
## @description parse checkstyle args
## @audience private
## @stability evolving
## @replaceable no
## @param arg
## @param ..
function checkstyle_parse_args
{
local i
for i in "$@"; do
case ${i} in
--checkstyle-goal=*)
delete_parameter "${i}"
CHECKSTYLE_GOAL=${i#*=}
case ${CHECKSTYLE_GOAL} in
check)
CHECKSTYLE_OPTIONS="-Dcheckstyle.consoleOutput=true -Dcheckstyle.failOnViolation=false"
;;
checkstyle)
;;
*)
yetus_error "Warning: checkstyle goal ${CHECKSTYLE_GOAL} not supported. It may have unexpected behavior"
;;
esac
;;
esac
done
}
## @description initialize the checkstyle plug-in
## @audience private
## @stability evolving
## @replaceable no
function checkstyle_initialize
{
if declare -f maven_add_install >/dev/null 2>&1; then
maven_add_install checkstyle
fi
}
## @description checkstyle plug-in specific difference calculator
## @audience private
## @stability evolving
## @replaceable no
## @param branchlog
## @param patchlog
## @return differences
function checkstyle_calcdiffs
{
declare orig=$1
declare new=$2
declare tmp=${PATCH_DIR}/pl.$$.${RANDOM}
declare j
# first, strip filenames:line:
# this keeps column: in an attempt to increase
# accuracy in case of multiple, repeated errors
# since the column number shouldn't change
# if the line of code hasn't been touched
# remove the numbers from the error message for comparing
# so if only the error message numbers change
# we do not report new error
# shellcheck disable=SC2016
cut -f3- -d: "${orig}" | "${AWK}" -F'\1' '{ gsub("[0-9,]+", "", $2) ;print $1":"$2}' > "${tmp}.branch"
# shellcheck disable=SC2016
cut -f3- -d: "${new}" | "${AWK}" -F'\1' '{ gsub("[0-9,]+", "", $2) ;print $1":"$2}' > "${tmp}.patch"
# compare the errors, generating a string of line
# numbers. Sorry portability: GNU diff makes this too easy
"${DIFF}" --unchanged-line-format="" \
--old-line-format="" \
--new-line-format="%dn " \
"${tmp}.branch" \
"${tmp}.patch" > "${tmp}.lined"
# now, pull out those lines of the raw output
# removing extra marker before the chekstyle error
# message which was needed for calculations
# shellcheck disable=SC2013
for j in $(cat "${tmp}.lined"); do
head -"${j}" "${new}" | tail -1 | tr -d $'\x01'
done
rm "${tmp}.branch" "${tmp}.patch" "${tmp}.lined" 2>/dev/null
}
## @description execute checkstyle
## @audience private
## @stability stable
## @replaceable no
function checkstyle_runner
{
declare repostatus=$1
declare tmp=${PATCH_DIR}/$$.${RANDOM}
declare j
declare i=0
declare fn
declare savestart=${TIMER}
declare savestop
declare output
declare logfile
declare logfilename
declare modulesuffix
declare cmd
declare line
declare newline
declare logline
declare text
declare linenum
declare codeline
declare cmdresult
declare -a filelist
declare rol
# first, let's clear out any previous run information
modules_reset
# loop through the modules we've been given
#shellcheck disable=SC2153
until [[ $i -eq ${#MODULE[@]} ]]; do
# start the clock per module, setup some help vars, etc
start_clock
fn=$(module_file_fragment "${MODULE[${i}]}")
modulesuffix=$(basename "${MODULE[${i}]}")
output="${PATCH_DIR}/${repostatus}-checkstyle-${fn}.txt"
logfile="${PATCH_DIR}/buildtool-${repostatus}-checkstyle-${fn}.txt"
buildtool_cwd "${i}"
#BUG - fix this
case ${BUILDTOOL} in
ant)
cmd="${ANT} \
-Dcheckstyle.consoleOutput=true \
${MODULEEXTRAPARAM[${i}]//@@@MODULEFN@@@/${fn}} \
${ANT_ARGS[*]} checkstyle"
;;
maven)
cmd="${MAVEN} ${MAVEN_ARGS[*]} \
checkstyle:${CHECKSTYLE_GOAL} \
${CHECKSTYLE_OPTIONS} \
${MODULEEXTRAPARAM[${i}]//@@@MODULEFN@@@/${fn}} -Ptest-patch"
;;
gradle)
cmd="${GRADLEW} ${GRADLEW_ARGS[*]} \
checkstyleMain checkstyleTest \
${MODULEEXTRAPARAM[${i}]//@@@MODULEFN@@@/${fn}}"
;;
*)
UNSUPPORTED_TEST=true
return 0
;;
esac
# we're going to execute it and pull out
# anything that beings with a /. that's
# almost certainly checkstyle output.
# checkstyle 6.14 or upper adds severity
# to the beginning of line, so removing it
#shellcheck disable=SC2086
echo_and_redirect "${logfile}" ${cmd}
cmdresult=$?
case ${BUILDTOOL} in
ant)
"${SED}" -e 's,^\[ERROR\] ,,g' -e 's,^\[WARN\] ,,g' "${logfile}" \
| "${GREP}" ^/ \
| "${SED}" -e "s,${BASEDIR},.,g" \
> "${tmp}"
;;
maven)
"${SED}" -e 's,^\[ERROR\] ,,g' -e 's,^\[WARN\] ,,g' "${logfile}" \
| "${GREP}" ^/ \
| "${SED}" -e "s,${BASEDIR},.,g" \
> "${tmp}"
;;
gradle)
# find -path isn't quite everywhere yet...
while read -r line; do
if [[ ${line} =~ build/reports/checkstyle ]]; then
filelist+=("${line}")
fi
done < <(find "${BASEDIR}" -name 'main.xml' -o -name 'test.xml')
touch "${tmp}"
if [[ ${#filelist[@]} -gt 0 ]]; then
# convert xml to maven-like output
while read -r line; do
if [[ $line =~ ^\<file ]]; then
fn=$(echo "$line" | "${SED}" -E 's,^.+name="([^"]+)".+$,\1,' | "${SED}" -e "s,${BASEDIR},.,g")
elif [[ $line =~ ^\<error ]]; then
if [[ $line =~ column ]]; then
newline=$(echo "$line" | "${SED}" -E 's,^.+line="([0-9]+)".*column="([0-9]+)".*severity="([a-z]+)".*message="([^"]+)".+$,__FN__:\1\:\2:\4,')
else
newline=$(echo "$line" | "${SED}" -E 's,^.+line="([0-9]+)".*severity="([a-z]+)".*message="([^"]+)".+$,__FN__:\1:\3,')
fi
newline=$(unescape_html "${newline}")
newline=$(echo "$newline" | "${SED}" -e "s,__FN__,$fn,")
echo "$newline" >> "${tmp}"
fi
done < <(cat "${filelist[@]}")
fi
;;
esac
if [[ "${modulesuffix}" == . ]]; then
modulesuffix=root
fi
logfilename=$(basename "${logfile}")
if [[ ${cmdresult} == 0 ]]; then
module_status ${i} +1 "${logfilename}" "${BUILDMODEMSG} passed checkstyle in ${modulesuffix}"
else
module_status ${i} -1 "${logfilename}" "${BUILDMODEMSG} fails to run checkstyle in ${modulesuffix}"
((result = result + 1))
fi
# if we have some output, we need to do more work:
if [[ -s ${tmp} && "${BUILDMODE}" = patch ]]; then
# first, let's pull out all of the files that
# we actually care about, esp since that run
# above is likely from the entire source
# this will grealy cut down how much work we
# have to do later
for j in "${CHANGED_FILES[@]}"; do
"${GREP}" "${j}" "${tmp}" >> "${tmp}.1"
done
# now that we have just the files we care about,
# let's unscrew it. You see...
# checkstyle seems to do everything it possibly can
# to make it hard to process, including inconsistent
# output (sometimes it has columns, sometimes it doesn't!)
# and giving very generic errors when context would be
# helpful, esp when doing diffs.
# in order to help calcdiff and the user out, we're
# going to reprocess the output to include the code
# line being flagged. When calcdiff gets a hold of this
# it will have the code to act as context to help
# report the correct line
# file:linenum:(column:)error ====>
# file:linenum:code(:column)\x01:error
# \x01 will later used to identify the beginning
# of the checkstyle error message
pushd "${BASEDIR}" >/dev/null || return 1
while read -r logline; do
file=${logline%%:*}
rol=${logline/#${file}:}
linenum=${rol%%:*}
text=${rol/#${linenum}:}
codeline=$(head -n "+${linenum}" "${file}" | tail -1 )
{
echo -n "${file}:${linenum}:${codeline}"
echo -ne '\x01'
echo ":${text}"
} >> "${output}"
done < <(cat "${tmp}.1")
popd >/dev/null || return 1
# later on, calcdiff will turn this into code(:column):error
# compare, and then put the file:line back onto it.
else
cp -p "${tmp}" "${output}"
fi
rm "${tmp}" "${tmp}.1" 2>/dev/null
savestop=$(stop_clock)
#shellcheck disable=SC2034
MODULE_STATUS_TIMER[${i}]=${savestop}
popd >/dev/null || return 1
((i=i+1))
done
TIMER=${savestart}
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
function checkstyle_postcompile
{
declare repostatus=$1
if [[ "${repostatus}" = branch ]]; then
checkstyle_preapply
else
checkstyle_postapply
fi
}
function checkstyle_preapply
{
local result
if ! verify_needed_test checkstyle; then
return 0
fi
big_console_header "checkstyle: ${PATCH_BRANCH}"
start_clock
personality_modules_wrapper branch checkstyle
checkstyle_runner branch
result=$?
modules_messages branch checkstyle true
# keep track of how much as elapsed for us already
CHECKSTYLE_TIMER=$(stop_clock)
if [[ ${result} != 0 ]]; then
return 1
fi
return 0
}
function checkstyle_postapply
{
declare result
declare module
declare mod
declare fn
declare i=0
declare numbranch=0
declare numpatch=0
declare addpatch=0
declare summarize=true
declare tmpvar
if ! verify_needed_test checkstyle; then
return 0
fi
big_console_header "checkstyle: ${BUILDMODE}"
start_clock
personality_modules_wrapper patch checkstyle
checkstyle_runner patch
result=$?
if [[ ${UNSUPPORTED_TEST} = true ]]; then
return 0
fi
# add our previous elapsed to our new timer
# by setting the clock back
offset_clock "${CHECKSTYLE_TIMER}"
until [[ $i -eq ${#MODULE[@]} ]]; do
if [[ ${MODULE_STATUS[${i}]} == -1 ]]; then
((result=result+1))
((i=i+1))
continue
fi
module=${MODULE[$i]}
fn=$(module_file_fragment "${module}")
# if there is no comparison to be done,
# we can speed this up tremendously
if [[ "${BUILDMODE}" = full ]]; then
touch "${PATCH_DIR}/branch-checkstyle-${fn}.txt"
cp -p "${PATCH_DIR}/patch-checkstyle-${fn}.txt" \
"${PATCH_DIR}/results-checkstyle-${fn}.txt"
else
# call calcdiffs to allow overrides
calcdiffs \
"${PATCH_DIR}/branch-checkstyle-${fn}.txt" \
"${PATCH_DIR}/patch-checkstyle-${fn}.txt" \
checkstyle \
> "${PATCH_DIR}/results-checkstyle-${fn}.txt"
fi
tmpvar=$(wc -l "${PATCH_DIR}/branch-checkstyle-${fn}.txt")
numbranch=${tmpvar%% *}
tmpvar=$(wc -l "${PATCH_DIR}/patch-checkstyle-${fn}.txt")
numpatch=${tmpvar%% *}
tmpvar=$(wc -l "${PATCH_DIR}/results-checkstyle-${fn}.txt")
addpatch=${tmpvar%% *}
((fixedpatch=numbranch-numpatch+addpatch))
statstring=$(generic_calcdiff_status "${numbranch}" "${numpatch}" "${addpatch}" )
mod=${module}
if [[ ${mod} = \. ]]; then
mod=root
fi
if [[ ${addpatch} -gt 0 ]] ; then
((result = result + 1))
module_status "${i}" -1 "results-checkstyle-${fn}.txt" "${mod}: ${BUILDMODEMSG} ${statstring}"
elif [[ ${fixedpatch} -gt 0 ]]; then
module_status "${i}" +1 "results-checkstyle-${fn}.txt" "${mod}: ${BUILDMODEMSG} ${statstring}"
summarize=false
fi
((i=i+1))
done
modules_messages patch checkstyle "${summarize}"
if [[ ${result} != 0 ]]; then
return 1
fi
return 0
}