包管理一直是c/c++項目開發過程中無法觸及的痛苦之處。由于出現歷史太早,c/c++發展初期并沒有現代的包管方案的概念,這部分一直需求一直被忽略。
等到開發者想起要解決這個問題時,發現c/c++與硬件結合太過于緊密,支持太多的平臺,在平臺適配上有大量的工作需要處理,對于一個編譯型語言,需要包管理需要能夠正確的處理不同的平臺上的二進制兼容問題或者源碼編譯問題,這導致想基于現有的代碼來實現一個現代化的c\c++的包管理系統基本上是一個不可能完成的任務。于是開發者很明智的選擇了rust。
盡管如此,人們還是給出了一些折衷的結局方案,包括linx上的apt,mac上的brew,windows上的nutget等方案。其中apt的方案主要解決二進制分發問題,對版本的控制較弱。其中brew/nutget等已經初具一個現代化包管理系統的雛形了。
在這包管理的基礎上,出現像conan/vcpkg/buckaroo這樣比較優秀的現代化的包管理系統。本文將著重介紹conan的基本概念和主要使用方法。
Conan核心概念
編譯配置
在給定版本的源碼情況下,的如何來識別構建的二進制是否兼容,對于c\c++程序來說,這是一個不能完成的任務,因為同一份源碼,可以獲得完全不同的結果,這回設計到編譯器/目標平臺等各種因素。甚至一個簡單的宏開關,都會導致編譯的二進制結果時完全不同的。
Conan通過settings/options來識別一個源碼編譯出來的結果是否為同一份二進制。一個常見的conan配置如下:
-
> conan profile show default
-
Configuration for profile default:
-
-
-
-
-
-
-
-
-
-
-
-
即在給定的profile下,對于同一份源碼,應該要生成同樣的二進制文件。
引用
Conan的另外一個概念叫做reference,reference的組成如下
Pkg/version@user/channel
例如
-
-
qt/5.6.3@bincrafters/stable
-
dtkcore/2.0.9@iceyer/stable
-
dtkcore/2.0.9@iceyer/testing
-
dtkwidget/2.0.9@iceyer/stable
Pkg和version比較容易理解,代表包名和版本,user代表創建包的用戶,stable表示這個包的發布通道。
不同用戶創建的包其實沒啥關系的,總之一個完整的reference才能代表一份源碼。
包
有了源碼和編譯配置,我們很自然的想到了,可以用來構建我們需要的二進制了。Conan中的package是指的根據profile構建出來的二進制文件的集合,這個一定要搞清楚。在一個reference下,我們可以根據不同的參數來構建不同的package,如構建不同os的版本,構建包含不同特性的版本等。用一張官方的圖來解釋下:
通過上面的圖,聰明的同學一定發現槽點了:conan是基于python的,并且是和cmake強綁定的… 特別是cmake這種面向字符串編程的工具,似乎有著成為c++構建工具的事實標準的趨勢發展。
實戰Windows靜態程序構建
目前Windows下的包管理一直是粗獷的拷貝式管理,這導致了很多工程實踐上的不便。通過Conan,我們可以很好來解決這些問題。以dtkcore為例,我們將描述如果構建一個靜態的dtkcore。
首先我們需要一個靜態構建的qt,這部分比較復雜,這里暫時不展開了,可以通過添加一個remote倉庫來使用已經構建好的:
conan remote add iceyer https:
創建包
通過conan new可以創建一個包描述文件,我們在dtkcore源碼目錄執行如下命令創建一個conan模板文件conanfile.py:
conan new dtkcore/2.0.9@iceyer/stable
通過修改conanfile.py,可以讓dtkcore構建可用的二級制包,最終的conanfile.py如下:
-
from conans import ConanFile, tools
-
-
-
class DtkcoreConan(ConanFile):
-
-
-
-
author = 'Iceyer me@iceyer.net'
-
url = 'https://github.com/linuxdeepin/dtkcore'
-
description = 'cross platform ui library'
-
-
settings = 'os', 'compiler', 'build_type', 'arch'
-
options = {'shared': [True, False]}
-
default_options = 'shared=False'
-
-
-
requires = 'jom_installer/1.1.2@bincrafters/stable', 'qt/5.6.3@iceyer/stable'
-
-
def extend_include_path(self):
-
return '%s/include/libdtk-%s/DCore' % (self.package_folder, self.version)
-
-
-
-
-
-
-
outdir = self.build_folder
-
-
mkspecsdir = outdir + '/mkspecs'
-
-
-
env_vars = tools.vcvars_dict(self.settings)
-
env_vars['_CL_'] = '/utf-8'
-
with tools.environment_append(env_vars):
-
-
command += ' VERSION=%s' % self.version
-
-
-
command += ' CONFIG+=release'
-
command += ' PREFIX=%s' % outdir
-
command += ' MKSPECS_INSTALL_DIR=%s' % mkspecsdir
-
if self.options.shared == False:
-
command += ' DTK_STATIC_LIB=YES'
-
command += ' DTK_STATIC_TRANSLATION=YES'
-
command += ' DTK_NO_MULTIMEDIA=YES'
-
command += ' %s' % self.source_folder
-
-
-
-
-
-
-
self.update_path(self.build_folder)
-
-
outdir = self.build_folder
-
self.copy('*', dst='include', src=outdir+'/include')
-
self.copy('*.lib', dst='lib', src=outdir+'/lib')
-
self.copy('*.dll', dst='lib', src=outdir+'/lib')
-
self.copy('*', dst='mkspecs', src=outdir+'/mkspecs')
-
-
-
self.cpp_info.libs = ['dtkcore']
-
self.cpp_info.includedirs.append(self.extend_include_path())
-
self.env_info.QMAKEPATH = self.cpp_info.rootpath
-
self.env_info.QMAKEFEATURES = self.cpp_info.rootpath + '/mkspecs/features'
-
-
-
self.update_path(self.package_folder)
-
-
def update_path(self, source):
-
-
-
module_pri = source + '/mkspecs/modules/qt_lib_dtkcore.pri'
-
-
for line in s.readlines():
-
if line.startswith('QT.dtkcore.tools'):
-
line = 'QT.dtkcore.tools = %s\n' % (
-
self.package_folder + '/bin')
-
elif line.startswith('QT.dtkcore.libs'):
-
line = 'QT.dtkcore.libs = %s\n' % (
-
self.package_folder + '/lib')
-
elif line.startswith('QT.dtkcore.includes'):
-
line = 'QT.dtkcore.includes = %s\n' % (
-
self.extend_include_path())
-
-
-
-
-
s = open(module_pri, 'w')
-
-
except FileNotFoundError:
-
print('skip update qt module file')
Conan配置
其中DtkcoreConan屬性部分大部分是關于包的一些描述信息,可以不用關心,其中需要注意的幾個屬性和方法是:
requires
requires = 'jom_installer/1.1.2@bincrafters/stable', 'qt/5.6.3@iceyer/stable'
這個描述了dtkcore的依賴信息,對與windows來說,有qt就足夠了,更復雜的情況,可以通過requires方法來返回不同平臺對應的依賴。
build(self)
在命令行執行conan build時,會調用這個方法,需要在這里完成源碼的構建。注意這里通過tools.vcvars_dict來導入的windows平臺的Visual Studio的構建環境,這個在不同平臺需要做相應的處理。當然,對于cmake程序可以使用Conan官方提供的工具,由于dtkcore時通過qmake構建的,這里需要實現構建過程。
Dtkcore在這里主要完成了windows平臺編譯環境導入和調用qmake/jom進行構建等功能。
package(self)
在命令行執行conan package時,會調用這個目錄,將編譯結果拷貝到self.package_folder目錄。由于dtkcore使用了qt的module功能,這個東西需要絕對路徑,所以這里會實現一個update_path將qt_lib_dtkcore.pri里面的路徑修改為真實的安裝路徑。
package_info(self)
在其他程序使用這個庫時,會調用這個方法獲取庫信息,主要可以配置的東西有環境變量已經cpp的構建信息,參考如下:https://docs.conan.io/en/latest/reference/conanfile/attributes.html#cpp-info
Dtk由于要支持qt風格的引入方式,會添加一個額外的includepath,也是通過這里來處理的。
deploy(self)
這里時將包給其他人使用時,使用者將包安裝到自己系統路徑時調用的方法,注意這里如果需要修改一些路徑問題,也會在這里處理。
Conan使用
在構建好dtkcore/dtkwidget后,就可以在其他地方使用了,以deepin-boot-maker為例,在deepin-boot-maker源碼中添加一個構建描述文件:
-
from conans import ConanFile, CMake, tools
-
-
-
class DeepinbootmakerConan(ConanFle):
-
name = "deepin-boot-maker"
-
-
-
-
-
-
-
settings = "os", "compiler", "build_type", "arch"
-
options = {"shared": [True, False]}
-
default_options = "shared=False"
-
-
requires = 'dtkcore/2.0.9@iceyer/stable', 'dtkwidget/2.0.9@iceyer/stable', 'OpenSSL/1.0.2n@conan/stable', 'jom_installer/1.1.2@bincrafters/stable'
-
-
-
outdir = self.build_folder
-
-
env_vars = tools.vcvars_dict(self.settings)
-
env_vars['_CL_'] = '/utf-8'
-
with tools.environment_append(env_vars):
-
-
command += ' VERSION=%s' % self.version
-
-
-
command += ' CONFIG+=release'
-
command += ' PREFIX=%s' % outdir
-
command += ' DEFINES+=DTK_STATIC_LIB'
-
command += ' DTK_STATIC_TRANSLATION=YES'
-
command += ' DTK_NO_MULTIMEDIA=YES'
-
command += ' %s' % self.source_folder
-
-
-
-
然后使用如下命令構建即可:
-
-
-
conan install .. -s compiler.runtime=MT -s arch=x86 -o qt:qtsvg=True -o qt:qttools=True
-
conan 會自動從遠處下載好依賴,并使用依賴進行構建。
總結
Conan很好的解決了c\c++項目中的源碼管理和編譯配置管理的問題,并提供了強大的中心化二進制管理和分發功能,基于python的conanfile.py的配置文件有及其強大的擴展性,可以很好解決c\c++項目中的二進制管理問題。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。