文章目錄
Step 1:實現C++構建
這個環節主要工作是選擇並使用適合自己項目的 C++ 構建系統,例如 CMake、QMake、Autotools(config/make)等。這個過程本身並不複雜,但往往會涉及到 C++ 工程化的老大難問題——如何提供 C++ 構建所需的第三依賴庫。
想要了解 C++ 構建系統和依賴管理混亂的原因,可以查看 爲什麼 C++ 的構建系統和依賴管理這麼混亂?
爲了解決這個問題,本質上有三種解決方案:
- 方案一:最理想化的解決方案,源碼工程不依賴任何二進制形式的三方庫,即將三方庫以源碼形式進行引入後直接從源碼進行編譯;
- 方案二:最簡便也是最常見的解決方案,在編譯環境中將構建過程中的所需三方庫提前進行準備(直接存儲於編譯環境本地或者編譯前從遠端進行拉取);
- 方案三:最工程化的解決方案,使用 C++ 依賴管理工具,將所有工程及其依賴庫納入管理。
下表簡要對比了三種解決方案優缺點:
如何提供 C++ 構建所需三方依賴庫 | 編譯速度 | 啓動成本 | 維護成本 | 備註 |
---|---|---|---|---|
方案一:將三方庫以源碼形式進行引入 | 較慢 | 較低 | 低 | 部分項目不可行(如不提供基礎庫源碼) |
方案二:在編譯環境中提前準備所需三方庫 | 快 | 低 | 高 | |
方案三:使用 C++ 依賴管理工具 | 較快 | 高 | 一般 |
因此,我們在項目實踐策略上一般會這樣進行:
- 如果項目可以將三方庫以源碼形式引入,就儘量選擇爲方案一
- 如果項目必須使用二進制形式的第三方依賴庫且第三方庫變更頻率低,則選擇方案二
- 如果項目必須使用二進制形式的第三方依賴庫且第三方庫變更頻率高且依賴關係較爲複雜,再選擇方案三
如果選擇方案三,推薦使用 Conan 工具來進行管理。
Step 2:實現自動化
確定目標:使用 gitlab-ci 實現
瞭解更多關於 gitlab-ci 的細節可以查看官方文檔:GitLab Continuous Integration (GitLab CI/CD)
首先,需要明確:在 GitLab 上實現自動化構建的最佳方式(沒有之一)是 gitlab-ci。這是因爲 gitlab-ci 是 GitLab 官方提供 CI 工具,因此在 GitLab 平臺上它具備其他 CI 工具無法比擬的巨大優勢——將自動化與代碼無縫銜接。
gitlab-ci 中的重要定義:
- Pipeline:自動化的抽象概念,Pipeline 其實相當於一個完整的自動化任務定義,裏面可以包含多個 Stage
- Stage:自動化的抽象概念,Stage 表示構建階段,當一個 Pipeline 中定義多個 Stages,這些 Stages 會有以下特點:
- 所有 Stages 會按照順序運行,即當前 Stage 執行完成後,下一個 Stage 纔會開始
- 如果當前 Stage 失敗,那麼後面的 Stages 不會再執行
- Job: 自動化的具體定義,Jobs 表示具體的構建工作,當一個 Stage 裏面定義多個 Jobs,這些 Jobs 會有以下特點:
- 相同 Stage 中的 Jobs 支持並行執行
- 相同 Stage 中的 Jobs 都執行成功時,該 Stage 纔會成功
- .gitlab-ci.yml :gitlab-ci 約定位於項目根目錄下 .gitlab-ci.yml 用於定義當前項目的 Pipeline,任何提交或者 Merge Request 的合併都可以觸發該 Pipeline 執行。
- GitLab Runner:在 GitLab 中,Job 需要明確定義在什麼樣的 GitLab Runners 中運行。目前 GitLab Runners 支持 GNU/Linux, macOS, FreeBSD 和 Windows等系統,並且支持部署於 Kubernetes 之上。
選擇 GitLab Runner 的最佳實踐方案:GitLab Runner on Kubernetes
如果按照傳統的方式,即只是將 GitLab-Runner 安裝於某個或某幾個機器上的方式,容易出現如下的問題:
- 很難保障每個 Runner 所在機器環境一致,因此 Job 在設計上要多考慮一些情況增加 CI 實現的複雜度
- 缺乏統一調度,資源分配容易出現不平衡
爲了解決這些痛點,我們可以採用在 Kubernetes 集羣中運行 GitLab-Runner 來動態執行 gitlab-ci 腳本任務,它整個流程如下圖:
具體實現步驟如下:
- 搭建 Kubernetes 並安裝 GitLab Runner :參考 Run GitLab Runner on a Kubernetes cluster
- 定製滿足項目要求的 Docker Image:參考 Docker — 從入門到實踐、Best practices for writing Dockerfiles
- 將定製的 Docker Image 上傳至 Docker Registry 進行管理:參考 Harbor User Guide:Pulling and pushing images using Docker client
設計項目自動化流程:掌握 .gitlab-ci.yml基本語法
想要系統瞭解 .gitlab-ci.yml 語法可查看官方文檔:Configuration of your pipelines with .gitlab-ci.yml
這裏給出 gitlab-ci 給出的 C++ 項目 .gitlab-ci.yml 模板作爲參考:
# This file is a template, and might need editing before it works on your project.
# use the official gcc image, based on debian
# can use verions as well, like gcc:5.2
# see https://hub.docker.com/_/gcc/
image: gcc
build:
stage: build
# instead of calling g++ directly you can also use some build toolkit like make
# install the necessary build tools when needed
# before_script:
# - apt update && apt -y install make autoconf
script:
- g++ helloworld.cpp -o mybinary
artifacts:
paths:
- mybinary
# depending on your build setup it's most likely a good idea to cache outputs to reduce the build time
# cache:
# paths:
# - "*.o"
# run tests using the binary built before
test:
stage: test
script:
- ./runmytests.sh
Step 3:引入業界常用的功能模塊
加速編譯-ccache
# Sharing caches across the same branch and the same job
cache:
paths:
- key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
- ccache/
ccache-build:
before_script:
- apt-get update -yqq
- apt-get install -y -qq # List truncated for web
- # CCache Config
- mkdir -p ccache
- export CCACHE_BASEDIR=${PWD}
- export CCACHE_DIR=${PWD}/ccache
script:
- cmake . -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- make
其他的 cache 策略可以參考:Cache dependencies in GitLab CI/CD
靜態代碼分析-cppcheck
cppcheck:
before_script:
- sudo apt install -y --no-install-recommends cppcheck=1.82-1
- cppcheck --version
script:
- cppcheck *.cpp --verbose --enable=all --inconclusive --language=c++
構建產物持久化-JFrog Bintray
deploy-bintray:
before_script:
- sudo apt install -y --no-install-recommends curl
scrtpt:
- cmake .
- make
after_script:
- curl -u ${USERNAME}:${PASSWORD} -T build/app "${JFROG_BINTRAY_LOCATION}/${TARGET_FILE_PATH}"
artifacts:
expire_in: 1 day
paths:
- build/app