
Im PVS-Studio-Analyzer für die Programmiersprachen C und C++ unter Linux und macOS, ab Version 7.04, gibt es jetzt eine Testfunktion zur Überprüfung der angegebenen Dateiliste. Mit dem neuen Modus kann der Analyzer so konfiguriert werden, dass er Commits und Pull-Requests überprüft. In diesem Artikel wird erklärt, wie die Prüfung der Liste der geänderten Dateien eines GitHub-Projekts in beliebten CI-Systemen wie Travis CI, Buddy und AppVeyor eingerichtet wird.
Modus zur Überprüfung einer Dateiliste
— ist ein Werkzeug zur Erkennung von Fehlern und potenziellen Sicherheitsanfälligkeiten im Quellcode von Programmen, die in den Sprachen C, C++, C# und Java geschrieben sind. Es funktioniert in 64-Bit-Systemen unter Windows, Linux und macOS.
In der Version PVS-Studio 7.04 für Linux und macOS gibt es einen Modus zur Überprüfung der Liste der Quellcodedateien. Dies funktioniert für Projekte, deren Build-System es ermöglicht, eine Datei zu generieren . Diese Datei ist notwendig, damit der Analyzer Informationen über die Kompilierung der angegebenen Dateien extrahieren kann. Wenn Ihr Build-System die Generierung einer Datei compile_commands.json nicht unterstützt, können Sie versuchen, eine solche Datei mit dem Dienstprogramm .
Der Überprüfungsmodus für die Dateiliste kann zusammen mit dem Trace-Log des strace-Compilers (pvs-studio-analyzer trace) verwendet werden. Dazu müssen Sie zunächst einen vollständigen Build des Projekts durchführen und diesen nachverfolgen, damit der Analyzer umfassende Informationen zu den Kompilierungsparametern aller überprüften Dateien sammeln kann.
Dieser Ansatz hat jedoch einen wesentlichen Nachteil: Entweder müssen Sie bei jedem Start eine vollständige Nachverfolgung des gesamten Projekts durchführen, was der Idee einer schnellen Überprüfung von Commits widerspricht, oder, falls das Ergebnis der Nachverfolgung zwischengespeichert wird, könnten nachfolgende Ausführungen des Analyzers unvollständig sein, wenn sich die Struktur der Abhängigkeiten der Quell-Dateien ändert (z. B. wenn eine neue #include-Anweisung zu einer der Quell-Dateien hinzugefügt wird).
Daher empfehlen wir nicht, den Überprüfungsmodus für die Dateiliste mit dem Trace-Log zur Überprüfung von Commits oder Pull Requests zu verwenden. Falls Sie inkrementelle Builds bei der Überprüfung von Commits durchführen können, ziehen Sie in Betracht, den Modus .
Die Liste der Quelldateien für die Analyse wird in einer Textdatei gespeichert und dem Analyzer über den Parameter übergeben. -S:
pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txtIn dieser Datei werden relative oder absolute Pfade zu den Dateien angegeben, wobei jede neue Datei in einer neuen Zeile stehen muss. Es ist zulässig, nicht nur Dateinamen zur Analyse anzugeben, sondern auch verschiedenen Text. Der Analyzer wird erkennen, dass dies keine Datei ist, und die Zeile ignorieren. Dies kann nützlich sein, um Kommentare zu hinterlassen, wenn die Dateien manuell angegeben werden. Oft wird jedoch die Liste der Dateien während der Analyse in CI generiert, beispielsweise können dies Dateien aus einem Commit oder einem Pull-Request sein.
Jetzt kann mit diesem Modus schnell neuer Code überprüft werden, bevor er in den Hauptentwicklungszweig aufgenommen wird. Damit das Überprüfungssystem auf Warnungen des Analyzers reagiert, muss in das Tool plog-converter Flag hinzugefügt —indicate-warnings:
plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...Mit diesem Flag wird der Konverter einen ungleich null Rückgabewert zurückgeben, wenn im Analysebericht Warnungen vorhanden sind. Am Rückgabecode kann der Pre-Commit-Hook, der Commit oder der Pull Request blockiert werden, während der generierte Analysebericht auf dem Bildschirm angezeigt, geteilt oder per E-Mail versendet werden kann.
Hinweis. Bei der ersten Ausführung wird die gesamte Projektdateiliste analysiert, da der Analysator eine Abhängigkeitdatei für die Quellcodedateien im Projekt im Hinblick auf die Header-Dateien generieren muss. Dies ist eine Besonderheit der Analyse von C- und C++-Dateien. In Zukunft kann die Abhängigkeitdatei zwischengespeichert werden und der Analysator wird sie automatisch aktualisieren. Der Vorteil einer Überprüfung der Commits im Vergleich zur Verwendung des inkrementellen Analysemodus besteht darin, dass nur diese Datei und nicht die Objektdateien zwischengespeichert werden müssen.
Allgemeine Prinzipien der Analyse von Pull-Requests
Die Analyse des gesamten Projekts nimmt recht viel Zeit in Anspruch, sodass es sinnvoll ist, nur einen Teil davon zu überprüfen. Das Problem besteht darin, neue Dateien von den anderen Dateien im Projekt zu trennen.
Betrachten wir ein Beispiel eines Commit-Baums mit zwei Zweigen:

Stellen wir uns vor, dass der Commit A1 eine ausreichende Menge an Code enthält, die bereits überprüft wurde. Kürzlich haben wir einen Zweig von dem Commit A1 erstellt und einige Dateien geändert.
Sie haben sicherlich bemerkt, dass nach A1 noch zwei Commits vorgenommen wurden, aber auch diese waren Zusammenführungen anderer Zweige, schließlich committen wir nicht in master. Und nun ist der Zeitpunkt gekommen, an dem hotfix bereit ist. Daher wurde ein Pull Request zur Zusammenführung erstellt. B3 und A3.
Selbstverständlich wäre es möglich, das gesamte Ergebnis ihrer Zusammenführung zu überprüfen, aber das wäre zu langwierig und nicht gerechtfertigt, da nur einige Dateien geändert wurden. Deshalb ist es effektiver, nur die Änderungen zu analysieren.
Dazu erhalten wir die Differenz zwischen den Zweigen, während wir uns in dem HEAD-Zweig befinden, von dem wir in master zusammenführen möchten:
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list$MERGE_BASE darüber werden wir später detaillierter sprechen. Tatsache ist, dass nicht jeder CI-Service die notwendigen Informationen zur Merge-Basis bereitstellt, weshalb es jedes Mal notwendig ist, neue Wege zur Beschaffung dieser Daten zu finden. Dies wird weiter unten in jedem der beschriebenen Webdienste ausführlich dargestellt.
Wir haben also die Unterschiede zwischen den Branches erhalten, genauer gesagt – die Liste der geänderten Dateinamen. Nun müssen wir die Datei .pvs-pr.list (in die wir die Ausgabe oben umgeleitet haben) an den Analyzer übergeben:
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.listNach der Analyse müssen wir die Protokolldatei (PVS-Studio.log) in ein benutzerfreundliches Format konvertieren:
plog-converter -t errorfile PVS-Studio.log --cerr -wDieser Befehl gibt eine Liste von Fehlern in (dem Standardausgabestrom für Fehlermeldungen) aus.
Allerdings müssen wir nicht nur die Fehler ausgeben, sondern auch unserem Service für das Build- und Testverfahren über die existierenden Probleme informieren. Dazu wurde dem Konverter das Flag -W (—indicate-warnings) hinzugefügt. Bei Vorliegen auch nur einer Warnung des Analyzers ändert sich der Rückgabewert des Tools plog-converter auf 2, was dem CI-Service signalisiert, dass potenzielle Fehler in den Dateien des Pull Requests vorhanden sind.
Travis CI
Die Konfiguration erfolgt in Form einer Datei .travis.yml. Zur Vereinfachung empfehle ich, alles in ein separates Bash-Skript mit Funktionen auszulagern, die aus der Datei aufgerufen werden. .travis.yml (bash script_name.sh function_name).
Wir werden den notwendigen Code in das Skript unter bashhinzufügen, damit wir mehr Funktionalität erhalten. Im Abschnitt install werden wir Folgendes schreiben:
install:
- bash .travis.sh travis_installWenn Sie Anweisungen hatten, können Sie diese in das Skript übertragen und die Bindestriche entfernen.
Öffnen wir die Datei .travis.sh und fügen wir die Installation des Analysators in die Funktion ein travis_install():
travis_install() {
wget -q -O - https://files.viva64.com/etc/pubkey.txt
| sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
sudo apt-get update -qq
sudo apt-get install -qq pvs-studio
}Jetzt fügen wir in den Abschnitt ein script Analyse starten:
script:
- bash .travis.sh travis_scriptUnd im Bash-Skript:
travis_script() {
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
git diff --name-only origin/HEAD > .pvs-pr.list
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.list
--disableLicenseExpirationCheck
else
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
fi
plog-converter -t errorfile PVS-Studio.log --cerr -w
}Dieser Code muss nach dem Bau des Projekts ausgeführt werden, zum Beispiel, wenn Sie ein CMake-Bau hatten:
travis_script() {
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
cmake $CMAKE_ARGS CMakeLists.txt
make -j8
}Es wird so aussehen:
travis_script() {
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
cmake $CMAKE_ARGS CMakeLists.txt
make -j8
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
git diff --name-only origin/HEAD > .pvs-pr.list
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.list
--disableLicenseExpirationCheck
else
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
fi
plog-converter -t errorfile PVS-Studio.log --cerr -w
}Vielleicht ist Ihnen bereits die genannten Umgebungsvariablen aufgefallen. $TRAVIS_PULL_REQUEST und $TRAVIS_BRANCH. Travis CI deklariert diese selbst:
- $TRAVIS_PULL_REQUEST speichert die Nummer des Pull Requests oder false, wenn es sich um einen regulären Branch handelt;
- $TRAVIS_REPO_SLUG speichert den Namen des Projekt-Repositorys.
Der Ablauf dieser Funktion lautet:

Travis CI reagiert auf Rückgabewerte, deshalb wird das Vorhandensein von Warnungen dazu führen, dass der Service den Commit als fehlerhaft markiert.
Schauen wir uns nun diese Zeile Code genauer an:
git diff --name-only origin/HEAD > .pvs-pr.listDas Problem ist, dass Travis CI während der Analyse von Pull Requests automatisch Branches zusammenführt:

Daher analysieren wir A4, und nicht B3->A3. Aufgrund dieser Besonderheit müssen wir die Differenz zu A3, das genau die Spitze des Branches ist. origin.
Es gibt noch ein wichtiges Detail — das Caching von Abhängigkeiten der Headerdateien für die zu kompilierenden Übersetzungseinheiten (*.c, *.cc, *.cpp usw.). Diese Abhängigkeiten berechnet der Analyzer beim ersten Start im Prüfmodus der Dateiliste und speichert sie dann im Verzeichnis .PVS-Studio. Travis CI ermöglicht das Caching von Ordnern, daher speichern wir die Daten dieses Verzeichnisses. .PVS-Studio/:
cache:
directories:
- .PVS-Studio/Dieser Code muss in die Datei hinzugefügt werden. .travis.ymlDieses Verzeichnis speichert verschiedene Daten, die nach der Analyse gesammelt wurden und die nachfolgenden Analysen der Dateiliste oder die inkrementelle Analyse erheblich beschleunigen. Wenn dies nicht erfolgt, wird der Analyzer in der Tat jedes Mal alle Dateien analysieren.
Buddy
Wie Travis CI bietet die Möglichkeit zur automatisierten Erstellung und Testung von Projekten, die in GitHub gespeichert sind. Im Gegensatz zu Travis CI wird es über die Web-Oberfläche konfiguriert (Bash-Unterstützung ist vorhanden), sodass es nicht erforderlich ist, Konfigurationsdateien im Projekt zu speichern.
Zunächst müssen wir eine neue Aktion in die Build-Pipeline hinzufügen:

Geben wir den Compiler an, der zum Erstellen des Projekts verwendet wurde. Beachten Sie den Docker-Container, der in dieser Aktion installiert ist. Für GCC gibt es beispielsweise einen speziellen Container:

Jetzt installieren wir PVS-Studio und die erforderlichen Utilities:

Fügen Sie im Editor die folgenden Zeilen hinzu:
apt-get update && apt-get -y install wget gnupg jq
wget -q -O - https://files.viva64.com/etc/pubkey.txt | apt-key add -
wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
apt-get update && apt-get -y install pvs-studioJetzt wechseln wir zur Registerkarte Run (erstes Symbol) und fügen im entsprechenden Feld des Editors den folgenden Code hinzu:
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY
if [ "$BUDDY_EXECUTION_PULL_REQUEST_NO" != '' ]; then
PULL_REQUEST_ID="pulls/$BUDDY_EXECUTION_PULL_REQUEST_NO"
MERGE_BASE=`wget -qO -
https://api.github.com/repos/${BUDDY_REPO_SLUG}/${PULL_REQUEST_ID}
| jq -r ".base.ref"`
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
-S .pvs-pr.list
else
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
fi
plog-converter -t errorfile PVS-Studio.log --cerr -wWenn Sie den Abschnitt zu Travs-CI gelesen haben, wird Ihnen dieser Code bereits vertraut sein. Nun gibt es jedoch eine neue Phase:

Es geht darum, dass wir jetzt nicht das Ergebnis des Merge analysieren, sondern den HEAD-Branch, von dem der Pull Request stammt:

Daher befinden wir uns in einem bedingten Commit B3 und müssen die Unterschiede mit A3:
PULL_REQUEST_ID="pulls/$BUDDY_EXECUTION_PULL_REQUEST_NO"
MERGE_BASE=`wget -qO -
https://api.github.com/repos/${BUDDY_REPO_SLUG}/${PULL_REQUEST_ID}
| jq -r ".base.ref"`
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.listFür die Bestimmung A3 nutzen wir die GitHub API:
https://api.github.com/repos/${USERNAME}/${REPO}/pulls/${PULL_REQUEST_ID}Wir haben die folgenden Variablen verwendet, die Buddy bereitstellt:
- $BUDDY_EXECUTION_PULL_REQEUST_NO — die Nummer des Pull Requests;
- $BUDDY_REPO_SLUG — die Kombination aus Benutzername und Repository (zum Beispiel max/test).
Jetzt speichern wir die Änderungen, indem wir die Schaltfläche unten verwenden, und aktivieren die Analyse des Pull Requests:

Im Gegensatz zu Travis CI müssen wir nicht angeben .pvs-studio für das Caching, da Buddy alle Dateien automatisch für nachfolgende Ausführungen cached. Daher bleibt nur noch, die Anmeldeinformationen für PVS-Studio in Buddy zu speichern. Nach dem Speichern der Änderungen gelangen wir zurück in das Pipeline. Wir müssen zu den Variablen-Einstellungen gehen und den Benutzernamen sowie den Schlüssel für PVS-Studio hinzufügen:

Danach wird das Erscheinen eines neuen Pull Requests oder Commits die Überprüfung auslösen. Wenn der Commit Fehler enthält, wird Buddy dies auf der Seite des Pull Requests anzeigen.
AppVeyor
Die Einrichtung von AppVeyor ähnelt Buddy, da alles über die Weboberfläche erfolgt und es nicht erforderlich ist, eine *.yml-Datei in das Projekt-Repository hinzuzufügen.
Lassen Sie uns zum Tab Einstellungen in der Projektübersicht gehen:

Scrollen wir diese Seite nach unten und aktivieren wir das Caching für das Erstellen von Pull-Requests:

Jetzt gehen wir zum Tab Umgebung, wo wir das Bild für den Build und die erforderlichen Umgebungsvariablen angeben:

Wenn Sie die vorherigen Abschnitte gelesen haben, sind Sie mit diesen beiden Variablen gut vertraut — PVS_KEY und PVS_USERNAME. Wenn nicht, erinnere ich daran, dass sie zur Überprüfung der Lizenz des PVS-Studio-Analyzers erforderlich sind. Später werden wir ihnen in Bash-Skripten begegnen.
Am unteren Ende dieser Seite geben wir den Ordner für das Caching an:

Wenn wir das nicht tun, analysieren wir statt einiger Dateien das gesamte Projekt, aber die Ausgabe erfolgt für die angegebenen Dateien. Daher ist es wichtig, den richtigen Verzeichnisnamen einzugeben.
Jetzt ist es Zeit für das Skript zur Überprüfung. Öffnen wir den Tab Tests und wählen wir Script:

In dieses Formular muss der folgende Code eingefügt werden:
sudo apt-get update && sudo apt-get -y install jq
wget -q -O - https://files.viva64.com/etc/pubkey.txt
| sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
sudo apt-get update && sudo apt-get -y install pvs-studio
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY
PWD=$(pwd -L)
if [ "$APPVEYOR_PULL_REQUEST_NUMBER" != '' ]; then
PULL_REQUEST_ID="pulls/$APPVEYOR_PULL_REQUEST_NUMBER"
MERGE_BASE=`wget -qO -
https://api.github.com/repos/${APPVEYOR_REPO_NAME}/${PULL_REQUEST_ID}
| jq -r ".base.ref"`
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
--dump-files --dump-log pvs-dump.log
-S .pvs-pr.list
else
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
fi
plog-converter -t errorfile PVS-Studio.log --cerr -wLassen Sie uns auf den folgenden Teil des Codes achten:
PWD=$(pwd -L)
if [ "$APPVEYOR_PULL_REQUEST_NUMBER" != '' ]; then
PULL_REQUEST_ID="pulls/$APPVEYOR_PULL_REQUEST_NUMBER"
MERGE_BASE=`wget -qO -
https://api.github.com/repos/${APPVEYOR_REPO_NAME}/${PULL_REQUEST_ID}
| jq -r ".base.ref"`
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
--dump-files --dump-log pvs-dump.log
-S .pvs-pr.list
else
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
fiDie spezifische Zuweisung des Wertes des Befehls pwd an eine Variable, die diesen Wert standardmäßig speichern soll, mag auf den ersten Blick seltsam erscheinen, aber ich werde es jetzt erklären.
Während der Konfiguration des Analysetools in AppVeyor stieß ich auf ein sehr merkwürdiges Verhalten des Analysetools. Einerseits funktionierte alles korrekt, aber die Analyse wurde nicht gestartet. Ich verbrachte viel Zeit damit, zu erkennen, dass wir uns im Verzeichnis /home/appveyor/projects/testcalc/ befinden, während das Analysetool der Meinung war, wir wären in /opt/appveyor/build-agent/. Da wurde mir klar, dass die Variable $PWD etwas schummelt. Aus diesem Grund habe ich ihren Wert vor dem Start der Analyse manuell aktualisiert.
Und dann läuft alles wie gewohnt weiter:

Betrachten wir nun den folgenden Abschnitt:
PULL_REQUEST_ID="pulls/$APPVEYOR_PULL_REQUEST_NUMBER"
MERGE_BASE=`wget -qO -
https://api.github.com/repos/${APPVEYOR_REPO_NAME}/${PULL_REQUEST_ID}
| jq -r ".base.ref"`Hier erhalten wir die Unterschiede zwischen den Branches, für die ein Pull Request erstellt wurde. Dafür benötigen wir folgende Umgebungsvariablen:
- $APPVEYOR_PULL_REQUEST_NUMBER — die Nummer des Pull Requests;
- $APPVEYOR_REPO_NAME — der Benutzername und das Repository des Projekts.
Fazit
Natürlich haben wir nicht alle möglichen Continuous Integration-Dienste behandelt, aber sie weisen alle eine sehr ähnliche Arbeitsweise auf. Abgesehen vom Caching entwickelt jeder Dienst sein eigenes "Fahrrad", weshalb alles immer unterschiedlich ist.
An einigen Stellen, wie bei Travis-CI, funktionieren ein paar Codezeilen und das Caching einwandfrei; an anderen, wie bei AppVeyor, müssen Sie einfach nur den Ordner in den Einstellungen angeben; und an wieder anderen müssen Sie einzigartige Schlüssel erstellen und versuchen, das System zu überzeugen, Ihnen die Möglichkeit zu geben, den zwischengespeicherten Abschnitt zu überschreiben. Daher sollten Sie, wenn Sie die Analyse von Pull-Requests in einem Continuous Integration-Dienst einrichten möchten, der oben nicht behandelt wurde, zunächst sicherstellen, dass es keine Probleme mit dem Caching gibt.
Vielen Dank für Ihre Aufmerksamkeit. Wenn etwas nicht funktioniert, schreiben Sie uns gerne an . Wir helfen Ihnen weiter.
Wenn Sie diesen Artikel mit einem englischsprachigen Publikum teilen möchten, verwenden Sie bitte den Link zur Übersetzung: Maxim Zvyagintsev. .
Quelle: habr.com
