iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0
Software Development

C++ 套件管理器求生指南 Conan vs Vcpkg系列 第 27 篇

D27:Conan 最常見錯誤 Missing prebuilt package 談 Package ID 與相容性

  • 分享至 

  • xImage
  •  

使用 Conan 時最常看到的錯誤就是 Missing prebuilt package。

咦,ConanCenter 不是都有預編譯好的套件嗎?那為什麼我跑 conan install 指令,還是常常看到一大片紅紅的 Missing prebuilt package ?

說好的「下載就能用」呢?

答案就是: ConanCenter 有預編譯套件可供下載,但不一定有「為你量身訂做」的那一份。

一、案發現場:Missing prebuilt package 為什麼?

我們前面提過,C++二進位檔案之間的相容性錯綜複雜,到底 Visual Studio 2026 編譯出來的檔案,Visual Studio 2019 能不能用呢? GCC 10 跟 GCC 13 的相容性呢? Debug 版跟 Release 版的二進位檔案呢?預編譯套件用了 C++20 標準,可是下游消費端只能跑 C++17,這樣可以直接下載嗎?

上面那一串,我可以跟你說,有的可以,有的不行。實在太多條件跟狀況了,而 Conan 盡可能的把這些不相容的地方考慮進來,所以在真實的專案情境下(例如:我這個專案就是要用 MSVC2026 + /MT + C++26),幾乎無可避免的會恰好碰到一個小地方沒對上,最終就無法下載伺服器上的預編譯二進位檔。

所以從外面看「這家店明明有賣這道菜呀」,但是實際走進店裡,卻發現店家不接受「不加香菜大辣」特製餐點。

二、案件偵查:哪些東西讓 binary missing?

1. 編譯設定對不上

D14 我們講過 Package ID 這個東西:Conan 把套件的 settings、options 和依賴丟進去算 hash,算出來的就是 package ID,每個 package ID 對應一個 binary。

所以下面這些,只要改一項,就是另一個新的 package ID:

  • os: Windows 或者 Linux
  • arch: x86_64 或者 armv8
  • compiler: msvc / apple-clang / gcc
  • compiler.version: 194 vs 193 (MSVC)、17 vs 21 (Xcode)
  • compiler.runtime: MSVC 的 /MD(dynamic)vs /MT(static)
  • compiler.cppstd: C++語言標準 14、17、20…
  • build_type: Release vs Debug
  • options: shared=True/False、header_only

只要有一個東西沒對上,抱歉,預編譯二進位檔可能就是跟你無緣,無法套用。

2. Recipe 改版

另一個可能造成 binary 突然消失的隱形原因:Recipe 改版(recipe revision)。

  1. 版本號雖然一樣是 fmt/12.2.0,但是 Recipe,也就是打包腳本改了。
  2. 打包腳本更新了,但伺服器沒有用新的打包腳本去編譯二進位檔出來。
  3. Conan 預設自動去抓最新版的 Recipe 腳本。
  4. Conan 拿到了最新的 Recipe,去伺服器要二進位檔,撲了個空。

3. 間接依賴改動

還有一個比較少人注意的間接依賴。

比如你的程式引入了 cURL 這個套件,然後 cURL 套件需要 zlib,zlib 就是間接依賴。

今天狀況如下, cURL 雖然沒變,但是上游套件改版了,zlib 1.3.0 → 1.3.1 升版,請問 cURL 套件的預編譯二進位檔還可以用嗎?

這個就有趣了,實際上要看 zlib 到底是怎麼編譯跟連結的。如果 zlib 只是小改版修 BUG,而且公開函數的界面、記憶體布局都沒變,那「有機會」不需要重編。

到底需不需要重編,Conan 有相當細微的設定。官方稱之為 embed / non-embed 模式,有興趣者可用這個關鍵字下去鑽研細節。

一般使用者只要知道「間接依賴升版,也可能害你的套件 package ID 變動而 Missing」就夠了。

三、所以到底有什麼可以下載啦?

ConanCenter 上到底有什麼可以下載,可以直接問伺服器:

conan list "fmt/12.2.0:*" -r conancenter

我把 fmt 12.2.0 的結果整理成表格:

平台 編譯器 C++ 標準 build_type shared
Windows x86_64 msvc 194 14 Release True / False
Windows armv8 msvc 194 14 Release True / False
macOS x86_64 apple-clang 17 gnu17 Release True / False
macOS armv8 apple-clang 17 gnu17 Release True / False
Linux x86_64 gcc 13 gnu17 Release True / False

再加上一個 Header Only 的版本,總共只有 11 個預編譯的版本。

你有沒有發現一個有趣的狀況:怎麼全部都是 Release 版本的二進位檔?意思是,如果你用 Debug 編譯專案,那麼就抓不到官方伺服器上的預編譯二進位檔案了。

