Ce dépôt fournit un exemple simple sur la façon de configurer divers services CI ainsi que d'intégrer des outils d'analyse dans ces services. Ces outils doivent être utilisés dans le cadre d'un processus de développement logiciel (SDP) complet et peuvent également être utilisés comme modèle de départ pour toute application C ou C++. Les outils CI suivants sont utilisés, fournissant une prise en charge des tests pour Windows, Cygwin, Linux et macOS
Les contrôles suivants sont effectués :
Les projets réels suivants utilisent une variété de ces techniques dans le cadre de leur SDP :
Bien que ce dépôt puisse être exécuté sur la plupart des systèmes, voici les plates-formes prises en charge et leurs dépendances :
sudo apt-get install git build-essential cmake
setup-x86_64.exe -q -P git,make,gcc-core,gcc-g++,cmake
Installez les packages suivants :
Pour compiler et installer cet exemple, utilisez les instructions suivantes :
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
Ce qui suit fournit une description de tous les outils d'analyse qui ont été intégrés dans les services CI utilisés par ce projet, y compris une explication de leur fonctionnement.
Le CI est configuré pour vérifier la documentation manquante à l'aide de doxygen. Contrairement à la plupart des outils d'analyse utilisés dans ce projet, il n'y a pas de cible de création pour doxygen, et à la place, il est exécuté manuellement en utilisant doxygen avec le script suivant :
- 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
Ce script exécute doxygen par rapport au code source et tous les avertissements sont placés dans un fichier appelé doxygen_warnings.txt
. Si ce fichier est vide, cela signifie que l'analyse doxygen a réussi et que tout le code est documenté en fonction des paramètres du fichier de configuration .doxygen.txt
. Si ce fichier n'est pas vide, le test échoue et imprime les avertissements générés par doxygen.
git diff --check
fournit un moyen simple de détecter quand des erreurs d'espaces ont été vérifiées dans le dépôt, ainsi que de vérifier quand les nouvelles lignes de fin de fichier sont manquantes ou en contiennent trop. Plus d’informations sur ce contrôle peuvent être trouvées ici. Cette vérification est extrêmement utile pour les développeurs lorsque les PR contiennent des modifications sans rapport avec leurs modifications spécifiques.
- |
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
Cette vérification exécute simplement git diff --check
, qui renvoie une erreur si la vérification échoue. Si cela se produit, la différence est affichée pour que l'utilisateur puisse la voir.
Le formatage du code source est un excellent moyen de conserver une apparence cohérente avec le code. Le problème avec le formatage de la source est que, à moins que tout le monde ne l'utilise, les PR des développeurs contiendront des modifications sans rapport avec leurs modifications spécifiques, ou pire, tenter de corriger périodiquement le formatage de la source détruira l'historique git de votre dépôt à chaque fois que vous formaterez la source. Par conséquent, si le formatage source doit être utilisé, il doit être vérifié sur chaque différence de code pour garantir que le formatage est correct.
Pour cet exemple, nous utilisons Astyle, mais Clang Format fonctionnera également. Pour prendre en charge Astyle de manière simple, nous fournissons une cible make qui permet au développeur de formater son code source en exécutant simplement make format
. Pour ce faire, il faut d'abord récupérer Astyle :
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
)
Cette logique cmake utilise ExternalProject_Add pour télécharger automatiquement Astyle, le compiler pour votre plate-forme et l'installer dans votre répertoire de construction afin qu'il puisse être utilisé par notre cible make personnalisée. Notez que nous utilisons notre propre version corrigée d'Astyle qui modifie le système de construction de l'ensemble personnalisé de Makefiles d'Astyle à un système de construction CMake pour plus de simplicité.
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 ()
Pour créer notre cible astyle make personnalisée, nous utilisons le code CMake ci-dessus. Cela pointe CMake vers le binaire astyle résultant en fonction de la plate-forme et fournit à astyle les options de formatage et les fichiers sources spécifiques à ce projet.
- 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
Enfin, pour vérifier sur chaque PR qu'un changement de code adhère à notre configuration Astyle, nous ajoutons le code ci-dessus à notre script Travis CI. Cela crée notre cible make format
et l'exécute pour formater le code. Si make format
le code, un diff sera créé que git diff
pourra être utilisé pour détecter. Si aucune différence n'est créée, cela signifie que toutes les sources adhèrent à notre configuration Astyle et que le test réussit.
Clang Tidy fournit une analyse statique. La prise en charge de cet outil commence par l'ajout des éléments suivants au fichier CMakeLists.txt :
set (CMAKE_EXPORT_COMPILE_COMMANDS ON )
Cela indique à CMake d'enregistrer toutes les instructions de compilation utilisées pour compiler votre projet, y compris les indicateurs et les définitions. Clang Tidy utilisera ces informations pour analyser statiquement votre projet de la même manière qu'il a été compilé. L’avantage de cette approche est une amélioration significative de la précision. Le principal inconvénient de cette approche est que Clang Tidy n'est pas efficace pour analyser statiquement les fichiers qui n'apparaissent pas dans cette base de données de compilation (tels que les fichiers d'en-tête). Pour cette raison, si vous souhaitez analyser un en-tête, il doit être inclus dans un fichier source inclus dans la base de données de compilation.
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"
)
Enfin, Clang Tidy dispose de sa propre cible de création pour simplifier son utilisation. Ici, nous indiquons au script run-clang-tidy-4.0.py
quel binaire clang Tidy utiliser, ainsi que les vérifications à effectuer et quels fichiers d'en-tête inclure, qui sont tous. Nous désactivons le test -cert-err58-cpp car il déclenche le code de catch.hpp, et -misc-nosauf-move-constructor car il est toujours bogué dans la version 4.0. Notez que nous choisissons une version spécifique de Clang Tidy, ce qui est important car chaque nouvelle version de Clang Tidy corrige des bugs et ajoute de nouvelles vérifications, ce qui entraîne des résultats différents selon la version que vous utilisez.
- 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
Depuis Travis CI, nous activons Clang Tidy et transférons sa sortie dans un fichier. Si ce fichier contient un « avertissement » ou une « erreur », nous échouons au test et affichons les problèmes signalés par Clang Tidy à l'utilisateur pour qu'il les corrige. Cela garantit que chaque PR a été vérifié statiquement.
CppCheck est un autre outil d'analyse statique.
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
)
La version de CppCheck fournie par Ubuntu 14.04 est ancienne et ne prend pas bien en charge C++11, nous récupérons donc une version spécifique de CppCheck depuis GitHub, permettant à tous les utilisateurs du projet d'utiliser la même version.
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"
)
Nous ajoutons ensuite une cible personnalisée pour notre nouvelle application CppCheck, indiquant à CppCheck d'activer toutes ses vérifications (moins les avertissements pédants) et de vérifier tous nos fichiers sources. Notez que CppCheck doit savoir que MAIN=main, sinon il pensera que la fonction principale n'est pas exécutée, et nous devons dire à CppCheck d'erreur avec un code d'erreur non 0 afin que Travis CI signale un échec de test si l'un des les contrôles échouent.
- cmake -DENABLE_CPPCHECK=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make check
Exécuter le test Travis CI consiste simplement à activer CppCheck et à exécuter la cible de création personnalisée.
Coverity Scan est un autre outil d'analyse statique qui est très efficace pour détecter les problèmes structurels difficiles à trouver avec votre code. Si vous avez accès à Coverity Scan, cela vaut la peine de l'ajouter à votre SDP.
- 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 est également très simple à configurer. Le test Travis CI ci-dessus est un copier/coller de leur site Web après avoir enregistré votre projet. Tout ce que nous avons à faire est de compiler la source qui indique à Coverity Scan comment le code source est compilé. À partir de là, leur site Web fournira un moyen d'exclure des répertoires et de détecter les problèmes liés au code. Dans notre exemple, nous effectuons une analyse de chaque modification à maîtriser car le nombre de modifications à maîtriser est faible, mais sur les grands projets avec beaucoup de fusions par jour, Coverity Scan suggère d'utiliser une branche coverity_scan spécifique pour les analyses. Si cela est fait, une analyse nocturne doit être configurée en saisissant la branche master et en la poussant vers la branche coverity_scan chaque nuit. De cette façon, les problèmes liés à Coverity Scan peuvent être identifiés rapidement.
Codecov est un outil de couverture puissant mais simple à configurer.
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 ()
Pour configurer la couverture, nous devons activer la prise en charge de GCOV dans notre compilateur (en supposant GCC ou Clang). Une fois cette prise en charge activée, l'exécution make test
générera des statistiques de couverture que Codecov pourra analyser pour vous donner un rapport sur le code qui a ou non été testé unitairement.
- cmake -DENABLE_COVERAGE=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make test
- cd ..
- bash <(curl -s https://codecov.io/bash)
Le test Travis CI est aussi simple que de compiler et d'exécuter les tests unitaires, puis d'exécuter le script bash de Codecov. Une fois cela fait, les résultats peuvent être consultés sur le site Web de Codecov.
La combinaison est un autre outil de couverture.
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 ()
Comme Codecov, GCOV doit être activé.
- 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
Contrairement à Codecov, les combinaisons sont beaucoup plus difficiles à configurer. Codecov garde une trace des fichiers qui se trouvent dans votre référentiel git et génère uniquement des rapports pour les fichiers du dépôt, tandis que Coveralls générera des rapports de couverture pour tous les fichiers qu'il voit, y compris les fichiers générés par CMake. Coveralls ne dispose pas non plus d'un simple script bash pour rapporter les données de couverture à leur serveur, mais nécessite à la place l'installation d'un outil externe spécifique au C++ pour collecter les données GCOV. Pour ces raisons, nous devons installer cpp-coveralls, puis lui dire d'exclure les fichiers/répertoires spécifiques en cours de collecte qui ne devraient pas l'être.
Les Google Sanitizers sont un outil d'analyse dynamique inclus dans GCC et Clang/LLVM.
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 ()
Chaque désinfectant doit être exécuté de manière isolée et nous avons donc un test par groupe de désinfectants. Les indicateurs de chaque ensemble peuvent être trouvés sur la page GitHub de Google ainsi que dans la documentation d'utilisation de Clang.
- cmake -DENABLE_ASAN= ON -DCMAKE_CXX_COMPILER= "g++-6" ..
- make
- make test
Pour chaque test, nous activons la vérification spécifique et les tests unitaires, et si une vérification échoue, le test unitaire se terminera avec un code de sortie non 0, provoquant l'échec du test par Travis CI. Il convient de noter que chaque nouvelle version de GCC et Clang est dotée d'un meilleur support et que, comme certains autres outils, vous devez donc vous en tenir à une version spécifique.
Valgrind est un autre outil d'analyse dynamique qui permet la détection des fuites.
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" )
Le moyen le plus simple d'exécuter Valgrind est d'utiliser le support intégré de CMake car il gérera la logique d'erreur pour vous. Pour cette raison, nous devons indiquer à CMake quels drapeaux donner à Valgrind. Dans ce cas, nous activons toutes ses vérifications et disons à Valgrind de quitter avec un code de sortie différent de 0 afin que si une vérification échoue, Travis CI échouera au test.
- cmake -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- ctest -T memcheck
Pour exécuter le test, il suffit de compiler le code et d'exécuter les tests unitaires en utilisant ctest, activant le mode memcheck.
Ce projet est sous licence MIT.