基於 Conan 的 C/C++ 持續交付流水線

背景


在當下軟件應用的開發過程當中,單槍匹馬或者小作坊式的模式已經很少見了,協作式的開發成爲主流。相應的,應用的代碼也不再是從零開始,而是基於或引用很多已有的、共享的模塊,如各種開源的框架和共用庫,或者協作團隊中開發的自研庫,這就是軟件開發中常說的“依賴”。爲了更好地管理這種依賴關係,各種開發語言都逐漸發展出了自己的依賴管理系統,如 Java 的 Maven、NodeJS 的 NPM、Python 的 Pypi 等。這些依賴管理系統的日漸完善和廣泛應用,使得相應語言的應用開發更加簡潔、高效,大大推動了軟件應用的發展和普及。


然而相對的,作爲軟件開發重要組成部分的 C/C++ 語言,由於其編譯型語言的特性,即應用最終要編譯成爲目標機器可直接執行的程序,使得 C/C++ 的依賴管理一直是一個衆所周知的難題和痛點。這主要體現在:

  • 應用二進制接口不兼容。C/C++ 的依賴不僅僅體現在代碼上,還包括操作系統、架構、編譯器等環境配置上,爲了確保一個共享庫與其他庫、整個應用的兼容性,必須通過各種配置來描述這些配置的依賴信息。

  • 編譯構建慢。由於頭文件和預處理機制,以及上面提到的兼容性,需要額外的機制來提升編譯效率,才能保證只編譯那些需要重新編譯的代碼。

  • 代碼鏈接和內嵌。一個靜態的 C/C++ 庫能夠被另一個庫通過頭文件包含的方式引用,而一個共享庫也能嵌入另一個靜態庫。在兩種情形中,當任何依賴變更時,都必須管理哪些庫是需要重新構建的。

  • 生態系統的快速發展。針對不同平臺、不同構建任務及應用場景的編譯器、構建系統層出不窮,導致解決上述問題的工作量不斷地增加。


當然,針對 C/C++ 的依賴管理,業界也開展了很多研究和實踐工作,可惜大多數的效果並不理想。而本文將會介紹一種當前已逐步得到業界關注和認可,並得到大力推廣的 C/C++ 依賴管理方案--Conan,以及如何基於 Conan、持續集成(CI)工具 Jenkins 和全語言製品庫 Artifactory,實現 C/C++ 應用的持續交付流水線。



Conan--C/C++ 的包管理器


Conan 是一個開源項目(FOSS,Free Open Source Software),https://conan.io ,爲 C/C++ 的依賴包管理構建了非中心化的管理架構,開發者可以像 Docker Registry 一樣搭建自己專屬的依賴包倉庫。Conan 是跨平臺的解決方案,同時兼容各種構建工具,以依賴包的二進制文件爲基礎來定義、管理依賴關係,使得依賴包的獲取和消費更加符合目標環境和配置的特點,提升了整體編譯的效率。Conan 是基於 Python 語言的,上手門檻較低,也易於擴展。在被 JFrog 收購之後,Conan 在保持開源特性的同時,還結合了 JFrog 產品在製品管理、DevOps 工具集成等方面的能力,爲開發者提供了更爲全面的支持。Conan 在當前 C/C++ 依賴管理領域已得到廣泛關注和快速發展。


通常之前介紹的像 Maven 這樣的依賴管理系統,都包含以下幾個部分:

  • 依賴包倉庫,用以存儲需要引用的依賴包,即各種通用框架或共享模塊;

  • 依賴訪問協議,用以描述如何在依賴包倉庫中定位、獲取或上傳共享模塊;

  • 依賴描述語言,用以描述如何定義依賴關係,以及後臺對依賴關係的自動解析;

  • 客戶端,根據描述語言中的定義,遵照訪問協議,從倉庫中獲取或上傳相應的依賴包。


Conan 也在這幾方面提供了成熟的解決方案:


  • 依賴包倉庫

