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