iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0
Software Development

30 天 CMake 跨平台之旅系列 第 16

[Day 16] 管理 Library 版本

  • 分享至 

  • xImage
  •  

本日內容

  • Shared Library Versioning
  • Library Compatibility
  • 預告

連結: 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 時可能需要注意的地方

讓我們開始吧!

Shared Library Versioning

在 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 VERSIONSOVERSION, 下面簡單說明這兩 properties 個是什麼東西

Version, SO version?

Version, 就是 library 的完整版本, 通常包含 [major[.minor[.patch]]]

  • major
    • 通常是有 API 改動 (breaking change) 才會增加此版號
  • minor
    • 通常是有增加新功能或修改現有功能, 但是向後相容的就會增加此版號
  • patch
    • 通常是不影響 spec 行為的修正, 比如修復 bug

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 Compatibility

除了 library version 以外, 另外一個對相容性很重要的 target property 是 COMPATIBILITY_INTERFACE_<type> <value>

  • type
    • 變數類型, 包含
    • BOOL
    • NUMBER_MAX, NUMBER_MIN
    • STRING
  • value
    • 給定的變數名稱
    • 可以當作 compiler definitions 傳給 compiler

該 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 MessageUtil, 並設定相反的 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 告訴我們 MainIS_TEST 設定和 Util 不一致, 所以停止執行

預告

記得之前提過的 nmreadelf 嗎? 下一篇會用這兩個工具, 更深入的去看 binary 檔案中到底有哪些東西


上一篇
[Day 15] 加入 Open source library
下一篇
[Day 17] 什麼是 Symbols?
系列文
30 天 CMake 跨平台之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言