﻿# Автоматический статический анализ с помощью PVS\-Studio при сборке RPM\-пакетов

Возникла задача автоматизировать статический анализ пакетов в составе дистрибутива\. Наилучший инструмент для этого — PVS\-Studio, потому что умеет перехватывать вызовы компилятора с помощью strace, таким образом, не требуя никаких изменений в сборочных скриптах\. Сначала под наблюдением pvs\-studio\-analyzer запускается сборка, собирает лог, затем запускается анализатор этого лога и формируется отчет\. Рассмотрим, как такое настроить, избежав внесения правок в каждый пакет\.


> \_Эта статья впервые была опубликована \\\(на русском языке\\\) на сайте nixtux\\\.ru\\\. Статья размещена в нашем блоге и переведена с разрешения автора\\\.\_



В качестве операционной системы будем рассматривать rosa2019\.1 \([dnf \+ rpm4](http://wiki.rosalab.ru/ru/index.php/%D0%9F%D0%B5%D1%80%D0%B5%D1%85%D0%BE%D0%B4_ROSA_%D1%81_RPM_5_%D0%BD%D0%B0_RPM_4)\)\. Я использую сборки rootfs и systemd\-nspawn \([snr](https://github.com/mikhailnov/snr)\)\. Сначала установим необходимые для работы пакеты:

```cpp
dnf install git-core bash abf basesystem-build how-to-use-pvs-studio-free
```

С помощью утилиты [how\-to\-use\-pvs\-studio\-free](https://github.com/viva64/how-to-use-pvs-studio-free) нужно в исходники добавить заголовки PVS\-Studio\. Эти исходники затем будут отстрипаны от бинарников и попадут в debugsource\-подпакеты, которые публикуются в репозиторий\. Установим ее из репозитория:

```cpp
sudo dnf install how-to-use-pvs-studio-free
```

Ставим саму [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/download/):

```cpp
wget  https://files.viva64.com/pvs-studio-latest.rpm -O pvs-studio-latest.rpm
sudo dnf install pvs-studio-latest.rpm
```

Теперь суть в следующем\. Как известно, RPM превращает секцию спека _%prep_ в shell\-скрипт _/var/tmp/\*\.sh_ и запускает его, аналогично делает с секциями _%build_ и _%install_\. Мы сделаем обертку, которая будет запускать эти скрипты вместо _%\_buildshell \(/bin/sh\)_\.

Создаем файл _/usr/bin/pvs\-prep_ со следующим текстом:

```cpp
#!/bin/sh
set -eu
sh "$2"
( cd "$1"
how-to-use-pvs-studio-free -c 2 -m .
)
```

Этот скрипт будет на стадии _%prep_ добавлять шапку с рекламой PVS\-Studio в исходники\.

Создаем файл _/usr/bin/pvs\-builder_ со следующим текстом:

```cpp
#!/bin/bash
set -eu
name="$1"
script="$2"
test -f "$script"
[ -n "$2" ]
/usr/bin/pvs-studio-analyzer trace -- /bin/bash -e "$script"
mkdir -p "$HOME/pvs-logs/raw-logs"
pvs-studio-analyzer analyze -o "$HOME/pvs-logs/${name}" 
  -j "$(nproc)" #-C /usr/bin/clang
rm -fr "$HOME/pvs-logs/html-logs/${name}"
mkdir -p "$HOME/pvs-logs/html-logs/${name}"
plog-converter -a GA:1,2 -t fullhtml "$HOME/pvs-logs/${name}" 
  -o "$HOME/pvs-logs/html-logs/${name}"
mv -v "$HOME/pvs-logs/html-logs/${name}/fullhtml"/* 
  "$HOME/pvs-logs/html-logs/${name}/"
rm -fr "$HOME/pvs-logs/html-logs/${name}/fullhtml"
```

Этот скрипт будет запускать скрипт _%build_ под трассировщиком PVS\-Studio, а затем создавать отчет\.

Делаем их исполняемыми:

```cpp
chmod +x /usr/bin/pvs-builder /usr/bin/pvs-prep
```

Не используем /usr/local/bin, поскольку он не входит в $PATH в RPM\.

Теперь в файл _/etc/rpm/macros_ дописываем:

```cpp
%__spec_prep_cmd /usr/bin/pvs-prep "%{_sourcedir}"
%__spec_build_cmd /usr/bin/pvs-builder %{name}
```

\(см\. [https://github\.com/rpm\-software\-management/rpm/issues/1399](https://github.com/rpm-software-management/rpm/issues/1399)\)

И всё\. Каждая сборка проекта на C/C\+\+ будет создавать HTML\-отчет в папке $HOME/pvs\-logs/html\-logs/имя\_пакета\.

Чтобы не мучиться с mock, можно собирать список пакетов так:

```cpp
cat list_sec_sorted.txt | while read -r line; do pushd $line 
  && dnf builddep --allowerasing -y *.spec 
  && abf rpmbuild ; popd; done
```

т\. е\. ставить сборочные зависимости в ту же систему, но разрешать удалять лишние пакеты, если устанавливаемые зависимости текущего пакета конфликтуют с зависимостями одного из предыдущих пакетов\.