昨天我們親手打包了 super-lib 套件,從頭到尾跑完,到最後印出一長串套件ID,看起來很有成就感。
那問題來了:conan create 成功,套件就一定能完全沒問題嗎?
答案是:不一定喔。
今天的目標:
test_package,讓 Conan 自動幫我們驗收
拿昨天的 super-lib 來說吧。假如 package_info() 裡面有拼錯字:
def package_info(self):
self.cpp_info.libs = ["super_lib"]
lib 檔名是連字號 super-lib,但是你手滑不小心寫成底線了。這樣子到時候 Linker 一定會有問題的。
但是這時你去跑 conan create 指令:
package(): Packaged 1 '.a' file: libsuper-lib.a
package(): Packaged 1 '.h' file: super_lib.h
Full package reference: super-lib/0.1.0#fc8c144345ba579ede9055fbc69856ca:38b8b2863892fd9462affa45e40edf183b436246#b8ea55c200632fd47bb7c55977b6cea0
咦,結果一樣成功了耶,連警告都沒有。
怎麼會這樣啊?
原來啊,conan create 指令只負責編譯跟打包套件,但是卻不會真的去「試用套件」。
package_info() 裡面的資訊是寫給「套件消費者」看的,要等到有人真的導入了這個套件,內容才會被讀取到。也就是說,這個錯會一路直到另一個專案安裝了 super-lib 時才會被發現。
這時候同事就會跑來問你:「欸,你的套件怎麼發生連結錯誤?」
有點尷尬呀。

怎麼辦呢?解法很簡單:套件打包完,自己先當第一個使用者囉!
這就是 Conan 的標準做法: 寫一個 test_package 幫套件驗收。
test_package 就是個迷你 Conan/CMake 消費端專案。
我們會利用這個迷你專案,實際安裝套件並且編譯出一個叫做 example 的執行檔,並且跑看看。
只要這個流程可以跑通,你至少就可以確定這個套件可以通過編譯,並且編譯後跑起來沒有錯誤。

請到專案目錄底下創建一個子目錄 test_package :
super-lib/
├── conanfile.py
├── CMakeLists.txt
├── include/
├── src/
└── test_package/
├── conanfile.py
├── CMakeLists.txt
└── src/
└── example.cpp
目錄名字 test_package 是約定的慣例,裡面是個迷你 Conan/CMake 專案。
example.cpp 裡就隨便呼叫一下套件內的函數,確實測試一下套件:
#include <super_lib.h>
int main() {
super_init();
}
CMakeLists.txt 記得加 super-lib,除此之外就是個一般專案:
cmake_minimum_required(VERSION 3.15)
project(PackageTest CXX)
find_package(super-lib REQUIRED)
add_executable(example src/example.cpp)
target_link_libraries(example PRIVATE super-lib::super-lib)
接下來重點來了 test_package/conanfile.py:
import os
from conan import ConanFile
from conan.tools.build import can_run
from conan.tools.cmake import CMake, cmake_layout
class SuperLibTestConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeDeps", "CMakeToolchain"
def requirements(self):
self.requires(self.tested_reference_str)
def layout(self):
cmake_layout(self)
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def test(self):
if can_run(self):
cmd = os.path.join(self.cpp.build.bindir, "example")
self.run(cmd, env="conanrun")
這份檔案看起來有點長,但其實跟昨天的食譜差不多。
兩個地方要注意:
一、self.tested_reference_str:這個變數會指向「剛剛打包好的那個套件」,所以不用寫死 super-lib/0.1.0。
二、最後的 test() 方法會實際跑一次編譯出來的執行檔。
這一切都準備好之後,我們就來重新跑一次套件打包。
test_package 這個目錄名稱是約定好的,Conan 看到就會自動用。
我們直接跑一次打包指令,看看能不能抓到拼錯字:
conan create .
看到錯誤訊息了嗎?Library 'super_lib' not found in package。
CMake Error at build/gcc-14-x86_64-gnu17-release/generators/cmakedeps_macros.cmake:81 (message):
Library 'super_lib' not found in package. If 'super_lib' is a system
library, declare it with 'cpp_info.system_libs' property
...
ERROR: super-lib/0.1.0 (test package): Error in build() method, line 20
cmake.configure()
ConanException: Error 1 while executing
剛剛沒人發現的錯,現在打包的過程中就被攔下來囉。
把 libs 改回 ["super-lib"],再跑一次:
======== Launching test_package ========
...
super-lib/0.1.0 (test package): Running test()
Hello from super-lib/0.1.0
這次打包就成功了,看到 Hello from super-lib/0.1.0 了!
所以完整來說,conan create 其實做了四件事:匯出、編譯、打包,最後用 test_package 驗收。
順帶一提,test_package 只是驗收用的,不會被打包進套件裡,所以不用擔心它跟著套件跑到同事那邊。
以後打包自己的套件,記得順手寫一個 test_package,同事會很感謝你的。