diff --git a/run-obmc-cppcheck.sh b/run-obmc-cppcheck.sh new file mode 100755 index 0000000..17b8030 --- /dev/null +++ b/run-obmc-cppcheck.sh @@ -0,0 +1,114 @@ +#!/bin/bash +############################################################################### +# +# This script is for running the cppcheck for obmc-phosphor-image +# +############################################################################### +# +# Required Inputs: +# WORKSPACE: Directory which contains the extracted openbmc-build-scripts +# directory +# REPOLIST: List of repos to do the cppcheck. + +usage() +{ + echo "usage: run-obmc-cppcheck.sh " + echo "Example:" + echo 'WORKSPACE=$(pwd) openbmc-build-scripts/run-obmc-cppcheck.sh ' + echo "" +} + +# Default variables +WORKSPACE=${WORKSPACE:-$(pwd)} +REPOLIST=${REPOLIST:-""} +OBMC_BUILD_SCRIPTS="openbmc-build-scripts" + +# +REPOLIST=$(echo $REPOLIST) +# + +# Check workspace, build scripts, and package to be unit tested exists +if [ ! -d "${WORKSPACE}" ]; then + echo "Workspace(${WORKSPACE}) doesn't exist, exiting..." + exit 1 +fi +if [ ! -d "${WORKSPACE}/${OBMC_BUILD_SCRIPTS}" ]; then + echo "Package(${OBMC_BUILD_SCRIPTS}) not found in ${WORKSPACE}, exiting..." + exit 1 +fi + +## + +# run cppcheck +function run_cppcheck() +{ + cd ${WORKSPACE} + for REPONAME in ${REPOLIST} + do + UNIT_TEST_PKG=$REPONAME CPPCHECK_ONLY=1 ./openbmc-build-scripts/run-unit-test-docker.sh + done +} + + +### MAIN #### + + +# MACHINE +MACHINE=${MACHINE:-p10bmc} +export MACHINE + +# environment variables +# +# List of Repos to scan. +REPOLIST=${REPOLIST:-""} + +# Check the repolist config file +REPOLIST_FILE=${WORKSPACE}/obmc-cppcheck-repolist.txt +[[ -z "${REPOLIST}" && -f "${REPOLIST_FILE}" ]] && REPOLIST=$(cat ${REPOLIST_FILE}) + +# Use default repos if not passed +if [ -z "${REPOLIST}" ] +then + REPOLIST=" + bmcweb \ + dbus-sensors \ + entity-manager \ + ibm-acf \ + ipl \ + libpldm \ + obmc-console \ + openpower-debug-collector \ + openpower-hw-diags \ + openpower-hw-isolation \ + openpower-occ-control \ + openpower-vpd-parser \ + panel \ + phosphor-bmc-code-mgmt \ + phosphor-certificate-manager \ + phosphor-dbus-interfaces \ + phosphor-debug-collector \ + phosphor-host-ipmid \ + phosphor-inventory-manager \ + phosphor-led-manager \ + phosphor-logging \ + phosphor-power \ + phosphor-state-manager \ + phosphor-user-manager \ + pldm \ + powervm-handler \ + service-config-manager \ + telemetry \ + " +fi + +echo "usage: run-obmc-cppcheck.sh" + + +# +cd ${WORKSPACE}/openbmc +. setup ${MACHINE} + +cd ${WORKSPACE} +run_cppcheck + +exit 0 diff --git a/run-unit-test-docker.sh b/run-unit-test-docker.sh index 5ac7a67..60d15c9 100755 --- a/run-unit-test-docker.sh +++ b/run-unit-test-docker.sh @@ -28,6 +28,10 @@ # EXTRA_UNIT_TEST_ARGS: Optional, pass arguments to unit-test.py # INTERACTIVE: Optional, run a bash shell instead of unit-test.py # http_proxy: Optional, run the container with proxy environment +# +# CPPCHECK_ONLY: Optional, run cppcheck only and skip all other tests +# FAIL_ON_CPPCHECK_ERROR=[0|1]: Optional, no ci failure even if the cppcheck errors by default +# # Trace bash processing. Set -e so when a step fails, we fail the build set -uo pipefail @@ -47,6 +51,10 @@ NO_CPPCHECK="${NO_CPPCHECK:-}" INTERACTIVE="${INTERACTIVE:-}" http_proxy=${http_proxy:-} +CPPCHECK_ONLY="${CPPCHECK_ONLY:-}" +# Ignore cppcheck error by default +FAIL_ON_CPPCHECK_ERROR="${FAIL_ON_CPPCHECK_ERROR:-""}" + # Timestamp for job echo "Unit test build started, $(date)" @@ -76,6 +84,8 @@ export DOCKER_IMG_NAME # Allow the user to pass options through to unit-test.py: # EXTRA_UNIT_TEST_ARGS="-r 100" ... EXTRA_UNIT_TEST_ARGS="${EXTRA_UNIT_TEST_ARGS:+,${EXTRA_UNIT_TEST_ARGS/ /,}}" +EXTRA_UNIT_TEST_ARGS+=${CPPCHECK_ONLY:+,--cppcheck-only} +[ "${FAIL_ON_CPPCHECK_ERROR}" == "1" ] && EXTRA_UNIT_TEST_ARGS+=",--fail-on-cppcheck-error" # Unit test and parameters if [ "${INTERACTIVE}" ]; then diff --git a/scripts/run-cppcheck.sh b/scripts/run-cppcheck.sh new file mode 100755 index 0000000..2258e63 --- /dev/null +++ b/scripts/run-cppcheck.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# +# Test for cppcheck filter +# Required Inputs: +# WORKSPACE: Directory which contains the extracted openbmc-build-scripts +# directory +# REPONAME: repo to run cppcheck. If not, use UNIT_TEST_PKG +# LOGDIR: Optional, log output dir. If not, WORKSPACE/logs/ +# REPORTDIR: Optional, report output dir, If not, use WORKSPACE/reports/ + +set -uo pipefail + +args=$* + +[ -z "${UNIT_TEST_PKG}" ] && ( echo "UNIT_TEST_PKG is not set"; exit 1) + +WORKSPACE=${WORKSPACE:-$(pwd)} +REPONAME=${REPONAME:-${UNIT_TEST_PKG}} +LOGDIR=${LOGDIR:-${WORKSPACE}/logs} +REPORTDIR=${REPORTDIR:-${WORKSPACE}/reports} +mkdir -p ${LOGDIR} +mkdir -p ${REPORTDIR} + +## Note: Additional necessary opts +CPPCHECK_ADD_OPTS=-D"__cppcheck__=1" + +CPPCHECK_STDOUT=${LOGDIR}/cppchk.${REPONAME}.stdout +CPPCHECK_STDERR=${LOGDIR}/cppchk.${REPONAME}.stderr +cppcheck ${CPPCHECK_ADD_OPTS} --template="{file}:{line}:{column}: {id}: {severity}: {message}" $args 2> ${CPPCHECK_STDERR} | tee ${CPPCHECK_STDOUT} + +CPPCHECK_ERR_LOG=${REPORTDIR}/cppchk.${REPONAME}.error.log + +FAILED=0 + +rm -f ${CPPCHECK_ERR_LOG} +while read -r filelinecol id severity remaining; do + if [[ "$filelinecol" == "/"* || "$filelinecol" == "subprojects/"* ]] + then + # ignore the absolute-full-file paths like /usr/include/*, or under subprojects + continue + fi + if [[ "$severity" == "warning:" || "$severity" == "error:" ]] + then + echo "Filename:Line:Column: $filelinecol" >> ${CPPCHECK_ERR_LOG} + echo "id:severity: $id $severity" >> ${CPPCHECK_ERR_LOG} + echo "remaining: $remaining" >> ${CPPCHECK_ERR_LOG} + echo "---" >> ${CPPCHECK_ERR_LOG} + FAILED=1 + fi +done < ${CPPCHECK_STDERR} + +if [[ $FAILED -ne 0 ]] +then + echo "cppcheck ($UNIT_TEST_PKG) failed, CPPCHECK_ERR_LOG=$CPPCHECK_ERR_LOG" + cat ${CPPCHECK_ERR_LOG} + exit 1 +fi diff --git a/scripts/unit-test.py b/scripts/unit-test.py index c8c8687..d3dc2a8 100755 --- a/scripts/unit-test.py +++ b/scripts/unit-test.py @@ -379,11 +379,17 @@ def run_cppcheck(): ): return None + cppchkenv = os.environ.copy() + cppchkenv["UNIT_TEST_PKG"] = UNIT_TEST_PKG + cppchkenv["WORKSPACE"] = WORKSPACE + with TemporaryDirectory() as cpp_dir: # http://cppcheck.sourceforge.net/manual.pdf try: check_call_cmd( - "cppcheck", + os.path.join( + WORKSPACE, "openbmc-build-scripts", "scripts", "run-cppcheck.sh" + ), "-j", str(multiprocessing.cpu_count()), "--enable=style,performance,portability,missingInclude", @@ -396,9 +402,12 @@ def run_cppcheck(): "--library=googletest", "--project=build/compile_commands.json", f"--cppcheck-build-dir={cpp_dir}", + env=cppchkenv, ) except subprocess.CalledProcessError: print("cppcheck found errors") + if FAIL_ON_CPPCHECK_ERROR: + raise Exception("cppcheck found errors") def is_valgrind_safe(): @@ -515,6 +524,8 @@ def maybe_make_coverage(): via `make check-code-coverage`. If the package does not have code coverage testing then it just skips over this. """ + if CPPCHECK_ONLY: + return if not make_target_exists("check-code-coverage"): return @@ -722,13 +733,19 @@ def configure(self, build_for_testing): check_call_cmd("./configure", *conf_flags) def build(self): + if CPPCHECK_ONLY: + return check_call_cmd(*make_parallel) def install(self): + if CPPCHECK_ONLY: + return check_call_cmd("sudo", "-n", "--", *(make_parallel + ["install"])) check_call_cmd("sudo", "-n", "--", "ldconfig") def test(self): + if CPPCHECK_ONLY: + return try: cmd = make_parallel + ["check"] for i in range(0, args.repeat): @@ -758,6 +775,8 @@ def dependencies(self): return [] def configure(self, build_for_testing): + if CPPCHECK_ONLY: + return self.build_for_testing = build_for_testing if INTEGRATION_TEST: check_call_cmd( @@ -776,6 +795,8 @@ def configure(self, build_for_testing): ) def build(self): + if CPPCHECK_ONLY: + return check_call_cmd( "cmake", "--build", @@ -786,14 +807,21 @@ def build(self): ) def install(self): + if CPPCHECK_ONLY: + return check_call_cmd("sudo", "cmake", "--install", ".") check_call_cmd("sudo", "-n", "--", "ldconfig") def test(self): + if CPPCHECK_ONLY: + return if make_target_exists("test"): check_call_cmd("ctest", ".") def analyze(self): + if CPPCHECK_ONLY: + run_cppcheck() + return if os.path.isfile(".clang-tidy"): with TemporaryDirectory(prefix="build", dir=".") as build_dir: # clang-tidy needs to run on a clang-specific build @@ -959,6 +987,7 @@ def get_configure_flags(self, build_for_testing): return meson_flags def configure(self, build_for_testing): + # Note - configure is still needed for CPPCHECK_ONLY meson_flags = self.get_configure_flags(build_for_testing) try: check_call_cmd( @@ -971,13 +1000,19 @@ def configure(self, build_for_testing): self.package = Meson._project_name("build") def build(self): + if CPPCHECK_ONLY: + return check_call_cmd("ninja", "-C", "build") def install(self): + if CPPCHECK_ONLY: + return check_call_cmd("sudo", "-n", "--", "ninja", "-C", "build", "install") check_call_cmd("sudo", "-n", "--", "ldconfig") def test(self): + if CPPCHECK_ONLY: + return # It is useful to check various settings of the meson.build file # for compatibility, such as meson_version checks. We shouldn't # do this in the configure path though because it affects subprojects @@ -1057,6 +1092,9 @@ def _maybe_valgrind(self): raise Exception("Valgrind tests failed") def analyze(self): + if CPPCHECK_ONLY: + run_cppcheck() + return self._maybe_valgrind() # Run clang-tidy only if the project has a configuration @@ -1345,6 +1383,22 @@ def find_file(filename, basedir): default=False, help="Do not run cppcheck", ) + parser.add_argument( + "--fail-on-cppcheck-error", + dest="FAIL_ON_CPPCHECK_ERROR", + action="store_true", + required=False, + default=False, # No CI failure even if cppcheck error by default + help="Igonore the cppcheck errors", + ) + parser.add_argument( + "--cppcheck-only", + dest="CPPCHECK_ONLY", + action="store_true", + required=False, + default=False, + help="Only run cppcheck and skip all other tests", + ) arg_inttests = parser.add_mutually_exclusive_group() arg_inttests.add_argument( "--integration-tests", @@ -1390,6 +1444,8 @@ def find_file(filename, basedir): WORKSPACE = args.WORKSPACE UNIT_TEST_PKG = args.PACKAGE TEST_ONLY = args.TEST_ONLY + CPPCHECK_ONLY = args.CPPCHECK_ONLY + FAIL_ON_CPPCHECK_ERROR = args.FAIL_ON_CPPCHECK_ERROR NO_CPPCHECK = args.NO_CPPCHECK INTEGRATION_TEST = args.INTEGRATION_TEST BRANCH = args.BRANCH @@ -1409,7 +1465,7 @@ def printline(*line): CODE_SCAN_DIR = os.path.join(WORKSPACE, UNIT_TEST_PKG) # Run format-code.sh, which will in turn call any repo-level formatters. - if FORMAT_CODE: + if (not CPPCHECK_ONLY) and FORMAT_CODE: check_call_cmd( os.path.join( WORKSPACE, "openbmc-build-scripts", "scripts", "format-code.sh" @@ -1461,7 +1517,7 @@ def printline(*line): # Run any custom CI scripts the repo has, of which there can be # multiple of and anywhere in the repository. ci_scripts = find_file(["run-ci.sh", "run-ci"], CODE_SCAN_DIR) - if ci_scripts: + if (not CPPCHECK_ONLY) and ci_scripts: os.chdir(CODE_SCAN_DIR) for ci_script in ci_scripts: check_call_cmd(ci_script) diff --git a/setup-obmc-cppcheck.sh b/setup-obmc-cppcheck.sh new file mode 100755 index 0000000..213dde9 --- /dev/null +++ b/setup-obmc-cppcheck.sh @@ -0,0 +1,137 @@ +#!/bin/bash +############################################################################### +# +# This script is for preparing the cppcheck repo environment for obmc-phosphor-image +# +############################################################################### +# +# Required Inputs: +# WORKSPACE: Directory which contains the extracted openbmc-build-scripts +# directory +# BRANCH: Optional, branch or tag to build from each of the +# openbmc repositories. default is master, which will be +# used if input branch not provided or not found +# REPOLIST: List of repos to do the cppcheck. +# MACHINE: Type of system to run tests + +usage() +{ + echo "Usage: setup-obmc-cppcheck.sh" + echo "Example:" + echo 'WORKSPACE=$(pwd) BRANCH=fw1120.00-1.70 ./openbmc-build-scripts/setup-obmc-cppcheck.sh ' + echo "" +} + +# Default variables +BRANCH=${BRANCH:-""} +WORKSPACE=${WORKSPACE:-$(pwd)} +REPOLIST=${REPOLIST:-""} +OBMC_BUILD_SCRIPTS="openbmc-build-scripts" + +# +REPOLIST=$(echo $REPOLIST) +# +[ -z "$BRANCH" ] && (usage; exit 1) + +# Check workspace, build scripts, and package to be unit tested exists +if [ ! -d "${WORKSPACE}" ]; then + echo "Workspace(${WORKSPACE}) doesn't exist, exiting..." + exit 1 +fi +if [ ! -d "${WORKSPACE}/${OBMC_BUILD_SCRIPTS}" ]; then + echo "Package(${OBMC_BUILD_SCRIPTS}) not found in ${WORKSPACE}, exiting..." + exit 1 +fi + +## + +# Clone/Extract openbmc repo +function setup_openbmc_repos_for_cppcheck() +{ + cd ${WORKSPACE} + if [ ! -d ./openbmc ] + then + echo git clone git@github.ibm.com:openbmc/openbmc.git + git clone git@github.ibm.com:openbmc/openbmc.git + fi + + cd ${WORKSPACE}/openbmc + source setup ${MACHINE} + + ## fetch openbmc repo + git fetch --all + git checkout ${BRANCH} + git pull + + # walk-thru each repo & prepare the cppcheck + for REPONAME in ${REPOLIST} + do + echo "===== Extract repo $REPONAME and link to $WORKSPACE/$REPONAME =====" + bitbake ${REPONAME} -f -c do_fetch + bitbake ${REPONAME} -f -c do_unpack + + eval $(bitbake -e ${REPONAME} | grep ^WORKDIR=) + if [ -d ${WORKDIR}/git ] + then + echo "Linking ${WORKDIR}/git to $WORKSPACE/${REPONAME}" + ln -fs ${WORKDIR}/git $WORKSPACE/${REPONAME} + fi + done +} + +### MAIN #### + + +# MACHINE +MACHINE=${MACHINE:-p10bmc} +export MACHINE + +# environment variables +# +# List of Repos to scan. +REPOLIST=${REPOLIST:-""} + +# Check the repolist config file +REPOLIST_FILE=${WORKSPACE}/obmc-cppcheck-repolist.txt +[[ -z "${REPOLIST}" && -f "${REPOLIST_FILE}" ]] && REPOLIST=$(cat ${REPOLIST_FILE}) + +# Use default repos if not passed +if [ -z "${REPOLIST}" ] +then + REPOLIST=" + bmcweb \ + dbus-sensors \ + entity-manager \ + ibm-acf \ + ipl \ + libpldm \ + obmc-console \ + openpower-debug-collector \ + openpower-hw-diags \ + openpower-hw-isolation \ + openpower-occ-control \ + openpower-vpd-parser \ + panel \ + phosphor-bmc-code-mgmt \ + phosphor-certificate-manager \ + phosphor-dbus-interfaces \ + phosphor-debug-collector \ + phosphor-host-ipmid \ + phosphor-inventory-manager \ + phosphor-led-manager \ + phosphor-logging \ + phosphor-power \ + phosphor-state-manager \ + phosphor-user-manager \ + pldm \ + powervm-handler \ + service-config-manager \ + telemetry \ + " +fi + +echo "Usage: setup-obmc-cppcheck.sh for branch ${BRANCH}" + +setup_openbmc_repos_for_cppcheck + +exit 0