1#!/usr/bin/env bash
2
3set -e
4
5function help() {
6    cat <<EOF
7Usage: ${0} [OPTION] ... -- <compiler arguments>
8
9This script is a wrapper for cppcheck that enables it to analyse the files that
10are the target for the build, it is used in place of a selected compiler and the
11make process will run it on every file that needs to be built.
12All the arguments passed to the original compiler are forwarded to it without
13modification, furthermore, they are used to improve the cppcheck analysis.
14
15Options:
16  --compiler=       Use this compiler for the build
17  --cppcheck-cmd=   Command line for the cppcheck analysis.
18  --cppcheck-html   Prepare for cppcheck HTML output
19  --cppcheck-plat=  Path to the cppcheck platform folder
20  --ignore-path=    This script won't run cppcheck on the files having this
21                    path, the compiler will run anyway on them. This argument
22                    can be specified multiple times.
23  -h, --help        Print this help
24EOF
25}
26
27BUILD_DIR=""
28CC_FILE=""
29OBJ_FILE=""
30COMPILER=""
31CPPCHECK_HTML="n"
32CPPCHECK_PLAT_PATH=""
33CPPCHECK_TOOL=""
34CPPCHECK_TOOL_ARGS=""
35FORWARD_FLAGS=""
36IGNORE_PATH="n"
37IGNORE_PATH_LIST=""
38JDB_FILE=""
39OBJTREE_PATH=""
40
41# Variable used for arg parsing
42forward_to_cc="n"
43sm_tool_args="n"
44obj_arg_content="n"
45
46for OPTION in "$@"
47do
48    if [ "${forward_to_cc}" = "y" ]; then
49        if [[ ${OPTION} == *.c ]]
50        then
51            CC_FILE="${OPTION}"
52        elif [ "${OPTION}" = "-o" ]
53        then
54            # After -o there is the path to the obj file, flag it
55            obj_arg_content="y"
56        elif [ "${obj_arg_content}" = "y" ]
57        then
58            # This must be the path to the obj file, turn off flag and save path
59            OBJTREE_PATH="$(dirname "${OPTION}")"
60            OBJ_FILE="$(basename "${OPTION}")"
61            obj_arg_content="n"
62        fi
63        # Forward any argument to the compiler
64        FORWARD_FLAGS="${FORWARD_FLAGS} ${OPTION}"
65        continue
66    fi
67    case ${OPTION} in
68        -h|--help)
69            help
70            exit 0
71            ;;
72        --build-dir=*)
73            BUILD_DIR="${OPTION#*=}"
74            sm_tool_args="n"
75            ;;
76        --compiler=*)
77            COMPILER="${OPTION#*=}"
78            sm_tool_args="n"
79            ;;
80        --cppcheck-cmd=*)
81            CPPCHECK_TOOL="${OPTION#*=}"
82            sm_tool_args="y"
83            ;;
84        --cppcheck-html)
85            CPPCHECK_HTML="y"
86            sm_tool_args="n"
87            ;;
88        --cppcheck-plat=*)
89            CPPCHECK_PLAT_PATH="${OPTION#*=}"
90            sm_tool_args="n"
91            ;;
92        --ignore-path=*)
93            IGNORE_PATH_LIST="${IGNORE_PATH_LIST} ${OPTION#*=}"
94            sm_tool_args="n"
95            ;;
96        --)
97            forward_to_cc="y"
98            sm_tool_args="n"
99            ;;
100        *)
101            if [ "${sm_tool_args}" = "y" ]; then
102                CPPCHECK_TOOL_ARGS="${CPPCHECK_TOOL_ARGS} ${OPTION}"
103            else
104                echo "Invalid option ${OPTION}"
105                exit 1
106            fi
107            ;;
108    esac
109done
110
111if [ "${COMPILER}" = "" ]
112then
113    echo "--compiler arg is mandatory."
114    exit 1
115fi
116
117if [ "${BUILD_DIR}" = "" ]
118then
119    echo "--build-dir arg is mandatory."
120    exit 1
121fi
122
123function create_jcd() {
124    local line="${1}"
125    local arg_num=0
126    local same_line=0
127
128    {
129        echo "["
130        echo "    {"
131        echo "        \"arguments\": ["
132
133        for arg in ${line}; do
134            # This code prevents to put comma in the last element of the list or
135            # on sequential lines that are going to be merged
136            if [ "${arg_num}" -ne 0 ] && [ "${same_line}" -eq 0 ]
137            then
138                echo ","
139            fi
140            if [ "${same_line}" -ne 0 ]
141            then
142                echo -n "${arg}\""
143                same_line=0
144            elif [ "${arg}" = "-iquote" ] || [ "${arg}" = "-I" ]
145            then
146                # cppcheck doesn't understand -iquote, substitute with -I
147                echo -n "            \"-I"
148                same_line=1
149            else
150                echo -n "            \"${arg}\""
151            fi
152            arg_num=$(( arg_num + 1 ))
153        done
154        echo ""
155        echo "        ],"
156        echo "        \"directory\": \"$(pwd -P)\","
157        echo "        \"file\": \"${CC_FILE}\""
158        echo "    }"
159        echo "]"
160    } > "${JDB_FILE}"
161}
162
163
164# Execute compiler with forwarded flags
165# Shellcheck complains about missing quotes on FORWARD_FLAGS, but they can't be
166# used here
167# shellcheck disable=SC2086
168${COMPILER} ${FORWARD_FLAGS}
169
170if [ -n "${CC_FILE}" ];
171then
172    for path in ${IGNORE_PATH_LIST}
173    do
174        if [[ ${CC_FILE} == *${path}* ]]
175        then
176            IGNORE_PATH="y"
177            echo "${0}: ${CC_FILE} ignored by --ignore-path matching *${path}*"
178        fi
179    done
180    if [ "${IGNORE_PATH}" = "n" ]
181    then
182        JDB_FILE="${OBJTREE_PATH}/${OBJ_FILE}.json"
183
184        # Prepare the Json Compilation Database for the file
185        create_jcd "${COMPILER} ${FORWARD_FLAGS}"
186
187        out_file="${OBJTREE_PATH}/${OBJ_FILE}.cppcheck.txt"
188
189        # Select the right target platform, ARCH is generated from Xen Makefile
190        case ${ARCH} in
191            arm64)
192                # arm64 has efi code compiled with -fshort-wchar
193                platform="${CPPCHECK_PLAT_PATH}/arm64-wchar_t2.xml"
194                ;;
195            arm32)
196                # arm32 has no efi code
197                platform="${CPPCHECK_PLAT_PATH}/arm32-wchar_t4.xml"
198                ;;
199            x86_64)
200                # x86_64 has efi code compiled with -fshort-wchar
201                platform="${CPPCHECK_PLAT_PATH}/x86_64-wchar_t2.xml"
202                ;;
203            *)
204                echo "ARCH: ${ARCH} not expected!"
205                exit 1
206                ;;
207        esac
208
209        if [ ! -f "${platform}" ]
210        then
211            echo "${platform} not found!"
212            exit 1
213        fi
214
215        # Generate build directory for the analysed file
216        cppcheck_build_dir="${BUILD_DIR}/${OBJTREE_PATH}/${OBJ_FILE}"
217        mkdir -p "${cppcheck_build_dir}"
218
219        # Shellcheck complains about missing quotes on CPPCHECK_TOOL_ARGS, but
220        # they can't be used here
221        # shellcheck disable=SC2086
222        ${CPPCHECK_TOOL} ${CPPCHECK_TOOL_ARGS} \
223            --project="${JDB_FILE}" \
224            --output-file="${out_file}" \
225            --platform="${platform}" \
226            --cppcheck-build-dir=${cppcheck_build_dir}
227
228        if [ "${CPPCHECK_HTML}" = "y" ]
229        then
230            # Shellcheck complains about missing quotes on CPPCHECK_TOOL_ARGS,
231            # but they can't be used here
232            # shellcheck disable=SC2086
233            ${CPPCHECK_TOOL} ${CPPCHECK_TOOL_ARGS} \
234                --project="${JDB_FILE}" \
235                --output-file="${out_file%.txt}.xml" \
236                --platform="${platform}" \
237                --cppcheck-build-dir=${cppcheck_build_dir} \
238                -q \
239                --xml
240        fi
241    fi
242fi
243