이 저장소는 다양한 CI 서비스를 설정하고 분석 도구를 이러한 서비스에 통합하는 방법에 대한 간단한 예를 제공합니다. 이러한 도구는 포괄적인 소프트웨어 개발 프로세스(SDP)의 일부로 사용해야 하며 C 또는 C++ 애플리케이션의 시작 템플릿으로도 사용할 수 있습니다. Windows, Cygwin, Linux 및 macOS에 대한 테스트 지원을 제공하는 다음 CI 도구가 사용됩니다.
다음 검사가 수행됩니다.
다음 실제 프로젝트에서는 SDP의 일부로 이러한 다양한 기술을 사용합니다.
이 저장소는 대부분의 시스템에서 실행되도록 만들 수 있지만 지원되는 플랫폼과 해당 종속성은 다음과 같습니다.
sudo apt-get install git build-essential cmake
setup-x86_64.exe -q -P git,make,gcc-core,gcc-g++,cmake
다음 패키지를 설치하십시오.
이 예제를 컴파일하고 설치하려면 다음 지침을 따르십시오.
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
다음은 작동 방식에 대한 설명을 포함하여 이 프로젝트에서 사용되는 CI 서비스에 통합된 모든 분석 도구에 대한 설명을 제공합니다.
CI는 doxygen을 사용하여 누락된 문서를 확인하도록 설정되었습니다. 이 프로젝트에 사용된 대부분의 분석 도구와 달리 doxygen에 대한 make 대상이 없으며 대신 다음 스크립트와 함께 doxygen을 사용하여 수동으로 실행됩니다.
- 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
이 스크립트는 소스 코드에 대해 doxygen을 실행하고 모든 경고는 doxygen_warnings.txt
라는 파일에 저장됩니다. 이 파일이 비어 있으면 doxygen 분석이 통과되었으며 모든 코드가 .doxygen.txt
구성 파일의 설정에 따라 문서화되었음을 의미합니다. 이 파일이 비어 있지 않으면 테스트가 실패하고 doxygen에 의해 생성된 경고가 인쇄됩니다.
git diff --check
공백 오류가 저장소에 체크인된 시기를 감지하는 간단한 방법을 제공할 뿐만 아니라 파일 끝 줄 바꿈이 누락되었거나 너무 많이 포함되어 있는지 확인하는 방법도 제공합니다. 이 수표에 대한 자세한 내용은 여기에서 확인할 수 있습니다. 이 확인은 PR에 특정 변경 사항과 관련 없는 수정 사항이 포함된 경우 개발자에게 매우 유용합니다.
- |
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
이 검사는 단순히 git diff --check
실행하며, 검사가 실패하면 오류를 반환합니다. 이런 일이 발생하면 사용자가 볼 수 있도록 차이점이 표시됩니다.
소스 코드 형식 지정은 코드의 일관된 모양과 느낌을 유지하는 좋은 방법입니다. 소스 형식의 문제는 모든 사람이 사용하지 않는 한 개발자 PR에 특정 변경 사항과 관련 없는 수정 사항이 포함되거나 더 나쁜 경우 소스 형식을 주기적으로 수정하려고 하면 소스 형식을 지정할 때마다 저장소의 git 기록이 파괴된다는 것입니다. 따라서 소스 형식을 사용하려면 모든 코드 비교에서 형식이 올바른지 확인해야 합니다.
이 예에서는 Astyle을 사용하지만 Clang 형식도 작동합니다. Astyle을 간단한 방법으로 지원하기 위해 우리는 개발자가 단순히 make format
실행하여 소스 코드의 형식을 지정할 수 있도록 하는 make 대상을 제공합니다. 이렇게 하려면 먼저 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
)
이 cmake 로직은 ExternalProject_Add를 사용하여 Astyle을 자동으로 다운로드하고 플랫폼에 맞게 컴파일한 다음 사용자 정의 make 대상에서 사용할 수 있도록 빌드 디렉터리에 설치합니다. 단순화를 위해 Astyle의 사용자 정의 Makefile 세트에서 CMake 빌드 시스템으로 빌드 시스템을 변경하는 자체 패치 버전의 Astyle을 사용합니다.
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 ()
사용자 정의 astyle make 대상을 생성하려면 위의 CMake 코드를 사용합니다. 이는 플랫폼에 따라 결과 스타일 바이너리를 CMake에 지정하고 이 프로젝트에 특정한 서식 옵션 및 소스 파일과 함께 스타일을 제공합니다.
- 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
마지막으로 각 PR에서 코드 변경 사항이 Astyle 구성을 준수하는지 확인하기 위해 위의 코드를 Travis CI 스크립트에 추가합니다. 그러면 make format
대상이 생성되고 이를 실행하여 코드 형식을 지정합니다. make format
코드 형식을 지정하면 git diff
사용하여 감지할 수 있는 diff가 생성됩니다. 차이점이 생성되지 않으면 모든 소스가 Astyle 구성을 준수하고 테스트가 통과되었음을 의미합니다.
Clang Tidy는 정적 분석을 제공합니다. 이 도구에 대한 지원은 CMakeLists.txt에 다음을 추가하는 것부터 시작됩니다.
set (CMAKE_EXPORT_COMPILE_COMMANDS ON )
이는 CMake가 플래그 및 정의를 포함하여 프로젝트를 컴파일하는 데 사용되는 모든 컴파일 지침을 기록하도록 지시합니다. Clang Tidy는 이 정보를 사용하여 프로젝트가 컴파일된 것과 동일한 방식으로 프로젝트를 정적으로 분석합니다. 이 접근 방식의 장점은 정확도가 크게 향상된다는 것입니다. 이 접근 방식의 가장 큰 단점은 Clang Tidy가 이 컴파일 데이터베이스에 표시되지 않는 파일(예: 헤더 파일)을 정적으로 분석하는 데 능숙하지 않다는 것입니다. 이러한 이유로 헤더를 분석하려면 컴파일 데이터베이스에 포함된 소스 파일에 헤더가 포함되어 있어야 합니다.
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"
)
마지막으로 Clang Tidy에는 사용을 단순화하기 위해 자체 make 대상이 제공됩니다. 여기서 우리는 run-clang-tidy-4.0.py
스크립트에 사용할 tidy 바이너리, 수행할 검사, 포함할 헤더 파일 등을 모두 알려줍니다. -cert-err58-cpp 테스트는 catch.hpp에서 코드를 트리거하기 때문에 해제하고 -misc-noException-move-constructor는 4.0에서 여전히 버그가 있기 때문에 해제합니다. Clang Tidy의 각 새 버전은 버그를 수정하고 새로운 검사를 추가하여 사용하는 버전에 따라 결과가 다르기 때문에 Clang Tidy의 특정 버전을 선택하는 것이 중요합니다.
- 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
Travis CI에서 Clang Tidy를 활성화하고 출력을 파일에 덤프합니다. 이 파일에 "경고" 또는 "오류"가 포함되어 있으면 테스트가 실패하고 Clang Tidy가 보고한 문제를 수정하도록 사용자에게 출력합니다. 이렇게 하면 모든 PR이 정적으로 검사되었는지 확인됩니다.
CppCheck는 또 다른 정적 분석 도구입니다.
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
)
Ubuntu 14.04에서 제공하는 CppCheck 버전은 오래되었고 C++11을 잘 지원하지 않으므로 GitHub에서 특정 버전의 CppCheck를 가져와 프로젝트의 모든 사용자가 동일한 버전을 사용할 수 있도록 합니다.
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"
)
그런 다음 새로 구축된 CppCheck 애플리케이션에 대한 사용자 정의 대상을 추가하여 CppCheck에 모든 검사(현학적 경고 제외)를 활성화하고 모든 소스 파일을 검사하도록 지시합니다. CppCheck는 MAIN=main이라는 것을 알아야 합니다. 그렇지 않으면 메인 기능이 실행되지 않는다고 생각할 것이며 Travis CI가 다음과 같은 경우 실패한 테스트를 보고하도록 CppCheck에 0이 아닌 오류 코드로 오류를 지시해야 합니다. 검사가 실패합니다.
- cmake -DENABLE_CPPCHECK=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make check
Travis CI 테스트를 실행하는 것은 CppCheck를 켜고 사용자 정의 make 대상을 실행하는 것만큼 간단합니다.
Coverity Scan은 코드에서 찾기 어려운 구조적 문제를 찾는 데 매우 유용한 또 다른 정적 분석 도구입니다. Coverity Scan에 액세스할 수 있다면 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은 설정도 매우 간단합니다. 위의 Travis CI 테스트는 프로젝트 등록 후 웹사이트에서 잘라내어 붙여넣은 것입니다. 우리가 해야 할 일은 Coverity Scan에 소스 코드가 어떻게 컴파일되는지 알려주는 소스를 컴파일하는 것뿐입니다. 거기에서 해당 웹사이트는 디렉터리를 제외하고 코드 문제를 확인할 수 있는 수단을 제공합니다. 이 예에서는 마스터에 대한 변경 수가 적기 때문에 마스터에 대한 모든 변경 사항에 대해 스캔을 수행하지만 하루에 병합이 많은 대규모 프로젝트의 경우 Coverity Scan은 스캔을 위해 특정 Coverity_scan 분기를 사용할 것을 제안합니다. 이 작업이 완료되면 마스터 브랜치를 잡고 매일 밤 Coverity_scan 브랜치로 푸시하여 야간 스캔을 설정해야 합니다. 이렇게 하면 Coverity Scan의 문제를 신속하게 식별할 수 있습니다.
Codecov는 강력하면서도 설치가 간단한 적용 범위 도구입니다.
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 지원을 활성화해야 합니다(GCC 또는 Clang 가정). 이 지원이 활성화되면 make test
실행하면 Codecov가 분석하여 어떤 코드가 단위 테스트되었는지 여부에 대한 보고서를 제공할 수 있는 적용 범위 통계가 생성됩니다.
- cmake -DENABLE_COVERAGE=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make test
- cd ..
- bash <(curl -s https://codecov.io/bash)
Travis CI 테스트는 단위 테스트를 컴파일하고 실행한 다음 Codecov의 bash 스크립트를 실행하는 것만큼 간단합니다. 이 작업이 완료되면 Codecov 웹사이트에서 결과를 확인할 수 있습니다.
작업복은 또 다른 적용 도구입니다.
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 ()
Codecov와 마찬가지로 GCOV도 활성화해야 합니다.
- 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
Codecov와 달리 Coveralls는 설정하기가 훨씬 어렵습니다. Codecov는 git 저장소에 있는 파일을 추적하고 저장소에 있는 파일에 대한 보고서만 생성하는 반면 Coveralls는 CMake에서 생성된 파일을 포함하여 표시되는 모든 파일에 대한 적용 범위 보고서를 생성합니다. 또한 Coveralls에는 서버에 적용 범위 데이터를 보고하는 간단한 bash 스크립트가 없지만 대신 GCOV 데이터를 수집하기 위한 외부 C++ 특정 도구를 설치해야 합니다. 이러한 이유로 우리는 cpp-coveralls를 설치한 다음 수집되어서는 안되는 특정 파일/디렉토리를 제외하도록 지시해야 합니다.
Google Sanitizer는 GCC 및 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 ()
각 살균제는 별도로 실행되어야 하므로 살균제 그룹당 하나의 테스트가 있습니다. 각 세트의 플래그는 Google의 GitHub 페이지와 Clang의 사용 문서에서 찾을 수 있습니다.
- cmake -DENABLE_ASAN= ON -DCMAKE_CXX_COMPILER= "g++-6" ..
- make
- make test
각 테스트마다 특정 검사와 단위 테스트를 켜고, 검사가 실패하면 단위 테스트가 0이 아닌 종료 코드로 종료되어 Travis CI가 테스트에 실패하게 됩니다. GCC 및 Clang의 각 새 버전에는 더 나은 지원이 제공되므로 다른 도구와 마찬가지로 특정 버전을 사용해야 합니다.
Valgrind는 누출 감지 기능을 제공하는 또 다른 동적 분석 도구입니다.
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" )
Valgrind를 실행하는 가장 쉬운 방법은 오류 논리를 자동으로 처리해 주는 CMake의 내장 지원 기능을 사용하는 것입니다. 이러한 이유로 Valgrind에 어떤 플래그를 제공할지 CMake에 알려주어야 합니다. 이 경우 모든 검사를 활성화하고 Valgrind에게 0이 아닌 종료 코드로 종료하도록 지시하여 검사가 실패하면 Travis CI가 테스트를 실패하게 합니다.
- cmake -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- ctest -T memcheck
테스트를 실행하려면 코드를 컴파일하고 ctest를 사용하여 단위 테스트를 실행하고 memcheck 모드를 활성화하기만 하면 됩니다.
이 프로젝트는 MIT 라이선스에 따라 라이선스가 부여됩니다.