Conan 在 JFrog 的公網製品分發平臺 JFrog Bintray 上搭建了公共的依賴包倉庫conan-center(https://conan.bintray.com),開發者可以直接在這裏獲取所需的各種公共依賴包。


同時,開發者還可以在本地搭建原生的 conan_server,或者直接利用 JFrog Artifactory 製品倉庫,做爲私有的依賴包倉庫。


  • 依賴訪問協議

Conan 通過“<包名>/<版本號>@<所有者>/<成熟度>”的命名規則來定位一個依賴包,其中“<所有者>/<成熟度>”(user/channel)定義了一個類似於命名空間(NameSpace)的機制,用於區分針對同一個共享庫的不同實現。


每一個依賴包都分爲 recipe 和 package 兩個部分。 recipe 定義了依賴包的基本信息、依賴關係、構建方法等基本信息,package 則根據目標環境和配置,如操作系統、架構、編譯器等(即 Conan 中的 setting)的不同,保存對應的二進制實現。這樣,客戶端訪問時,先根據命名規則定位到 recipe,再根據目標 setting 的不同選擇對應的二進制 package 來下載、使用。


在 conan-center 中,各種公共庫都根據目標 setting 的不同提供了大量的二進制 package 供開發者直接使用,大大提高了 C/C++ 應用的編譯效率。


  • 依賴描述語言

Conan提供了簡單明瞭的依賴關係描述方式,在後續的示例中會做詳細解讀。


  • 客戶端

Conan 的客戶端提供了豐富的命令行命令,能夠方便地實現依賴關係的解析和依賴包的管理。


Conan 客戶端的安裝也非常簡便,大家可以參考文檔自己實踐(https://docs.conan.io/en/latest/installation.html)。


安裝好之後,我們可以運行第一個命令:

可以看到 Conan 客戶端已經預先配置好了與公共庫 conan-center 的連接,開發者可以直接使用其中的公共依賴包。


本文後續將通過示例來展示 Conan 如何利用這些解決方案來提供服務。



Conan 應用示例


本文將基於一個簡單的 C++ 應用來展示如何使用 Conan,其代碼可以在https://github.com/xingao0803/demo-poco-timer.git  中獲得,供大家參考。


  • 描述依賴關係

示例中的 C++ 應用,timer.cpp,是一個簡單的 timer 程序,引用了公共庫 POCO:

爲了在 Conan 中描述與 POCO 的依賴關係,需要編寫 conanfile.txt,這就是上一節提到的依賴描述語言。

其中,[requires] 部分列出了本應用需要的依賴包,這裏是 POCO,1.8.0.1版本,而且是由 pocoproject 提供的穩定(stable)版本。


[generators] 則列出了編譯本應用使用的編譯器類型。Conan 提供的公開示例大多是基於 cmake 的,這裏改用更爲通用的 compiler_args,不限定編譯器的類型。


當然,這裏只是一個簡單的例子,conanfile.txt 的更多內容請參考 Conan 官方文檔https://docs.conan.io/en/latest/reference/conanfile_txt.html。


我們知道,在軟件開發中,除了代碼中的直接依賴,還會有各種傳遞依賴。而 conanfile.txt 裏只列出了直接依賴。Conan 的客戶端提供了“conan info”命令來解析所有的依賴關係。


在 conanfile.txt 所在目錄執行:

本應用相關的各種依賴傳遞關係就會在 denpendencies.html 裏展示出來,如下圖:

此時再執行:

可以看到,相關依賴包的 recipe 已經下載到本地 cache 裏了。


  • 下載依賴包二進制文件

“conan info” 命令只是下載了依賴包的 recipe。要下載對應 setting 的 package 二進制包,用以編譯,還需要執行 “conan install” 命令。


在 conanfile.txt 所在目錄創建構建子目錄,並執行 “conan install”:

從運行結果可以看出,Conan 的客戶端根據本地的 setting 設置和依賴包的 recipe,自動從 conan-center 上獲取對應的二進制 package,下載到本地 cache 裏。


當然,由於 C/C++ 生態系統的快速發展,Conan 現有的二進制 package 不一定能夠覆蓋所有的 setting 組合。“—build=missing” 參數就是指定在沒有 setting 對應的二進制 package 時,根據 recipe 中定義的方法,自動從依賴包的源代碼編譯出對應的二進制包,存儲在本地 cache 中。例如:當我在 macbook 上執行上述操作時,POCO 就需要進行重新編譯:

當我們再次運行 “conan install” 命令時可以發現,此時相關的依賴包已經可以直接從本地 cache 獲取了,避免了重複的網絡訪問和編譯工作。


  • 上傳到私有依賴包倉庫

在當前團隊協作開發的模式下,僅僅把依賴包下載到本地 cache 是遠遠不夠的,我們還需要讓整個團隊都能夠分享這些依賴包。這樣即避免了重複的網絡訪問和編譯,又保證了團隊中依賴引用的一致性。此時就需要引入私有的依賴包倉庫。


Conan 提供了原生的 conan_server 作爲本地化部署的私有倉庫,可以參考文檔https://docs.conan.io/en/latest/uploading_packages/running_your_server.html。


而這裏我們要推薦 JFrog 的 Artifactory 全語言製品倉庫。Artifactory 不僅僅可以做爲 Conan 倉庫,其全語言的支持能力還使其能夠同時提供 Maven、NPM、Docker 等依賴倉庫的服務。 Artifactory 還提供元數據的能力,也就是可以在倉庫存儲製品的屬性上記錄整個 DevOps 過程中的關鍵數據。此外,Artifactory 還可以和 Jenkins 緊密集成,在 Jenkins Pipeline 中提供針對各種開發語言的 DSL,方便持續交付流水線的開發與編排。Artifactory 全語言、元數據,以及集成 Jenkins 的優勢在後續示例中都能夠得到體現。


Artifactory 的相關信息可以參見文檔https://www.jfrog.com/confluence/display/RTF/Welcome+to+Artifactory ,“JFrog 傑蛙 DevOps” 微信公衆號上也有很多相關文章和視頻課程供大家參考。此外,JFrog Artifactory 還針對 Conan 的應用推出了社區版-- Artifactory CE,https://docs.conan.io/en/latest/uploading_packages/artifactory_ce.html,供大家使用。


爲了使用 Artifactory 建設私有的 Conan 依賴包倉庫,我們需要在 Artifactory 創建一個 Conan 類型的 local repository。創建方法參見https://www.jfrog.com/confluence/display/RTF/Conan+Repositories 。


然後,我們需要執行 Conan 的客戶端命令 “conan remote add” 和 “conna user” 加入這個私有倉庫。具體的執行方法在 Artifactory Repository 的  “Set Me Up” 部分有清晰的描述:

其中 <REMOTE> 作爲這個私有倉庫的別名,會在後續的命令中用來指定使用該倉庫。


私用倉庫創建好之後,我們可以使用 “conan upload” 命令來上傳下載到本地 cache 的依賴包:

其中 -r <REMOTE> 用以指定目標私有倉庫,--all 指定同時上傳 recipe 和 package。

運行之後,在 Artifactory 的 Conan repository 裏就可以看到上傳的依賴包了。

之後當再次需要使用這些依賴包時,就可以在執行 “conan install” 時利用 “-r <REMOTE>” 參數指定從私有倉庫獲取了。


  • 編譯C++應用

執行 “conan install” 之後,除了會下載相應依賴包的二進制 package 之外,還會自動生成編譯相關的參數引用文檔。如在前面的示例中,build 目錄下會自動生成 conanbuildinfo.args 文件,其中包含了編譯過程中如何引用相應依賴包的參數設置。當編譯時,可以直接引用這些參數:

可以看出,基於 Conan 的依賴管理,只要通過 conanfile.txt 描述依賴關係,通過 conan install 命令下載依賴包 package,我們就可以便捷地完成 C++ 應用的編譯。


  • 管理C++應用

當然,從應用開發過程來看,僅僅完成編譯還是不夠的,我們還需要管理好編譯產出的可執行程序,供後續測試、部署、發佈等環節使用。


通常會把應用編譯好的可執行程序存儲到代碼管理系統,如 git 或 svn 中,或者文件服務器,如 ftp 上。但這種方式會丟失掉應用程序特定的 setting 信息,而且會對現有系統造成性能上的影響。


這裏我們還是推薦使用 Artifactory,利用其 Generic repository 來存儲可執行程序。Artifactory 全語言的支持使得能夠在同一個倉庫中統一管理 Conan 依賴包和編譯好的可執行程序。而其企業級高可用的特性能夠保證製品訪問的性能和穩定。


在 Artifactory 裏創建一個 Generic 的 local repository,在其 “Set Me Up” 部分可以看到如何利用 Artifactory 的 rest api 來上傳和下載相應的可執行程序:

同時,我們可以在 repository 裏通過設置不同的目錄來記錄可執行程序的版本。

針對可執行程序特定的 setting,也可以通過Artifactoy 的元數據能力記錄到對應程序的屬性上。


在執行完 “conan install” 之後,build 目錄裏還會自動生成 conaninfo.txt 文件,記錄了本應用及其依賴相關的各種配置參數,其中 [settings]、[full_settings] 部分記錄了相關的 setting 信息:

可以將這些 setting 參數提取出來,利用 Artifactory 的 rest api 寫入屬性:

在 Artifactory 的 repository 裏就可以看到這些屬性了:

此外,Artifactory 還提供了 AQL(Artifactory Query Language),使得我們能夠基於這些元數據屬性設置查詢條件,從而定位與目標 setting 匹配的可執行程序。


設置 aql 文件 1timer.aql 如下:

再利用 Artifactory 的查詢 rest api,我們就可以定位和下載符合 setting 需求的可執行程序,供後續的測試、部署等環節使用了。

同時,我們還可以利用 Artifactory 元數據的能力,將後續環節的關鍵數據,如測試結果,也記錄在該程序的屬性中,從而爲質量監控審批、出錯回溯檢查等提供數據支持。



C/C++持續交付流水線


前面的示例,我們利用 Conan 依賴管理和 Artifactory 私有倉庫實現了完整的 C++ 應用開發流程。在當前 DevOps 應用背景下,我們還需要能夠自動化地重複執行這一流程,以實現 C++ 應用的持續交付。這就需要利用 Conan、Artifactory 和 Jenkins 這一類工具的集成來實現。


Artifactory 提供了 Jenkins 的插件,支持 Jenkins Pipeline 中針對 Artifactory 和 Conan 的 DSL,可以很方便、直觀的實現 Artifactory 和 Conan 的各種操作。前面示例中的各種操作,都可以利用相應的 DSL 語句,集成到 Jenkins Pipeline 當中,如:

完整的 Jenkins Pipeline 代碼在https://github.com/xingao0803/demo-poco-timer.git  中可以找到,供大家參考。


在 Jenkins 中執行該 Pipeline,就可以實現 timer 應用的自動化持續交付流水線:



總結


C/C++ 的依賴管理一直是軟件開發領域的痛點。本文介紹了當前得到廣泛關注和迅速發展的 C/C++ 依賴管理解決方案--Conan 的基本原理和應用流程。同時,利用 timer 應用示例,展示瞭如何基於 Conan、Artifactory 和 Jenkins,實現C/C++應用的持續交付流水線。


當然,本文中的示例只展示了 Conan 最基本的應用場景,大家可以根據相關資料,學習和研究 Conan 更多的案例。我們也會陸續推出後續文章,爲大家進一步展示 Conan 的特性和應用方式。



文章作者:高欣,JFrog中國高級架構師



參考文獻

  • Conan 官網https://conan.io

  • JFrog 官網 http://www.jfrogchina.com

  • Artifactory 文檔 https://www.jfrog.com/confluence/display/RTF/Welcome+to+Artifactory

  • Artifactory社區版 https://docs.conan.io/en/latest/uploading_packages/artifactory_ce.html

  • 示例代碼 https://github.com/xingao0803/demo-poco-timer.git



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章