合理利用开发文件的版本信息
对于供他人调用的库(尤其是活跃开发中,接口经常变化的)来说,版本信息非常重要。版本变化意味这接口变化。应用引入库时也应该检查版本号,方便其他人编译。比如,某个开发库 A 新增了一个头文件,在升级了这个库依赖后引入了这个头文件。外部开发者想要编译这个软件, 最后只能得到 “xxx.h not found” 的编译错误。 如果头文件命名和库名有关还好点,很多时候根本看不出来到达是那里出错了,是自己的环境被破坏了,还是自己安装的某个依赖版本太高了还是太低了,如果是,是具体哪个依赖,正确的版本又是什么。这需要花费很多时间才能解决。如果加上版本检查,在编译开始之前就可以发现问题。
set (VERSION "5.5" CACHE STRING "define project version")
cmake 版本号格式是 <major>[.<minor>[.<patch>[.<tweak>]]]
,最高 4 位的版本号,参见 CMP0048 。一般来说,major version 改变意味着不再保证兼容(可能包含接口删除),minor version 改变可能有新增接口,patch version 改变应该无接口变化,只是 bug 修复等。
设置版本号时应该使用 CACHE,这允许编译时使用 -DVERSION 赋值, 默认值也不要写死 1.0.0, 建议默认值至少让 minor version 应该保持正确(对库的要求应该比对应用的要求更严格)。
此外,编译时 -DVERSION 不能直接使用 DEB_VERSION_UPSTREAM,因为 deb 的版本号是可以类似 1.0.2~2
这样带有 ‘~’ 或者 ‘-’ 符号的,这不符合 cmake 标准,应该处理一下:
PACK_VER = $(shell echo $(DEB_VERSION_UPSTREAM) | awk -F'[+_~-]' '{print $$1}')
参考修改:fix: deb version incompatible cmake standard
project 可以传入 VERSION 参数,不要使用 project(deepin-camera VERSION 1.0.0)
这种写死的错误版本号。
project (foo
VERSION ${VERSION}
DESCRIPTION "foo is a demo library"
HOMEPAGE_URL "https://github.com/ownername/foo"
LANGUAGES CXX C
)
project 配置正确后 cmake 会自动获得 PROJECT_VERSION
,PROJECT_VERSION_MAJOR
,PROJECT_VERSION_MINOR
,PROJECT_VERSION_PATCH
和
PROJECT_VERSION_TWEAK
变量,可以自由使用,比如:
set_target_properties(${TARGET_NAME} PROPERTIES
VERSION "${PROJECT_VERSION}"
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
确保 pkgconfig 文件提供正确的版本号,在 foo.pc.in 文件中,版本号用:
Version: @PROJECT_VERSION@
之后使用 configure_file
就可以替换出正确的版本号了。
在 cmake 中使用 pkg_check_modules 可以非常方便的判断引入包的版本,直接使用 =, <, >, <= 或者 >= 就可以。
pkg_check_modules (GLIB2 glib-2.0) # 默认允许任何版本
pkg_check_modules (GLIB2 glib-2.0>=2.10) # 需要 2.10 及以上版本
pkg_check_modules (FOO glib-2.0>=2.10 gtk+-2.0) # 需要 2.10 及以上版本的 glib-2.0 和任意版本的 gtk+-2.0
另外,当 moduleSpec 只有一个时,FindPkgConfig 模块还会提供 _VERSION 变量。
pkg_check_modules (GLIB2 glib-2.0)
message("${GLIB2_VERSION}") # 这里会输出找到的 glib-2.0 版本值,如 2.58.3
参考: https://cmake.org/cmake/help/latest/module/FindPkgConfig.html
如果项目提供了 PkgNameConfig.cmake 用于 cmake 的 find_package 导入,应该同时提供版本信息(文件命名为 PkgNameConfigVersion.cmake)供调用者检查。
cmake 有一个专门用来生成 Config.cmake 的模块,可以通过 include(CMakePackageConfigHelpers)
引入。该模块提供了 write_basic_package_version_file 函数,可以直接生成 ConfigVersion.cmake 文件。使用示例如下:
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/PkgNameConfigVersion.cmake"
VERSION ${DVERSION}
COMPATIBILITY SameMajorVersion
)
- 文件名,必须以 PkgNameConfigVersion.cmake 格式命名,find_package 才可以自动识别。
- VERSION 传入当前项目的版本号,缺省值是 PROJECT_VERSION。
- COMPATIBILITY 版本检查策略,有 AnyNewerVersion,SameMajorVersion,SameMinorVersion,ExactVersion 四种。AnyNewerVersion 版本检查最为宽松,只需要提供的版本比需求的版本要相同或者更新,如需要 1.2 版本,找到了 1.2,1.5,1.99 或者 2.0 都是合法的,而找到 1.1 版本将视为没有找到这个包。而 SameMajorVersion 还要求主版本号也必须相同,前面的例子中,要求 1.2 找到 2.0 版本会被视为寻找失败 。类似的 SameMinorVersion 要求前 2 位版本号必须一致。ExactVersion 要求版本号完全一致,不考虑向后兼容的软件需要用这个。一般来说,DDE 大部分项目应该使用 SameMinorVersion。
生成的 PkgNameConfigVersion.cmake 文件应该安装到和 PkgNameConfig.cmake 同一个目录中,一般这个目录是 ${CMAKE_INSTALL_LIBDIR}/cmake/PkgName
find_package 支持 version 参数:
find_package(DtkGui 5.6.0 REQUIRED)
find_package(Qt5Gui 5.10 REQUIRED)
message("${Qt5Gui_VERSION}") # 输出找到的版本,如 5.15.3
对于可以多版本共存的包,默认会导入第一个找到的,如果需要找最高版本的,可以设置 set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
。
参考资料:
- https://cmake.org/cmake/help/latest/command/find_package.html
- https://cmake.org/cmake/help/latest/variable/PROJECT_VERSION.html
参考修改: