你有沒有用過 FFmpeg?
FFmpeg 函式庫超級大一包,支援超級多不同的影片格式,所以 ffmpeg 執行檔很肥。
如果我不需要支援某些冷門影片格式,可以拿掉嗎?其實可以的,ffmpeg 專案提供了很多編譯選項,讓你在編譯專案的時候可以啟動或關閉特定格式的支援,讓執行檔瘦身。
而且 FFmpeg 的選項不只是省時間、省空間,還牽涉到授權。像是 x264/x265 這兩個編碼器,只要一開就會讓 FFmpeg 變成 GPL 授權;開啟的 libfdk_aac 更會讓它變成 nonfree。如果你在做商用或閉源產品的話,這些是一定要關掉的。

那 Conan 可以處理這類的問題嗎?答案是「可以」。
Conan 有一個功能叫做 option,就是讓你客製化套件的編譯流程。
像是 ConanCenter 上的 ffmpeg recipe 就有 90 個選項,讓你量身定做徹底客製化套件。
另一個例子是 Boost。Boost 超大一包,裡面有一大堆不同的模組。有時候我只是想用其中一包 Boost.Asio,但是還是要整包抓下來。可以客製化嗎?
還好,ConanCenter 上的 Boost 替每個子模組都準備了一個開關,要哪些、不要哪些,自己選。
我們今天就來介紹這些在 Conan 裡叫做 options 的開關。
Options 要怎麼用呢?
很簡單,在下安裝指令時,後面加上 -o 就好了:
conan install . -o "boost/*:without_log=True"
格式是 -o "套件/*:選項=值",意思是「boost 的所有版本,without_log 都設成 True」,也就是不要編 log。引號是避免 shell 把 * 當成萬用字元處理掉。
要設好幾個選項,就多加幾個 -o:
conan install . -o "ffmpeg/*:with_libx264=False" -o "ffmpeg/*:with_libx265=False"
如果不想每次都打一長串,也可以寫進 conanfile.txt 效果是一樣的:
[options]
ffmpeg/*:with_libx264=False
ffmpeg/*:with_libx265=False
ffmpeg/*:with_libfdk_aac=False
不過要小心,萬一選項的「名字」打錯字的話(例如把 with_libx264 打成 with_libx246),Conan 是不會報錯的喔,只會安安靜靜地略過。所以設完選項,最好確認一下有沒有真的生效。
問題來了,我要怎麼知道有哪些開關呢?
可以去查 ffmpeg conanfile.py。
https://github.com/conan-io/conan-center-index/blob/master/recipes/ffmpeg/all/conanfile.py
就可以看到 options = { 變數下面那一長串的開關囉。
今天換我們當套件作者,能不能替 super-lib 自己加一個編譯選項呢?
到底是要小辣還是大辣?要不要蒜頭?要不要香菜?當然可以自己客製。

今天我們要加的選項叫 debug_log。開發的時候,常常想多印一些除錯訊息,但正式版要關閉,避免敏感資訊外洩,所以預設是關閉(False),需要的人再自己打開。
我們接下來要進行的事項:
options 和 default_options
首先請到 super-lib 的 conanfile.py 加入下面兩行:
settings = "os", "compiler", "build_type", "arch"
options = {"debug_log": [True, False]}
default_options = {"debug_log": False}
options 告訴 Conan:這個套件有個叫 debug_log 的選項,只接受 True/False。default_options 告訴 Conan:使用者沒指定時,選 False。
接下來要怎麼把 Conan option 傳遞給 CMake 呢? 請修改 generate():
def generate(self):
tc = CMakeToolchain(self)
tc.cache_variables["ENABLE_DEBUG_LOG"] = bool(self.options.debug_log)
tc.generate()
這樣就會設定 CMake ENABLE_DEBUG_LOG 變數了。
第三步,我們要讓 CMake 變數去實際影響 C++ 原始碼的行為。
請再 CMakeLists.txt 加入巨集定義:
if(ENABLE_DEBUG_LOG)
target_compile_definitions(super-lib PRIVATE ENABLE_DEBUG_LOG)
endif()
src/super_lib.cpp:
void super_init() {
#ifdef ENABLE_DEBUG_LOG
std::cerr << "[debug] super_init() called\n";
#endif
std::cout << "Hello from super-lib/0.1.0\n";
}
整條路徑是:

接下來,我們需要給兩個選項 debug_log=True or False 各自編譯一份二進位檔出來。
conan create . # 不加選項就是用預設的 False
conan create . -o "super-lib/*:debug_log=True"
套件打包完成後,用指令查一下本機快取 conan list "super-lib/0.1.0:*"
super-lib/0.1.0
revisions
db0bb9b3e89f4e659b8e9cb36a439882
packages
69074d771e60ee9fed66ab28f125ac14f5820c42
options
debug_log: True
b316527b247c5091c21870d91d1c61ad342833b9
options
debug_log: False
你會看到一個 Recipe Revision,因為只有一份食譜。但是下面有兩個 package ID。一個啟用 debug_log,一個沒有。Conan 分開存了兩份二進位檔。
注意,萬一漏掉其中一包忘了編譯,Conan 不會偷偷拿另一包頂替喔,會直接報錯 Missing binary。
提供選項之後,消費端要怎麼挑選 option 呢?
可以跟文章開頭一樣用命令列的 -o。切換選項後要重新 conan install 並重新編譯 app。
conan install -o "super-lib/*:debug_log=True"
或者寫進 conanfile.txt 裡面:
[options]
super-lib/*:debug_log=True
用 -o 或 [options] 選擇,兩邊是在設定同一件事喔。
今天的
debug_log只是示範。真實套件常見的選項有shared、fPIC等等。