summaryrefslogtreecommitdiff
path: root/.claude/skills/pvs/run_pvs.sh
blob: ce2cfd7e7989780babe5339b438e66beefdbc801 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/usr/bin/env bash
# Run PVS-Studio static analysis on TinyUSB for a given BOARD.
#
# Implements the build + analyze flow behind CLAUDE.md "Static Analysis (PVS-Studio)":
#   - build all examples for BOARD with compile_commands.json exported
#   - run pvs-studio-analyzer against that compile DB using .PVS-Studio/.pvsconfig
#   - convert the log to human-readable (errorfile) and SARIF output
#
# Usage:
#   .claude/skills/pvs/run_pvs.sh <BOARD> [extra pvs-studio-analyzer args...]
#
# Examples:
#   .claude/skills/pvs/run_pvs.sh raspberry_pi_pico
#   # Scope to specific files via a plaintext list (one path per line):
#   printf 'src/tusb.c\nsrc/class/cdc/cdc_device.c\n' > /tmp/files.txt
#   .claude/skills/pvs/run_pvs.sh stm32f407disco -S /tmp/files.txt
set -euo pipefail

BOARD="${1:-}"
if [ -z "$BOARD" ]; then
  echo "Usage: $0 <BOARD> [extra pvs-studio-analyzer args...]" >&2
  echo "Example: $0 raspberry_pi_pico" >&2
  exit 2
fi
shift

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd "$REPO_ROOT"

BUILD_DIR="examples/cmake-build-${BOARD}"
COMPILE_DB="${BUILD_DIR}/compile_commands.json"
REPORT_LOG="pvs-${BOARD}.log"
SARIF_OUT="pvs-${BOARD}.sarif"
JOBS="$(nproc 2>/dev/null || echo 4)"

# --- License ---------------------------------------------------------------
# Use an existing license file, else materialize one from PVS_STUDIO_CREDENTIALS
# (format: "<name> <key>", as supplied by the CI secret of the same name).
if ! pvs-studio-analyzer lic-info >/dev/null 2>&1; then
  if [ -n "${PVS_STUDIO_CREDENTIALS:-}" ]; then
    echo ">>> Registering PVS-Studio license from PVS_STUDIO_CREDENTIALS"
    # Split "<name> <key>" into exactly two fields, quoted, so a key containing
    # glob chars or extra spaces can't be word-split/expanded.
    read -r _pvs_name _pvs_key <<< "$PVS_STUDIO_CREDENTIALS"
    pvs-studio-analyzer credentials "$_pvs_name" "$_pvs_key"
  else
    echo "ERROR: no PVS-Studio license found and PVS_STUDIO_CREDENTIALS is unset." >&2
    exit 1
  fi
fi

# --- Build (exports compile_commands.json) ---------------------------------
echo ">>> Building all examples for ${BOARD} (this also fetches the compile DB)"
cmake examples -B "${BUILD_DIR}" -G Ninja \
  -DBOARD="${BOARD}" \
  -DCMAKE_BUILD_TYPE=MinSizeRel \
  -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build "${BUILD_DIR}"

if [ ! -f "${COMPILE_DB}" ]; then
  echo "ERROR: ${COMPILE_DB} was not produced by the build." >&2
  exit 1
fi

# --- Analyze ---------------------------------------------------------------
echo ">>> Running PVS-Studio analyzer (-j${JOBS})"
# Note: --dump-files scatters .PVS-Studio.i/.cfg dump files across the source
# tree (only useful for debugging false positives). It is omitted here to keep
# the working tree clean; add it back via "$@" if needed.
pvs-studio-analyzer analyze \
  -f "${COMPILE_DB}" \
  -R .PVS-Studio/.pvsconfig \
  -o "${REPORT_LOG}" -j"${JOBS}" \
  --security-related-issues \
  --misra-c-version 2023 --misra-cpp-version 2008 --use-old-parser \
  "$@"

# --- Report ----------------------------------------------------------------
echo ">>> General-analysis + MISRA findings (errorfile):"
plog-converter -a GA:1,2 -t errorfile "${REPORT_LOG}" || true
plog-converter -t sarif -o "${SARIF_OUT}" "${REPORT_LOG}" >/dev/null

echo ">>> Done."
echo "    Raw log : ${REPORT_LOG}"
echo "    SARIF   : ${SARIF_OUT}"