يوفر هذا الريبو مثالًا بسيطًا لكيفية إعداد خدمات CI المختلفة بالإضافة إلى دمج أدوات التحليل في هذه الخدمات. يجب استخدام هذه الأدوات كجزء من عملية تطوير البرامج الشاملة (SDP) ويمكن استخدامها أيضًا كقالب بداية لأي تطبيق C أو C++. يتم استخدام أدوات CI التالية، مما يوفر دعمًا للاختبار لأنظمة التشغيل Windows وCygwin وLinux وmacOS
يتم إجراء الفحوصات التالية:
تستخدم مشاريع العالم الحقيقي التالية مجموعة متنوعة من هذه التقنيات كجزء من برنامج 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.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.txt
. إذا لم تكن هذه الملفات فارغة، يفشل الاختبار، ويطبع التحذيرات الناتجة عن الدوكسيجين.
يوفر git diff --check
طريقة بسيطة لاكتشاف متى تم التحقق من أخطاء المسافات البيضاء في الريبو، بالإضافة إلى التحقق من فقدان الأسطر الجديدة في نهاية الملف أو احتوائها على عدد كبير جدًا. يمكن العثور على مزيد من المعلومات حول هذا الفحص هنا. يعد هذا الفحص مفيدًا للغاية للمطورين عندما تحتوي العلاقات العامة على تعديلات لا علاقة لها بالتغييرات المحددة الخاصة بهم.
- |
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
، والذي يعود بخطأ في حالة فشل الفحص. إذا حدث هذا، يتم عرض الفرق ليراه المستخدم.
يعد تنسيق التعليمات البرمجية المصدر طريقة رائعة للحفاظ على الشكل والمظهر المتسقين مع التعليمات البرمجية. المشكلة في تنسيق المصدر هي أنه ما لم يستخدمه الجميع، سيحتوي ممثلو العلاقات العامة للمطورين على تعديلات لا علاقة لها بتغييراتهم المحددة، أو ما هو أسوأ من ذلك، فإن محاولة إصلاح تنسيق المصدر بشكل دوري ستؤدي إلى تدمير سجل بوابة الريبو الخاص بك في كل مرة تقوم فيها بتنسيق المصدر. لذلك، إذا كان سيتم استخدام تنسيق المصدر، فيجب التحقق من ذلك في كل اختلاف في التعليمات البرمجية للتأكد من صحة التنسيق.
في هذا المثال، نستخدم Astyle، ولكن تنسيق Clang سيعمل أيضًا. لدعم Astyle بطريقة بسيطة، نوفر make target الذي يسمح للمطور بتنسيق كود المصدر الخاص به ببساطة عن طريق تشغيل make format
. للقيام بذلك، يجب علينا أولاً الحصول على 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 تلقائيًا، وتجميعه لنظامك الأساسي، وتثبيته في دليل البناء الخاص بك بحيث يمكن استخدامه من خلال هدف الإنشاء المخصص الخاص بنا. لاحظ أننا نستخدم نسختنا المصححة من Astyle والتي تغير نظام البناء من مجموعة Makefiles المخصصة لـ Astyle إلى نظام بناء CMake من أجل البساطة.
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 ()
لإنشاء هدف تصميم النمط المخصص لدينا، نستخدم رمز 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
للكشف عنه. إذا لم يتم إنشاء أي فرق، فهذا يعني أن المصدر بأكمله يلتزم بتكوين 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 هدفًا خاصًا بها لتبسيط استخدامه. هنا نخبر البرنامج النصي run-clang-tidy-4.0.py
أي برنامج clang tidy الثنائي يجب استخدامه، بالإضافة إلى عمليات التحقق التي يجب إجراؤها، وما هي ملفات الرأس التي يجب تضمينها، وكلها. نقوم بإيقاف تشغيل اختبار -cert-err58-cpp لأنه يقوم بتشغيل التعليمات البرمجية من Catch.hpp، و-misc-noexcept-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 إلى المستخدم لإصلاحها. وهذا يضمن أن كل العلاقات العامة قد تم فحصها بشكل ثابت.
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
)
إصدار CppCheck المقدم من Ubuntu 14.04 قديم، ولا يدعم C++ 11 جيدًا، لذلك حصلنا على إصدار محدد من CppCheck من GitHub، مما يسمح لجميع مستخدمي المشروع باستخدام نفس الإصدار.
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، وإلا فإنه سيعتقد أن الوظيفة الرئيسية لم يتم تنفيذها، ونحن بحاجة إلى إخبار CppCheck بالخطأ باستخدام رمز خطأ غير 0 حتى يقوم Travis CI بالإبلاغ عن فشل الاختبار في حالة وجود أي من تفشل الشيكات.
- cmake -DENABLE_CPPCHECK=ON -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- make check
يعد تشغيل اختبار Travis CI أمرًا بسيطًا مثل تشغيل CppCheck وتشغيل هدف التخصيص.
يعد 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 في برنامج التحويل البرمجي الخاص بنا (بافتراض دول مجلس التعاون الخليجي أو 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 بسيطًا مثل تجميع اختبارات الوحدة وتشغيلها، ثم تشغيل البرنامج النصي bash الخاص بـ Codecov. وبمجرد الانتهاء من ذلك، يمكن رؤية النتائج على موقع 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 بسيط للإبلاغ عن بيانات التغطية إلى الخادم الخاص بها، ولكنها تتطلب بدلاً من ذلك تثبيت أداة خارجية خاصة بـ C++ لجمع بيانات GCOV. لهذه الأسباب، يتعين علينا تثبيت cpp-coveralls، ثم إخباره باستبعاد ملفات/أدلة محددة يتم جمعها والتي لا ينبغي جمعها.
تعد Google Sanitizers أداة تحليل ديناميكية مضمنة في دول مجلس التعاون الخليجي و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 ()
يجب تشغيل كل معقم بشكل منفصل، وبالتالي لدينا اختبار واحد لكل مجموعة معقمات. يمكن العثور على العلامات الخاصة بكل مجموعة على صفحة GitHub الخاصة بـ Google بالإضافة إلى وثائق استخدام Clang.
- cmake -DENABLE_ASAN= ON -DCMAKE_CXX_COMPILER= "g++-6" ..
- make
- make test
لكل اختبار، نقوم بتشغيل الفحص المحدد، واختبارات الوحدة، وإذا فشل الفحص، فسيخرج اختبار الوحدة برمز خروج غير 0، مما يتسبب في فشل Travis CI في الاختبار. تجدر الإشارة إلى أن كل إصدار جديد منGC و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 المدمج لأنه سيتعامل مع منطق الأخطاء نيابةً عنك. لهذا السبب، نحتاج إلى إخبار CMake بالعلامات التي يجب منحها لـ Valgrind. في هذه الحالة، نقوم بتمكين جميع عمليات التحقق الخاصة بها ونطلب من Valgrind الخروج باستخدام رمز خروج غير 0، بحيث إذا فشل الفحص، فسوف يفشل Travis CI في الاختبار.
- cmake -DCMAKE_CXX_COMPILER="g++-6" ..
- make
- ctest -T memcheck
لإجراء الاختبار، كل ما يتعين علينا القيام به هو تجميع التعليمات البرمجية، وتشغيل اختبارات الوحدة باستخدام ctest، وتمكين وضع memcheck.
هذا المشروع مرخص بموجب ترخيص MIT.