連結: Day16 - Colab
我們在 Day11 到 Day13 中知道了該如何自己寫各種類型的 libraries, 也知道了進階的 Link Seams 的操作
也分別在 Day14 和 Day15 介紹了如何將外部套件引入我們的 CMake 專案, 從下載, build from source, 到讀取 package config (ex. <package-name>Config.cmake
, Find<package-name>.cmake
, <package-name>.pc
) 取得該套件的 library target 並加入我們專案的流程
但我們還缺少了最重要的部分: 管理 Library 的版本!
除了之外, 也會稍微帶過 link libraries 時可能需要注意的地方
讓我們開始吧!
在 Unix 系統上, shared library 的檔名通常會是 <libname>.so
, 就像我們在 Day 12 build 出的 shared library 一樣
我們來偷看一下系統的 shared libraries
ls -l /lib/*.so
rwxrwxrwx 1 root root 18 Feb 7 2022 /lib/libarmadillo.so -> libarmadillo.so.10
lrwxrwxrwx 1 root root 17 Sep 14 2021 /lib/libdfalt.so -> libdfalt.so.0.0.0
lrwxrwxrwx 1 root root 17 May 29 2022 /lib/libgdal.so -> libgdal.so.30.0.3
lrwxrwxrwx 1 root root 20 Sep 14 2021 /lib/libmfhdfalt.so -> libmfhdfalt.so.0.0.0
lrwxrwxrwx 1 root root 14 Jun 14 2021 /lib/libogdi.so -> libogdi.so.4.1
lrwxrwxrwx 1 root root 13 Aug 4 02:01 /lib/libR.so -> R/lib/libR.so
lrwxrwxrwx 1 root root 13 Jun 14 2021 /lib/libvpf.so -> libvpf.so.4.1
可以看到這些 shared libraries 大部分都只是 symlinks, 比如 /lib/libdfalt.so
他是指向同路徑下的 libdfalt.so.0.0.0
由於 dynamic libraries 的 symbols 是在 runtime 才會被處理並將 virtual memory mapping 到實體記憶體位置, dynamic linker (or ld.so
) 會根據 symlinks 去找到真正的 shared library 版本
這樣做的好處是, 我們只需要 link library 名稱就好, 只要 library 的 API 版本沒變, 即使某些 functions 改變了也會向後相容, 所以通常不會有什麼問題
所以, 為了讓使用我們 libray 的人能夠知道自己使用的版本, 我們最好加上 target properties VERSION
和 SOVERSION
, 下面簡單說明這兩 properties 個是什麼東西
Version, 就是 library 的完整版本, 通常包含 [major[.minor[.patch]]]
major
minor
patch
SO version, 通常是 major
版號, 且也是一個 symlink
會指向相同 major
底下最新的版本, 比如我們今天提供的 Colab 範例
total 32
drwxr-xr-x 3 root root 4096 Sep 16 05:34 CMakeFiles/
-rw-r--r-- 1 root root 1244 Sep 16 05:34 cmake_install.cmake
lrwxrwxrwx 1 root root 15 Sep 16 05:34 libMessage.so -> libMessage.so.1* 👈
lrwxrwxrwx 1 root root 19 Sep 16 05:34 libMessage.so.1 -> libMessage.so.1.0.0* 👈
-rwxr-xr-x 1 root root 14984 Sep 16 05:34 libMessage.so.1.0.0* 👈
-rw-r--r-- 1 root root 5921 Sep 16 05:34 Makefile
可以看到我們 libMessage.so
是一個 symlink 指向另一個 symlink libMessage.so.1
, 該 symlink 最後才會指向真實的版本 libMessage.1.0.0.so
代表當程式執行時, dynamic linker 會從 library 的 soname (即代表 so version 的檔案名稱) 一路找到真正的檔案來分配記憶體
有了 so version, 就不需要常常修改 link 的 libray 版本了, 讚讚
除了 library version 以外, 另外一個對相容性很重要的 target property 是 COMPATIBILITY_INTERFACE_<type> <value>
type
BOOL
NUMBER_MAX
, NUMBER_MIN
STRING
value
該 property 會產生 INTERFACE_<value>
, 就可以用來比較 libraries 是否相容
比如, 假設 libA.so.1 升級成 libA.so.2 了, 但他提供了相容的選項, 比如一個 compiler definition IS_OLD
並預設為 true, 讓使用升級版本 library 的 binary 不會壞掉
也可以用來檢查其他使用到 libA.so 的 library 所用的設定, 比如 libB.a 已經更新 libA.so 到新版, 並將 IS_OLD
設為 false
這樣當我們在 link libA.so 和 libB.a 的時候, CMake 會發現 binary 和 libB.a 對於 libA.so 的設定不同而丟出 error
比如今天的 Colab 的 src/lib/CMakeLists.txt
中, 我們新增 libraries Message
和 Util
, 並設定相反的 compatible interface IS_TEST
, 最後讓 executable link 他們
add_library(Message SHARED
message.cpp
)
add_library(Util STATIC
util.cpp
)
set_target_properties(Message PROPERTIES
VERSION 1.0.0
SOVERSION 1
COMPATIBLE_INTERFACE_BOOL IS_TEST
INTERFACE_IS_TEST TRUE
)
set_target_properties(Util PROPERTIES
VERSION 1.0.0
SOVERSION 1
COMPATIBLE_INTERFACE_BOOL IS_TEST
INTERFACE_IS_TEST FALSE
)
target_include_directories(Message PUBLIC
${PROJECT_SOURCE_DIR}/src/include
)
在 compile 時就會遇到以下 error
-- Configuring done (0.0s)
CMake Error: The INTERFACE_IS_TEST property of "Util" does
not agree with the value of IS_TEST already determined
for "Main".
CMake Error: The INTERFACE_IS_TEST property of "Util" does
not agree with the value of IS_TEST already determined
for "Main".
-- Generating done (0.0s)
CMake Generate step failed. Build files cannot be regenerated correctly.
make: *** [cmake_check_build_system] Error 1
上面的 error 告訴我們 Main
的 IS_TEST
設定和 Util
不一致, 所以停止執行
記得之前提過的 nm
和 readelf
嗎? 下一篇會用這兩個工具, 更深入的去看 binary 檔案中到底有哪些東西