blob: 59cba101ea22d4922eadfcddf49479ba3395376c [file] [log] [blame]
#!/usr/bin/env bash
#-------------------------------------------------------------------------------
# Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
#-------------------------------------------------------------------------------
##
##@file
##@brief Execute checkpatch
##
##This bash script can be used to execute checkpatch for the tf-m project.
##The script can be started with -h to give help on usage.
##
##@var SKIP_PATHS
##@brief Folders and files to not be analysed by checkpatch
##
##This variable specifies the list of directories which shall not be analysed
##by checkpatch.
##This is a colon (:) separated list.
##
#This is needed for Doxygen for now.
#!string SKIP_PATHS;
SKIP_PATHS='./build-\*:./platform/\*:*/tz_\*:./lib/\*:./platform/ext/\*:./bl2/ext/\*:./docs/\*:./tools/\*:./interface/include/mbedtls/\*:./interface/include/psa/\*'
##@var TFM_DIRECTORY_NAME
##@brief Default path to tf-m source code.
##
#This is needed for Doxygen for now.
#!path TFM_DIRECTORY_NAME;
TFM_DIRECTORY_NAME="./"
##@var CHECKPATCH_PATH_DEF
##@brief Default Path to checkpatch executable.
##
#This is needed for Doxygen for now.
#!path CHECKPATCH_PATH_DEF;
CHECKPATCH_PATH_DEF=$(readlink -f $(dirname "$0")"/checkpatch")
##@var CHECKPATCH_PATH
##@brief Path to checkpatch executable.
##
## Checkpatch path can be overriden by user argument. Initialized with Default
## value of ./checkpatch
##
#This is needed for Doxygen for now.
#!path CHECKPATCH_PATH;
CHECKPATCH_PATH=$CHECKPATCH_PATH_DEF
##@fn usage(void)
##@brief Print help text on usage.
##@returns n/a
##
#This is needed for Doxygen for now.
#!void usage(void){};
usage() {
echo "Usage: $(basename -- "$0") [-v] [-h] [-d <TF-M dir>] [-f <output_filename>] [-p <number>]"
echo " -v, Verbose output"
echo " -h, Script help"
echo " -d, <TF-M dir>, TF-M directory"
echo " -f, <output_filename>, Output filename"
echo " -l <number>, Check only the last <number> commits (HEAD~<number>)."
echo " -p <path>, Provide location of directory containing checkpatch."
echo " -r, Print raw output. Implies verbose."
echo -e "\nNOTE: make sure checkpatch is located in '$CHECKPATCH_PATH'"
}
##@fn app_err(void)
##@brief Print error massage.
##@returns n/a
##
#This is needed for Doxygen for now.
#!void app_err(void){};
app_err() {
echo "run-checkpatch.sh($LINENO): Error: "$1 >&2
}
##@fn check_tree()
##@brief Run checkpatch in directory tree checking mode
##@returns status code
##
##Execute checkpatch to check the full content of all source files under the
##directory specified in \ref TFM_DIRECTORY_NAME. Directory content specified in
##\ref SKIP_PATHS will be excluded.
##This function uses xargs to execute multiple checkpatch instances in parallel.
##
#This is needed for Doxygen for now.
#!void check_tree(){};
check_tree() {
# Find all files to execute checkpatch on
FIND_CMD="find $TFM_DIRECTORY_NAME -name '*.[ch]' -a -not \( -path "${SKIP_PATHS//:/ -o -path }" \)"
echo "run-checkpatch.sh($LINENO): Scanning "$TFM_DIRECTORY_NAME" dir to find all .c and .h files to check..."
# Modify checkpatch command line to make checkpatch work on files.
CHECKPATCH_CMD="$CHECKPATCH_CMD -f "
if [ $VERBOSE -eq 1 ]; then
eval "$FIND_CMD" | xargs -n 1 -i -P 8 $CHECKPATCH_CMD {} |tee -a "$OUTPUT_FILE_PATH"
RETURN_CODE=${PIPESTATUS[1]}
else
eval "$FIND_CMD" | xargs -n 1 -i -P 8 $CHECKPATCH_CMD {} >> $OUTPUT_FILE_PATH
RETURN_CODE=${PIPESTATUS[1]}
fi
}
##@fn check_diff()
##@brief Run checkpatch in git diff mode.
##@returns status code
##
##Execute checkpatch to check the last N (\ref CHECK_LAST_COMMITS) commits of
##the branch checked out at directory specified in \ref TFM_DIRECTORY_NAME.
##Directory content specified in \ref SKIP_PATHS will be excluded.
##
#This is needed for Doxygen for now.
#!void check_diff(){};
check_diff() {
BASE_COMMIT="HEAD~$CHECK_LAST_COMMITS"
#use find to limit diff content to the same set of files as when checking
#the whole tree.
FIND_CMD="find ./ -name '*.[ch]' -a -not \( -path "${SKIP_PATHS//:/ -o -path }" \)"
#enter tf-m working copy to make git commands execute fine
pushd "$TFM_DIRECTORY_NAME" > /dev/null
#Execute popd when shell exits
trap popd 0
#List of files we care about. Filter out changed files from interesting
#list of files. This is needed to avoid GIT_CMD to break the argument
#list length.
CARE_LIST=$(eval $FIND_CMD | grep "$(git diff $BASE_COMMIT --name-only)" -)
if [ ! -z "$CARE_LIST" ]; then
# Only run checkpatch if there are files to check
GIT_CMD="git diff $BASE_COMMIT -- $CARE_LIST"
echo "run-checkpatch.sh($LINENO): $GIT_CMD"
echo "run-checkpatch.sh($LINENO): Checking commits: $(git log "$BASE_COMMIT"..HEAD --format=%h | tr $"\n" " ")"
#Modify checkpatch parameters to give more details when working on
#diff:s
CHECKPATCH_CMD="$CHECKPATCH_CMD --showfile -"
fi
if [ $VERBOSE -eq 1 ]; then
$GIT_CMD | $CHECKPATCH_CMD | tee -a "$OUTPUT_FILE_PATH"
RETURN_CODE=${PIPESTATUS[1]}
else
$GIT_CMD | $CHECKPATCH_CMD >> $OUTPUT_FILE_PATH
RETURN_CODE=${PIPESTATUS[1]}
fi
popd > /dev/null
#Remove cleanup trap.
trap 0
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~~~~~~~~~~~~~~~~~~~~~~~ Entry point ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Internal variables not to be modified.
VERBOSE=0
##@var CHECK_LAST_COMMITS
##@brief Number of commits to check.
##
##Number of commits relative to HEAD to check. When set to 0 full file content
##is checked instead of commit diffs.
##
#This is needed for Doxygen for now.
#!path CHECK_LAST_COMMITS;
CHECK_LAST_COMMITS=0
# Whether to print the output to screen.
RAW_OUTPUT=0
# Getting options and setting variables required to execute the script. This
# script starts executing from here.
while getopts "vhd:f:l:p:r" opt
do
case $opt in
v) VERBOSE=1 ;;
h) usage ; exit 0 ;;
d) TFM_DIRECTORY_NAME="$OPTARG" ;;
f) OUTPUT_FILE_PATH="$OPTARG" ;;
l) CHECK_LAST_COMMITS="$OPTARG" ;;
p) CHECKPATCH_PATH="$OPTARG" ;;
r) RAW_OUTPUT=1
VERBOSE=1 ;;
\?) usage ; exit 1 ;;
esac
done
#Convert checkpath override path to full path
CHECKPATCH_PATH=$(readlink -f "$CHECKPATCH_PATH")
# Convert TF-M specific type defs file to full path
TFM_TYPE_DEF_FILE=$CHECKPATCH_PATH"/tfm_type_defs.txt"
# Prepare CheckPatch config file
CHECKPATCH_CONFIG_FILENAME=$CHECKPATCH_PATH_DEF"/checkpatch.conf"
sed -i.bak "s#TFM_TYPE_DEF_FILE#$TFM_TYPE_DEF_FILE#g" $CHECKPATCH_CONFIG_FILENAME
# Create checkpatch command
CHECKPATCH_APP=$CHECKPATCH_PATH"/checkpatch.pl"
CHECKPATCH_CMD=$CHECKPATCH_APP" $(grep -o '^[^#]*' $CHECKPATCH_CONFIG_FILENAME)"
# Check if checkpatch is present
if ! [ -f "$CHECKPATCH_APP" ]; then
app_err "checkpatch.pl was not found. checkpatch.pl has to be located in $CHECKPATCH_PATH"
exit 1
fi
OUTPUT_FILE_PATH=${OUTPUT_FILE_PATH:-"tfm_checkpatch_report.txt"}
#Truncate previous content
: > $OUTPUT_FILE_PATH
#Do we need to work on a git diff?
if [ $CHECK_LAST_COMMITS -eq 0 ]
then
#Working on files
check_tree
else
#Working on git diff
check_diff
fi
#Restore the contents of the config file to original values
sed -i.bak "s#$TFM_TYPE_DEF_FILE#TFM_TYPE_DEF_FILE#g" $CHECKPATCH_CONFIG_FILENAME
rm $CHECKPATCH_CONFIG_FILENAME.bak
if [ "$RAW_OUTPUT" == "1" ] ; then
rm $OUTPUT_FILE_PATH
exit $RETURN_CODE
else
echo "run-checkpatch.sh($LINENO): checkpatch report \"$(readlink -f ${OUTPUT_FILE_PATH})\" is ready!"
fi