Este repositório fornece um exemplo simples de como configurar vários serviços de CI, bem como integrar ferramentas de análise a esses serviços. Essas ferramentas devem ser usadas como parte de um processo de desenvolvimento de software (SDP) abrangente e também podem ser usadas como modelo inicial para qualquer aplicativo C ou C++. As seguintes ferramentas de CI são usadas, fornecendo suporte de teste para Windows, Cygwin, Linux e macOS
As seguintes verificações são realizadas:
Os seguintes projetos do mundo real utilizam uma variedade destas técnicas como parte do seu SDP:
Embora este repositório possa ser executado na maioria dos sistemas, a seguir estão as plataformas suportadas e suas dependências:
sudo apt-get install git build-essential cmake
setup-x86_64.exe -q -P git,make,gcc-core,gcc-g++,cmake
Instale os seguintes pacotes:
Para compilar e instalar este exemplo, use as seguintes instruções:
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
A seguir é fornecida uma descrição de todas as ferramentas de análise que foram integradas aos serviços de IC usados por este projeto, incluindo uma explicação de como elas funcionam.
O CI está configurado para verificar documentação ausente usando doxygen. Ao contrário da maioria das ferramentas de análise usadas neste projeto, não há um alvo definido para o doxygen e, em vez disso, ele é executado usando o doxygen manualmente com o seguinte script:
- 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
Este script executa doxygen no código-fonte e quaisquer avisos são colocados em um arquivo chamado doxygen_warnings.txt
. Se este arquivo estiver vazio, significa que a análise do doxygen foi aprovada e todo o código está documentado com base nas configurações do arquivo de configuração .doxygen.txt
. Se este arquivo não estiver vazio, o teste falha e imprime os avisos gerados pelo doxygen.
git diff --check
fornece uma maneira simples de detectar quando erros de espaço em branco foram verificados no repositório, bem como verificar quando novas linhas de final de arquivo estão faltando ou contêm muitas. Mais informações sobre esta verificação podem ser encontradas aqui. Esta verificação é extremamente útil para desenvolvedores quando os PRs contêm modificações não relacionadas às suas alterações específicas.
- |
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
Esta verificação simplesmente executa git diff --check
, que retorna com um erro se a verificação falhar. Se isso ocorrer, a diferença será exibida para o usuário ver.
A formatação do código-fonte é uma ótima maneira de manter uma aparência consistente com o código. O problema com a formatação da fonte é que, a menos que todos a usem, os PRs dos desenvolvedores conterão modificações não relacionadas às suas alterações específicas ou, pior, tentar corrigir a formatação da fonte periodicamente destruirá o histórico git do seu repositório cada vez que você formatar a fonte. Portanto, se a formatação de origem for usada, ela deverá ser verificada em cada diferença de código para garantir que a formatação esteja correta.
Neste exemplo, usamos Astyle, mas o Clang Format também funcionará. Para oferecer suporte ao Astyle de maneira simples, fornecemos um alvo make que permite ao desenvolvedor formatar seu código-fonte simplesmente executando make format
. Para fazer isso, devemos primeiro obter o 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
)
Esta lógica cmake usa ExternalProject_Add para baixar automaticamente o Astyle, compilá-lo para sua plataforma e instalá-lo em seu diretório de construção para que possa ser usado por nosso destino make personalizado. Observe que usamos nossa própria versão corrigida do Astyle que altera o sistema de compilação do conjunto personalizado de Makefiles do Astyle para um sistema de compilação CMake para simplificar.
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 ()
Para criar nosso alvo astyle make personalizado, usamos o código CMake acima. Isso aponta o CMake para o binário astyle resultante, dependendo da plataforma, e fornece ao astyle as opções de formatação e arquivos de origem específicos para este projeto.
- 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
Finalmente, para verificar em cada PR se uma alteração de código está de acordo com nossa configuração Astyle, adicionamos o código acima ao nosso script Travis CI. Isso cria nosso destino make format
e o executa para formatar o código. Se make format
formatar o código, um diff será criado e o git diff
poderá ser usado para detectar. Se nenhuma comparação for criada, significa que toda a fonte segue nossa configuração Astyle e o teste é aprovado.
Clang Tidy fornece análise estática. O suporte para esta ferramenta começa adicionando o seguinte ao CMakeLists.txt:
set (CMAKE_EXPORT_COMPILE_COMMANDS ON )
Isso diz ao CMake para registrar todas as instruções de compilação usadas para compilar seu projeto, incluindo sinalizadores e definições. Clang Tidy usará essas informações para analisar estaticamente seu projeto da mesma forma que foi compilado. A vantagem desta abordagem é uma melhoria significativa na precisão. A principal desvantagem dessa abordagem é que o Clang Tidy não é bom em analisar estaticamente arquivos que não aparecem neste banco de dados de compilação (como arquivos de cabeçalho). Por este motivo, se quisermos analisar um cabeçalho, ele deverá ser incluído por um arquivo fonte que esteja incluído no banco de dados de compilação.
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"
)
Finalmente, o Clang Tidy recebe seu próprio alvo para simplificar seu uso. Aqui informamos ao script run-clang-tidy-4.0.py
qual binário clang tidy usar, bem como quais verificações realizar e quais arquivos de cabeçalho incluir, que são todos eles. Desativamos o teste -cert-err58-cpp porque ele aciona o código de catch.hpp e -misc-noexcept-move-constructor porque ainda apresenta erros na versão 4.0. Observe que escolhemos uma versão específica do Clang Tidy, o que é importante porque cada nova versão do Clang Tidy corrige bugs e adiciona novas verificações, resultando em resultados diferentes dependendo da versão que você usa.
- 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
No Travis CI, habilitamos o Clang Tidy e despejamos sua saída em um arquivo. Se este arquivo contiver "aviso" ou "erro", falharemos no teste e enviaremos os problemas relatados pelo Clang Tidy ao usuário para serem corrigidos. Isso garante que cada PR foi verificado estaticamente.
CppCheck é outra ferramenta de análise estática.
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
)
A versão do CppCheck fornecida pelo Ubuntu 14.04 é antiga e não suporta bem o C++ 11, então pegamos uma versão específica do CppCheck do GitHub, permitindo que todos os usuários do projeto usem a mesma versão.
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"
)
Em seguida, adicionamos um destino personalizado para nosso aplicativo CppCheck recém-construído, informando ao CppCheck para ativar todas as suas verificações (menos avisos pedantes) e verificar todos os nossos arquivos de origem. Observe que o CppCheck precisa saber que MAIN = main, caso contrário, ele pensará que a função principal não foi executada, e precisamos informar ao CppCheck um erro com um código de erro diferente de 0 para que o Travis CI relate uma falha no teste se algum dos as verificações falham.
- cmake -DENABLE_CPPCHECK=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make check
Executar o teste Travis CI é tão simples quanto ativar o CppCheck e executar o destino personalizado.
Coverity Scan é outra ferramenta de análise estática muito boa para encontrar problemas estruturais difíceis de encontrar em seu código. Se você tiver acesso ao Coverity Scan, vale a pena adicioná-lo ao seu 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-
O Coverity Scan também é muito simples de configurar. O teste Travis CI acima é recortado/colado do site deles após você registrar seu projeto. Tudo o que precisamos fazer é compilar o código-fonte que informa ao Coverity Scan como o código-fonte é compilado. A partir daí, o site deles fornecerá um meio de excluir diretórios e ver problemas com o código. Em nosso exemplo, fazemos uma varredura em cada alteração no master, pois o número de alterações no master é pequeno, mas em projetos grandes com muitas mesclagens por dia, o Coverity Scan sugere o uso de um branch específico coverity_scan para varreduras. Se isso for feito, uma varredura noturna deve ser configurada pegando o branch master e enviando-o para o branch coverity_scan todas as noites. Dessa forma, os problemas com o Coverity Scan podem ser identificados rapidamente.
Codecov é uma ferramenta de cobertura poderosa, mas simples de configurar.
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 ()
Para configurar a cobertura, devemos habilitar o suporte GCOV em nosso compilador (assume GCC ou Clang). Assim que esse suporte for habilitado, a execução de make test
gerará estatísticas de cobertura que o Codecov pode analisar para fornecer um relatório de qual código foi ou não testado em unidade.
- cmake -DENABLE_COVERAGE=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make test
- cd ..
- bash <(curl -s https://codecov.io/bash)
O teste Travis CI é tão simples quanto compilar e executar os testes de unidade e, em seguida, executar o script bash do Codecov. Feito isso, os resultados podem ser conferidos no site da Codecov.
Macacão é outra ferramenta de cobertura.
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 ()
Assim como o Codecov, o GCOV deve estar habilitado.
- 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
Ao contrário do Codecov, o Coveralls é muito mais difícil de configurar. Codecov rastreia quais arquivos estão em seu repositório git e gera apenas relatórios para arquivos no repositório, enquanto Coveralls irá gerar relatórios de cobertura para todos os arquivos que vê, incluindo arquivos gerados pelo CMake. Coveralls também não possui um script bash simples para relatar dados de cobertura ao seu servidor, mas requer a instalação de uma ferramenta externa específica em C++ para coletar dados GCOV. Por essas razões, temos que instalar o cpp-coveralls e, em seguida, instruí-lo a excluir arquivos/diretórios específicos que estão sendo coletados e que não deveriam ser.
Os Google Sanitizers são uma ferramenta de análise dinâmica incluída no GCC e 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 ()
Cada desinfetante deve ser executado isoladamente e, portanto, temos um teste por grupo de desinfetantes. Os sinalizadores de cada conjunto podem ser encontrados na página GitHub do Google, bem como na documentação de uso do Clang.
- cmake -DENABLE_ASAN= ON -DCMAKE_CXX_COMPILER= "g++-6" ..
- make
- make test
Para cada teste, ativamos a verificação específica e os testes de unidade e, se uma verificação falhar, o teste de unidade será encerrado com um código de saída diferente de 0, fazendo com que o Travis CI falhe no teste. Deve-se destacar que cada nova versão do GCC e Clang vem com melhor suporte e, portanto, como algumas das outras ferramentas, você deve se limitar a uma versão específica.
Valgrind é outra ferramenta de análise dinâmica que fornece detecção de vazamentos.
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" )
A maneira mais fácil de executar o Valgrind é usar o suporte integrado do CMake, pois ele tratará a lógica de erro para você. Por esse motivo, precisamos informar ao CMake quais sinalizadores fornecer ao Valgrind. Nesse caso, habilitamos todas as suas verificações e dizemos ao Valgrind para sair com um código de saída diferente de 0 para que, se uma verificação falhar, o Travis CI falhará no teste.
- cmake -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- ctest -T memcheck
Para executar o teste, basta compilar o código e executar os testes unitários usando ctest, habilitando o modo memcheck.
Este projeto está licenciado sob a licença MIT.