手動管理 C/C++ 套件

想像你需要使用 LLVM 開發一個程式,如果在 macOS 上,最簡單的安裝方法是用 Homebrew,一行就搞定: brew install llvm。但 Homebrew 上的版本不一定是最新的而且也無法同時安裝不同的版本 (例如 LLVM 5.0/6.0 共存) 或同版本但不同設置(例如 LLVM 6.0 的 debug/release 版本);而在 Linux 及 Windows 上也有各自不同的安裝問題。

安裝完畢後,麻煩才剛開始:通常第一步是設置編譯環境的 CPPFLAGS 及 LDFLAGS:

For compilers to find this software you may need to set:
    LDFLAGS:  -L/usr/local/opt/llvm/lib
    CPPFLAGS: -I/usr/local/opt/llvm/include

根據不同的開發環境或編譯器,設置的方法也都不同。最後設置完後編譯及連結也不一定能成功,因為 LLVM 本身可能又依賴其他套件,還需要把它的 dependencies 一一安裝及設置。

Conan 介紹

Conan 企圖幫助 C/C++ 脫離這個窘境:沒有一個像樣的套件管理 (package management) 工具。

我建議先讀一下 Conan 的文檔來了解如何安裝 Conan 以及利用 Conan 來使用現成的程式庫

如果你是使用 CMake,事情會簡單一些,因為 Conan 提供的 CMake 整合還算好用,使用範例可以參考這個基於 LLVM 的小程式: clike

下載套件

除了預設的 conan-center,Conan 官網建議也可以到 bincraftersconan-community 裡尋找,可以用下列指令新增這兩個 remote:

$ conan remote add conan-community https://api.bintray.com/conan/conan-community/conan
$ conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan

之後就可以指定 remote 搜尋 package:

$ conan search ffmpeg -r=bincrafters
Existing package recipes:

ffmpeg/[email protected]/stable

目前 server 上面的套件數量不算多,有可能你會找不到你想要的,因此你不能期待很快地有人會幫你做出來,相反地你必須學會自己創建套件才能充分得到使用 Conan 的好處。

創建 package

自己創建一個套件其實也不難,基本的概念如下:

整個建置過程需要的目錄結構及流程會是由 Conan 來控制,但你需要提供一個 conanfile.py 描述每個步驟裡的需要執行的動作。Conan 會把打包的套件放在一個 local cache 裡讓其他的程式來使用。

以下示範如何下載 LLVM 6.0 的原始碼, 編譯,最後把它包裝成套件:

from conans import ConanFile, CMake
import glob
import os

class LlvmConan(ConanFile):
    name = "LLVM"
    version = "release_60"
    license = "LLVM Release License"
    url = "https://github.com/p47r1ck7541/llvm-60"
    description = "%s %s" % (name, version)
    settings = "os", "compiler", "build_type", "arch"
    generators = "cmake"

    def source(self):
        self.run("git clone https://github.com/llvm-mirror/llvm -b %s --depth 1 src" % self.version)

    def build(self):
        cmake = CMake(self)
        cmake.configure(source_folder="src")
        cmake.install()

    def package(self):
        # nothing to do here now because we reuse 'cmake install' to package files
        pass

    def package_info(self):
        self.cpp\_info.libs = \[os.path.basename(a) for a in glob.glob(os.path.join(self.package\_folder, "lib", "*.a"))\]
        self.cpp_info.cppflags = \["-std=c++11", "-fno-rtti"\]
        self.cpp_info.exelinkflags = \["-lcurses", "-lz"\]

幾個關鍵步驟:

  1. source(): 利用 git clone 下載原始碼
  2. build(): 負責編譯,這裏我們直接使用 Conan 提供的 CMake class 來控制 cmake 的執行,值得注意的是我們直接利用 cmake.install() 來取代實際打包套件的工作。
  3. package(): 如上解釋,不需做任何事。
  4. package_info(): 提供套件的相關使用資訊。例如 cppflagsexelinkflags 是寫死的,但 libs 則是運行 Python script 搜尋編譯結果找出來的。

完整範例可以參考這個 GitHub project: conan-llvm-60

實際運作過程如下(中間請自行快轉):

總結

就簡化自己手動管理 C/C++ 套件的工作來看,我認為 Conan 提供的好處值得你花時間去學它,至於它是否能成為主流的 C/C++ 套件管理工具,那就等待時間來驗證了。