事實上,ConanCenter 根本不可能把全世界每種編譯器版本、每種 CRT、每種 options 組合都編一份。它只能挑主流平台加上幾個固定的編譯器組合。

用 conan list ... -r conancenter 這個指令查詢套件,非常好用。

四、相容性:Conan 會先幫你找替代品

不過,找不到「一模一樣」的 package ID,Conan 也不會馬上放棄。

Conan 有一套 binary compatibility 相容機制。

比方說,Conan 有一條內建規則:同一個編譯器、同一個編譯器版本下,不同 compiler.cppstd 編出來的二進位檔視為相容。

比如說,我的專案是 C++17,官方伺服器上的 fmt/12.2.0 用的是 C++14。那麼我呼叫 conan install 的時候,Conan 判斷 C++14 的那份二進位檔案相容,可以直接拿來用。

用 C++14 編的 fmt 給 C++17 專案連結,應該是沒問題啦。

Windows 上它還會試圖去找 MSVC 的前一版 compiler.version=193,這是 Conan 對 msvc 的額外相容規則。因為微軟官方保證了 VS 2015 (190) 到 VS 2022 (194) 二進位向後相容,用 193 編出來的 library 給 194 連結是安全的,所以 Conan 敢大膽通融。反觀 Apple Clang 並沒有做出保證,Conan 就不敢跨版本相容。

但是除了少數例外,其實大部分編譯器設定還是會造成不相容。

五、找出不相容的原因

有一個超好用的指令:就是 conan graph explain。

只要把 conan install 裡面的字替換成 graph explain,他就會告訴你「最接近的那份 binary」,然後直接告訴你差異在哪裡。

比如我們用以下指令故意下達 static runtime 的要求。

conan graph explain . -s compiler.runtime=static

輸出

======== Closest binaries ========
fmt/12.2.0
  fmt/12.2.0#b445244e6d0151a425e229248cd2bed8:159b372171ff86fd7630aefcf978b21a606abf85
    remote: conancenter
    settings: Windows, x86_64, Release, msvc, 14, dynamic, Release, 194
    options: header_only=False, shared=False, with_os_api=True, with_unicode=True
    diff
      settings
        expected: compiler.runtime=static
        existing: compiler.runtime=dynamic
      explanation: This binary was built with different settings.

Conan 直接告訴你:你想要 static runtime,伺服器上只有 dynamic runtime。

以後看到 Missing prebuilt package,第一件事就是先打 graph explain,不用猜。

六、無法下載時怎麼辦?

找到問題之後,你有三條路可以選。

路線 A:改設定,去抓現成的

如果那個設定不是你非用不可的(例如只是 profile 裡不小心把 compiler.runtime 設成 static),改回官方有的設定,就可以直接下載現成的二進位檔啦。

路線 B:在本機編

真的需要那個設定,就讓 Conan 自己編譯就好了:

conan install . -s compiler.runtime=static --build=missing

只要在安裝的時候,下達 --build=missing 的指令,Conan 就會在本機上編譯缺漏的套件。

我的電腦上只用了 11 秒就編譯完成 fmt。編譯好的二進位留在本機快取,同一台電腦以後不用再編譯一次。

--build 有好幾種寫法:

  • 不加 --build:找不到 binary 就報錯
  • --build=never:明確禁止任何本機編譯
  • --build=missing:找不到(含相容 binary 也找不到)的才現場編
  • --build=missing:fmt/*:只允許 fmt 本機編譯,其他套件找不到照樣報錯
  • --build=fmt/*:fmt 一律從原始碼重編,不管伺服器上面有沒有
  • --build="*":全部重編

注意 pattern 要寫 fmt/*,不是只寫 fmt。平常開發 --build=missing 最省事。

路線 C:編一次,大家共用

如果團隊專案就是指定用了一個官方沒有的編譯器設定,團隊裡的十個開發者,難道十個人都要編一次?CI 上每一台機器也要編一次嗎?

不用啦。只要有一個人編譯好之後,上傳到團隊自己的 remote 伺服器:

conan remote add mycompany https://<your-remote-url>
conan upload "fmt/12.2.0:*" -r mycompany -c

之後同事安裝套件時,Conan 會到自架伺服器上面去找。只要算出來是同一個 package ID,就會從伺服器直接下載二進位檔,不必再編譯。

當然,這個解法需要自己架設 Conan server(例如 Artifactory),這也是大多數企業處理內部函式庫的方法。remote 怎麼加、怎麼上傳,請看 D19。


上一篇
D26:Conan 動態庫部署 deployer
下一篇
D28:vcpkg Binary Caching 二進位快取
系列文
C++ 套件管理器求生指南 Conan vs Vcpkg 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言