Dieses Repo bietet ein einfaches Beispiel für die Einrichtung verschiedener CI-Dienste sowie die Integration von Analysetools in diese Dienste. Diese Tools sollten als Teil eines umfassenden Softwareentwicklungsprozesses (SDP) verwendet werden und können auch als Startvorlage für jede C- oder C++-Anwendung verwendet werden. Die folgenden CI-Tools kommen zum Einsatz und bieten Testunterstützung für Windows, Cygwin, Linux und macOS
Folgende Prüfungen werden durchgeführt:
Die folgenden realen Projekte nutzen eine Vielzahl dieser Techniken als Teil ihres SDP:
Obwohl dieses Repo auf den meisten Systemen ausgeführt werden kann, sind die folgenden unterstützten Plattformen und ihre Abhängigkeiten aufgeführt:
sudo apt-get install git build-essential cmake
setup-x86_64.exe -q -P git,make,gcc-core,gcc-g++,cmake
Installieren Sie die folgenden Pakete:
Um dieses Beispiel zu kompilieren und zu installieren, verwenden Sie die folgenden Anweisungen:
git clone https://github.com/ainfosec/ci_helloworld.git
mkdir ci_helloworld/build
cd ci_helloworld/build
cmake ..
make
make test
git clone https://github.com/ainfosec/ci_helloworld.git
mkdir ci_helloworld/build
cd ci_helloworld/build
cmake -G "NMake Makefiles" ..
nmake
nmake test
git clone https://github.com/ainfosec/ci_helloworld.git
mkdir ci_helloworld/build
cd ci_helloworld/build
cmake -G "Visual Studio 15 2017 Win64" ..
msbuild ci_helloworld.sln
ctest
git clone https://github.com/ainfosec/ci_helloworld.git
mkdir ci_helloworld/build
cd ci_helloworld/build
cmake ..
make
make test
Im Folgenden finden Sie eine Beschreibung aller Analysetools, die in die in diesem Projekt verwendeten CI-Dienste integriert wurden, sowie eine Erläuterung ihrer Funktionsweise.
Das CI ist so eingerichtet, dass es mithilfe von Sauerstoff auf fehlende Dokumentation prüft. Im Gegensatz zu den meisten Analysetools, die in diesem Projekt verwendet werden, gibt es für doxygen kein Make-Ziel. Stattdessen wird es mit doxygen manuell mit dem folgenden Skript ausgeführt:
- doxygen .doxygen.txt
- |
if [[ -s doxygen_warnings.txt ]]; then
echo "You must fix doxygen before submitting a pull request"
echo ""
cat doxygen_warnings.txt
exit -1
fi
Dieses Skript führt doxygen für den Quellcode aus und alle Warnungen werden in einer Datei namens doxygen_warnings.txt
abgelegt. Wenn diese Datei leer ist, bedeutet dies, dass die Doxygen-Analyse erfolgreich war und der gesamte Code basierend auf den Einstellungen in der Konfigurationsdatei .doxygen.txt
dokumentiert ist. Wenn diese Datei nicht leer ist, schlägt der Test fehl und es werden die von doxygen generierten Warnungen ausgegeben.
git diff --check
bietet eine einfache Möglichkeit, zu erkennen, ob Leerzeichenfehler in das Repo eingecheckt wurden, und zu prüfen, ob Zeilenende-Zeilen entweder fehlen oder zu viele enthalten. Weitere Informationen zu dieser Prüfung finden Sie hier. Diese Prüfung ist für Entwickler äußerst nützlich, wenn PRs Änderungen enthalten, die nichts mit ihren spezifischen Änderungen zu tun haben.
- |
if [[ -n $(git diff --check HEAD^) ]]; then
echo "You must remove whitespace before submitting a pull request"
echo ""
git diff --check HEAD^
exit -1
fi
Diese Prüfung führt einfach git diff --check
aus, das mit einem Fehler zurückkehrt, wenn die Prüfung fehlschlägt. In diesem Fall wird der Unterschied für den Benutzer angezeigt.
Die Formatierung des Quellcodes ist eine hervorragende Möglichkeit, ein einheitliches Erscheinungsbild des Codes beizubehalten. Das Problem mit der Quellformatierung besteht darin, dass die PRs der Entwickler Änderungen enthalten, die nichts mit ihren spezifischen Änderungen zu tun haben, es sei denn, dass jeder sie verwendet. Schlimmer noch: Wenn Sie versuchen, die Quellformatierung regelmäßig zu korrigieren, wird der Git-Verlauf Ihres Repos jedes Mal zerstört, wenn Sie die Quelle formatieren. Wenn daher eine Quellformatierung verwendet werden soll, sollte diese bei jedem Codeunterschied überprüft werden, um sicherzustellen, dass die Formatierung korrekt ist.
Für dieses Beispiel verwenden wir Astyle, aber das Clang-Format funktioniert auch. Um Astyle auf einfache Weise zu unterstützen, stellen wir ein Make-Ziel bereit, das es dem Entwickler ermöglicht, seinen Quellcode durch einfaches Ausführen make format
zu formatieren. Dazu müssen wir zuerst Astyle erhalten:
list ( APPEND ASTYLE_CMAKE_ARGS
"-DCMAKE_INSTALL_PREFIX= ${CMAKE_BINARY_DIR} "
)
ExternalProject_Add(
astyle
GIT_REPOSITORY https://github.com/Bareflank/astyle.git
GIT_TAG v1.2
GIT_SHALLOW 1
CMAKE_ARGS ${ASTYLE_CMAKE_ARGS}
PREFIX ${CMAKE_BINARY_DIR} /astyle/ prefix
TMP_DIR ${CMAKE_BINARY_DIR} /astyle/tmp
STAMP_DIR ${CMAKE_BINARY_DIR} /astyle/stamp
DOWNLOAD_DIR ${CMAKE_BINARY_DIR} /astyle/download
SOURCE_DIR ${CMAKE_BINARY_DIR} /astyle/src
BINARY_DIR ${CMAKE_BINARY_DIR} /astyle/ build
)
Diese cmake-Logik verwendet ExternalProject_Add, um Astyle automatisch herunterzuladen, für Ihre Plattform zu kompilieren und in Ihrem Build-Verzeichnis zu installieren, damit es von unserem benutzerdefinierten Make-Ziel verwendet werden kann. Beachten Sie, dass wir zur Vereinfachung unsere eigene gepatchte Version von Astyle verwenden, die das Build-System von Astyles benutzerdefiniertem Satz von Makefiles auf ein CMake-Build-System ändert.
list ( APPEND ASTYLE_ARGS
--style=1tbs
--lineend=linux
-- suffix =none
--pad-oper
--unpad-paren
--break-closing-brackets
--align-pointer= name
--align-reference= name
--indent-preproc-define
--indent-switches
--indent-col1-comments
--keep-one-line-statements
--keep-one-line-blocks
--pad-header
--convert-tabs
--min-conditional-indent=0
--indent=spaces=4
--close-templates
--add-brackets
--break-after-logical
${CMAKE_SOURCE_DIR} / include /*.h
${CMAKE_SOURCE_DIR} /src/*.cpp
${CMAKE_SOURCE_DIR} / test /*.cpp
)
if ( NOT WIN32 STREQUAL "1" )
add_custom_target (
format
COMMAND ${CMAKE_SOURCE_DIR} /bin/astyle ${ASTYLE_ARGS}
COMMENT "running astyle"
)
else ()
add_custom_target (
format
COMMAND ${CMAKE_SOURCE_DIR} /bin/astyle.exe ${ASTYLE_ARGS}
COMMENT "running astyle"
)
endif ()
Um unser benutzerdefiniertes Astyle-Make-Ziel zu erstellen, verwenden wir den obigen CMake-Code. Dies verweist CMake je nach Plattform auf die resultierende Astyle-Binärdatei und stellt Astyle die für dieses Projekt spezifischen Formatierungsoptionen und Quelldateien bereit.
- cmake -DENABLE_ASTYLE=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make format
- |
if [[ -n $(git diff) ]]; then
echo "You must run make format before submitting a pull request"
echo ""
git diff
exit -1
fi
Um schließlich bei jedem PR zu überprüfen, ob eine Codeänderung unserer Astyle-Konfiguration entspricht, fügen wir den obigen Code zu unserem Travis CI-Skript hinzu. Dadurch wird unser make format
Ziel erstellt und ausgeführt, um den Code zu formatieren. Wenn make format
den Code formatiert, wird ein Diff erstellt, das mit git diff
erkannt werden kann. Wenn kein Diff erstellt wird, bedeutet dies, dass die gesamte Quelle unserer Astyle-Konfiguration entspricht und der Test bestanden wird.
Clang Tidy bietet statische Analysen. Die Unterstützung für dieses Tool beginnt mit dem Hinzufügen des Folgenden zur CMakeLists.txt:
set (CMAKE_EXPORT_COMPILE_COMMANDS ON )
Dadurch wird CMake angewiesen, alle Kompilierungsanweisungen aufzuzeichnen, die zum Kompilieren Ihres Projekts verwendet werden, einschließlich Flags und Definitionen. Clang Tidy verwendet diese Informationen, um Ihr Projekt auf die gleiche Weise statisch zu analysieren, wie es kompiliert wurde. Der Vorteil dieses Ansatzes ist eine deutliche Verbesserung der Genauigkeit. Der Hauptnachteil dieses Ansatzes besteht darin, dass Clang Tidy nicht gut darin ist, Dateien statisch zu analysieren, die nicht in dieser Kompilierungsdatenbank angezeigt werden (z. B. Header-Dateien). Aus diesem Grund muss ein Header, wenn Sie ihn analysieren möchten, in eine Quelldatei eingebunden werden, die in der Kompilierungsdatenbank enthalten ist.
list ( APPEND RUN_CLANG_TIDY_BIN_ARGS
-clang-tidy-binary ${CLANG_TIDY_BIN}
-header- filter =.*
-checks=clan*,cert*,misc*,perf*,cppc*,read*,mode*,-cert-err58-cpp,-misc-noexcept-move-constructor
)
add_custom_target (
tidy
COMMAND ${RUN_CLANG_TIDY_BIN} ${RUN_CLANG_TIDY_BIN_ARGS}
COMMENT "running clang tidy"
)
Schließlich erhält Clang Tidy ein eigenes Make-Ziel, um seine Verwendung zu vereinfachen. Hier teilen wir dem Skript run-clang-tidy-4.0.py
mit, welche Clang Tidy-Binärdatei verwendet werden soll, welche Prüfungen durchgeführt werden sollen und welche Header-Dateien einbezogen werden sollen. Wir deaktivieren den Test „-cert-err58-cpp“, weil er Code von „catch.hpp“ auslöst, und „-misc-noexclusive-move-constructor“, weil er in 4.0 immer noch fehlerhaft ist. Beachten Sie, dass wir eine bestimmte Version von Clang Tidy auswählen. Dies ist wichtig, da jede neue Version von Clang Tidy Fehler behebt und neue Prüfungen hinzufügt, was je nach verwendeter Version zu unterschiedlichen Ergebnissen führt.
- cmake -DENABLE_CLANG_TIDY=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make tidy > output.txt
- |
if [[ -n $(grep "warning: " output.txt) ]] || [[ -n $(grep "error: " output.txt) ]]; then
echo "You must pass the clang tidy checks before submitting a pull request"
echo ""
grep --color -E '^|warning: |error: ' output.txt
exit -1;
else
echo -e " 33[1;32mxE2x9Cx93 passed: 33[0m $1";
fi
Von Travis CI aus aktivieren wir Clang Tidy und sichern die Ausgabe in einer Datei. Wenn diese Datei „Warnung“ oder „Fehler“ enthält, bestehen wir den Test nicht und geben die von Clang Tidy gemeldeten Probleme an den Benutzer zur Behebung aus. Dadurch wird sichergestellt, dass jede PR statisch überprüft wurde.
CppCheck ist ein weiteres statisches Analysetool.
list ( APPEND CPPCHECK_CMAKE_ARGS
"-DCMAKE_INSTALL_PREFIX= ${CMAKE_BINARY_DIR} "
)
ExternalProject_Add(
cppcheck
GIT_REPOSITORY https://github.com/danmar/cppcheck.git
GIT_TAG 1.79
GIT_SHALLOW 1
CMAKE_ARGS ${CPPCHECK_CMAKE_ARGS}
PREFIX ${CMAKE_BINARY_DIR} /external/cppcheck/ prefix
TMP_DIR ${CMAKE_BINARY_DIR} /external/cppcheck/tmp
STAMP_DIR ${CMAKE_BINARY_DIR} /external/cppcheck/stamp
DOWNLOAD_DIR ${CMAKE_BINARY_DIR} /external/cppcheck/download
SOURCE_DIR ${CMAKE_BINARY_DIR} /external/cppcheck/src
BINARY_DIR ${CMAKE_BINARY_DIR} /external/cppcheck/ build
)
Die von Ubuntu 14.04 bereitgestellte Version von CppCheck ist alt und unterstützt C++11 nicht gut. Daher holen wir uns eine bestimmte Version von CppCheck von GitHub, sodass alle Benutzer des Projekts dieselbe Version verwenden können.
list ( APPEND CPPCHECK_ARGS
--enable=warning,style,performance,portability,unusedFunction
--std=c++11
--verbose
--error-exitcode=1
-- language =c++
-DMAIN=main
-I ${CMAKE_SOURCE_DIR} / include
${CMAKE_SOURCE_DIR} / include /*.h
${CMAKE_SOURCE_DIR} /src/*.cpp
${CMAKE_SOURCE_DIR} / test /*.cpp
)
add_custom_target (
check
COMMAND ${CMAKE_BINARY_DIR} /bin/cppcheck ${CPPCHECK_ARGS}
COMMENT "running cppcheck"
)
Anschließend fügen wir ein benutzerdefiniertes Ziel für unsere neu erstellte CppCheck-Anwendung hinzu und weisen CppCheck an, alle seine Prüfungen zu aktivieren (abzüglich umständlicher Warnungen) und alle unsere Quelldateien zu überprüfen. Beachten Sie, dass CppCheck wissen muss, dass MAIN=main ist, andernfalls denkt es, dass die Hauptfunktion nicht ausgeführt wird, und wir müssen CppCheck anweisen, einen Fehler mit einem Fehlercode ungleich 0 zu melden, damit Travis CI einen fehlgeschlagenen Test meldet, wenn einer der folgenden Punkte vorliegt Prüfungen schlagen fehl.
- cmake -DENABLE_CPPCHECK=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make check
Das Ausführen des Travis CI-Tests ist so einfach wie das Einschalten von CppCheck und das Ausführen des benutzerdefinierten Make-Ziels.
Coverity Scan ist ein weiteres statisches Analysetool, das sich sehr gut dazu eignet, schwer zu findende strukturelle Probleme in Ihrem Code zu finden. Wenn Sie Zugriff auf Coverity Scan haben, lohnt es sich, es zu Ihrem SDP hinzuzufügen.
- os : linux
env :
- TEST="Coverity Scan"
addons :
apt :
sources :
- ubuntu-toolchain-r-test
packages :
- gcc-6
- g++-6
coverity_scan :
project :
name : " ainfosec/ci_helloworld "
description : " A simple example of how to setup a complete CI environment for C and C++ "
notification_email : [email protected]
build_command_prepend : " cmake -DCMAKE_CXX_COMPILER=g++-6 .. "
build_command : " make "
branch_pattern : master
script :
- echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
Coverity Scan ist außerdem sehr einfach einzurichten. Der obige Travis CI-Test ist ein Ausschneiden/Einfügen von der Website, nachdem Sie Ihr Projekt registriert haben. Wir müssen lediglich die Quelle kompilieren, die Coverity Scan mitteilt, wie der Quellcode kompiliert wird. Von dort aus bietet ihre Website die Möglichkeit, Verzeichnisse auszuschließen und Probleme mit dem Code anzuzeigen. In unserem Beispiel führen wir einen Scan bei jeder Änderung am Master durch, da die Anzahl der Änderungen am Master gering ist. Bei großen Projekten mit vielen Zusammenführungen pro Tag empfiehlt Coverity Scan jedoch die Verwendung eines bestimmten coverity_scan-Zweigs für Scans. Wenn dies geschehen ist, sollte ein nächtlicher Scan eingerichtet werden, indem der Master-Zweig erfasst und jede Nacht in den Coverity_scan-Zweig verschoben wird. Auf diese Weise können Probleme mit Coverity Scan schnell identifiziert werden.
Codecov ist ein leistungsstarkes und dennoch einfach einzurichtendes Coverage-Tool.
if (ENABLE_COVERAGE)
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -g " )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -O0" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fprofile-arcs" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -ftest-coverage" )
set ( CMAKE_EXE_LINKER_FLAGS " ${CMAKE_EXE_LINKER_FLAGS} --coverage" )
endif ()
Um die Abdeckung einzurichten, müssen wir die GCOV-Unterstützung in unserem Compiler aktivieren (setzt GCC oder Clang voraus). Sobald diese Unterstützung aktiviert ist, werden durch die Ausführung von make test
Abdeckungsstatistiken generiert, die Codecov analysieren kann, um Ihnen einen Bericht darüber zu geben, welcher Code einheitlich getestet wurde und welcher nicht.
- cmake -DENABLE_COVERAGE=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make test
- cd ..
- bash <(curl -s https://codecov.io/bash)
Der Travis CI-Test ist so einfach wie das Kompilieren und Ausführen der Komponententests und das anschließende Ausführen des Bash-Skripts von Codecov. Sobald dies erledigt ist, können die Ergebnisse auf der Website von Codecov eingesehen werden.
Overalls sind ein weiteres Schutzinstrument.
if (ENABLE_COVERAGE)
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -g " )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -O0" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fprofile-arcs" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -ftest-coverage" )
set ( CMAKE_EXE_LINKER_FLAGS " ${CMAKE_EXE_LINKER_FLAGS} --coverage" )
endif ()
GCOV muss wie Codecov aktiviert sein.
- pip install --user git+git://github.com/eddyxu/cpp-coveralls.git
- cmake -DENABLE_COVERAGE=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make test
- cd ..
- |
coveralls --build-root build --gcov-options '-lp'
-e build/external
-e build/include
-e build/CMakeFiles/3.8.0
-e build/CMakeFiles/feature_tests.c
-e build/CMakeFiles/feature_tests.cxx
Im Gegensatz zu Codecov ist die Einrichtung von Coveralls viel schwieriger. Codecov verfolgt, welche Dateien sich in Ihrem Git-Repository befinden und generiert nur Berichte für Dateien im Repo, während Coveralls Abdeckungsberichte für alle angezeigten Dateien generiert, einschließlich der von CMake generierten Dateien. Coveralls verfügt außerdem nicht über ein einfaches Bash-Skript, um Abdeckungsdaten an seinen Server zu melden, sondern erfordert stattdessen die Installation eines externen C++-spezifischen Tools zum Sammeln von GCOV-Daten. Aus diesen Gründen müssen wir cpp-coveralls installieren und es dann anweisen, bestimmte Dateien/Verzeichnisse auszuschließen, die nicht erfasst werden sollen.
Die Google Sanitizers sind ein dynamisches Analysetool, das in GCC und Clang/LLVM enthalten ist.
if (ENABLE_ASAN)
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -g" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -O1" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fuse-ld=gold" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fsanitize=address" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fsanitize=leak" )
endif ()
if (ENABLE_USAN)
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fuse-ld=gold" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fsanitize=undefined" )
endif ()
if (ENABLE_TSAN)
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fuse-ld=gold" )
set ( CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fsanitize=thread" )
endif ()
Jedes Desinfektionsmittel muss isoliert durchgeführt werden, daher führen wir einen Test pro Desinfektionsmittelgruppe durch. Die Flags für jeden Satz finden Sie auf der GitHub-Seite von Google sowie in der Nutzungsdokumentation von Clang.
- cmake -DENABLE_ASAN= ON -DCMAKE_CXX_COMPILER= "g++-6" ..
- make
- make test
Für jeden Test aktivieren wir die spezifische Prüfung und die Komponententests. Wenn eine Prüfung fehlschlägt, wird der Komponententest mit einem Exit-Code ungleich 0 beendet, was dazu führt, dass Travis CI den Test nicht besteht. Es ist zu beachten, dass jede neue Version von GCC und Clang eine bessere Unterstützung bietet und Sie daher, wie bei einigen anderen Tools auch, bei einer bestimmten Version bleiben sollten.
Valgrind ist ein weiteres dynamisches Analysetool zur Leckerkennung.
set (MEMORYCHECK_COMMAND_OPTIONS " ${MEMORYCHECK_COMMAND_OPTIONS} --leak-check=full" )
set (MEMORYCHECK_COMMAND_OPTIONS " ${MEMORYCHECK_COMMAND_OPTIONS} --track-fds=yes" )
set (MEMORYCHECK_COMMAND_OPTIONS " ${MEMORYCHECK_COMMAND_OPTIONS} --trace-children=yes" )
set (MEMORYCHECK_COMMAND_OPTIONS " ${MEMORYCHECK_COMMAND_OPTIONS} --error-exitcode=1" )
Der einfachste Weg, Valgrind auszuführen, besteht darin, die integrierte Unterstützung von CMake zu verwenden, da diese die Fehlerlogik für Sie verwaltet. Aus diesem Grund müssen wir CMake mitteilen, welche Flags Valgrind geben sollen. In diesem Fall aktivieren wir alle seine Prüfungen und weisen Valgrind an, mit einem Exit-Code ungleich 0 zu beenden, damit Travis CI den Test nicht besteht, wenn eine Prüfung fehlschlägt.
- cmake -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- ctest -T memcheck
Um den Test auszuführen, müssen wir lediglich den Code kompilieren und die Komponententests mit ctest ausführen, um den Memcheck-Modus zu aktivieren.
Dieses Projekt ist unter der MIT-Lizenz lizenziert.