CMake库打包以及支持find_package(一)

简介: CMake库打包以及支持find_package(一)

本文对CMake中库的打包,安装,导出以及支持find_package,使其能够很简单的应用到其他的项目中进行详细的总结。


CMake打包库


假设我们的库的结构如下:


- include/
  - my_library/
    - header-a.hpp
  - header-b.hpp
  - config.hpp
  - ...
- src/
  - source-a.cpp
  - source-b.cpp
  - config.hpp.in
  - ...
  - CMakeLists.txt
- example/
  - example-a.cpp
  - ...
  - CMakeLists.txt
- tool/
  - tool.cpp
  - CMakeLists.txt
- test/
  - test.cpp
  - CMakeLists.txt
- CMakeLists.txt
- ...


在这个库中包含了不同的头文件和源文件,还包含一些例子,工具和单元测试模块。对于库、示例和单元测试,每个模块分别拥有自己的CMakeLists.txt,在其中定义了编译的目标并且在子目录中包含了相关的代码。而项目的根目录的CMakeLists.txt则定义了配置选项,并将这些子模块的加入编译中去。

库的相关配置在config.hpp.in中被定义,然后这个文件会被CMake预处理为config_impl.hpp,然后被config.hpp包含到项目中去(#include "config_impl.hpp")。

这种方法非常重要,能够让我们对不同的CMake配置文件进行分离,比如一些不相干的配置的宏等等


项目根目录的CMakeLists.txt文件:


cmake_minimum_required(VERSION 3.0)
project(MY_LIBRARY)
# define library version (update: apparently you can also do it in project()!)
set(MY_LIBRARY_VERSION_MAJOR 1 CACHE STRING "major version" FORCE)
set(MY_LIBRARY_VERSION_MINOR 0 CACHE STRING "minor version" FORCE)
set(MY_LIBRARY_VERSION ${MY_LIBRARY_VERSION_MAJOR}.${MY_LIBRARY_VERSION_MINOR} CACHE STRING "version" FORCE)
# some options
option(MY_LIBRARY_USE_FANCY_NEW_CLASS "whether or not to use fancy new class" ON)
option(MY_LIBRARY_DEBUG_MODE "whether or not debug mode is activated" OFF)
# add subdiretories
add_subdirectory(src)
add_subdirectory(example)
add_subdirectory(tool)
add_subdirectory(test)


这个文件中有一些option操作,这些配置选项能够讲相关配置写入到config.hpp.in中,我们需要在config.hpp.in定义这些选项,类似这种形式:#cmakedefine01

注意,库的版本号我们使用了force,这个就阻止了用户在CMakeCache.txt中更改这个版本号

库模块的src/CMakeLists.txt文件:


# set headers
set(header_path "${MY_LIBRARY_SOURCE_DIR}/include/my_library")
set(header ${header_path}/header-a.hpp
       ${header_path}/header-b.hpp
       ${header_path}/config.hpp
       ...)
# set source files
set(src source-a.cpp
    source-b.cpp
    ...)
# configure config.hpp.in
configure_file("config.hpp.in" "${CMAKE_CURRENT_BINARY_DIR}/config_impl.hpp")
# define library target
add_library(my_library ${header} ${src})
target_include_directories(my_library PUBLIC ${MY_LIBRARY_SOURCE_DIR}/include


首先,我们定义了头文件和源文件的列表,方便后续使用。

注意头文件的路径变量header_path,这个变量在不同的CMake子文件中是不同的,而源文件因为在同一目录中,则可以直接定义。

这个CMake文件同样能够生成config_impl.hpp,并保存在当前定义的库生成的二进制目录中(${CMAKE_CURRENT_BINARY_DIR}),然后被包含在config.hpp中,最终在库被使用能够被找到。

target_include_directories指定了这个库要用到的头文件,PUBLIC制定的包含目录包括了include/的子目录和当前CMake的二进制目录(为了包含config_impl.hpp)。

其余模块的的CMakeLists.txt类似

到这一步,其余的用户就能够通过add_subdirtectory()来添加库的目录,然后调用target_link_libraries(my_target PUBLIC my_library)来链接库,并且需要设置include_directories来包含相关的头文件,从而能够调用我们的库。


CMake安装库


我们需要安装的东西包括:头文件,可执行的工具以及已经编译好的库。这些都能够直接使用install()命令来直接安装。当我们使用cmake install(make install)这类命令时,会拷贝这些文件到${CMAKE_INSTALL_PREFIX}中(Linux下默认是/usr/local)。

首先,我们在项目的CMakeLists.txt定义相关的位置和变量:

set(tool_dest "bin")

set(include_dest "include/my_library-"${MY_LIBRARY_VERSION}")

set(main_lib_dest "lib/my_library-"${MY_LIBRARY_VERSION}")

然后我们配置install()命令:


# in tool/CMakeLists.txt  
install(TARGETS my_library_tool DESTINATION "${tool_dest}")  
# in src/CMakeLists.txt  
install(TARGETS my_library DESTINATION "${main_lib_dest}")  
install(FILES ${header} DESTINATION "${include_dest}")  
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/config_impl.hpp DESTINATION "${include_dest}")


这会将可执行工具安装到${CMAKE_INSTALL_PREFIX}/bin上,头文件安装到${CMAKE_INSTALL_PREFIX}/include/my_library-1.0,库安装到${CMAKE_INSTALL_PREFIX}/lib/my_library-1.0。现在就已经满足了我们的一个目标了:不同版本的库不会产生冲突,因为版本号成为了安装路径的一部分。

对于工具tool,我们假设其能够具有很好的兼容性,并且将其直接放到bin/文件夹中,这样其能够直接在终端运行,如果你有需求,你应该对这部分做一些自定义的调整。

但是目前仍然没有解决一个问题:每个编译出来的库可以拥有不同的配置。因为现在只有一个配置文件。我们当然也能通过配置不同的识别名称来区别不同的配置,就像利用不同的版本号一样,但是这对于大多数文件是不需要,因此我们不必采用这种方案。

再次忽略掉tool,那么就只剩下两个文件需要依赖不同的配置:编译得到的库和生成的config_impl.hpp文件。因为其中包含了对于库的一些宏的操作,因此我们需要根据配置的不同,将这两个文件放在不同的位置。

但是我们怎么去区分呢?

可以使用编译类型${CMAKE_BUILD_TYPE}这个变量。通过指示DebugReleaseMinSizeRel以及RelWithDebInfo,来指示不同的配置选项。

我们也可以定义自己的编译类型以及相对应的一些编译选项操作。

现在我们可以在项目的根CmakeLists.txt中添加一个新的变量了lib_dest

set(lib_dest ${main_lib_dest}/${CMAKE_BUILD_TYPE}")

并且需要更改config_impl.hpp和库目标的路径,将其安装到lib_dest中,这样对于不同的编译类型(也就是不同的配置),我们就会得到不同的config_impl.hpp和库文件。

现在,经过这些配置,我们已经能够区别不同版本和不同配置的库,将其安装到不同的目标路径中,比如${CMAKE_INSTALL_PREFIX}/lib/my_library-1.0/Debug

目录
相关文章
|
开发工具 git
Git添加子模块(submodule)
Git添加子模块(submodule)
897 0
|
7月前
|
存储 安全 Linux
【实战指南】7个设置/获取接口了解Linux时间管理
本文系统介绍了Linux时间管理中的7个关键设置/获取接口,涵盖时间获取(如`time`、`gettimeofday`、`clock_gettime`)、时间设置(如`stime`、`settimeofday`、`clock_settime`)以及时间转换和格式化等内容。文章详细解析了绝对时间和相对时间的概念,包括GMT、UTC及本地时间的区别,并通过实例测试展示了各接口的使用方法与特性。此外,还探讨了时区设置对时间计算的影响,强调在实际开发中推荐使用UTC作为基准时间以避免时区变化带来的问题。总结部分结合项目经验,提醒开发者注意时间服务的重要性及潜在风险,例如时间跳跃可能引发的应用故障。
345 121
【实战指南】7个设置/获取接口了解Linux时间管理
|
11月前
|
机器学习/深度学习 传感器 自动驾驶
深度学习在自动驾驶中的应用与挑战####
本文探讨了深度学习技术在自动驾驶领域的应用现状、面临的主要挑战及未来发展趋势。通过分析卷积神经网络(CNN)和循环神经网络(RNN)等关键算法在环境感知、决策规划中的作用,结合特斯拉Autopilot和Waymo的实际案例,揭示了深度学习如何推动自动驾驶技术向更高层次发展。文章还讨论了数据质量、模型泛化能力、安全性及伦理道德等问题,为行业研究者和开发者提供了宝贵的参考。 ####
|
Cloud Native Java 对象存储
面向未来的架构设计:Spring Cloud和Netflix OSS在云原生环境下的发展趋势
面向未来的架构设计:Spring Cloud和Netflix OSS在云原生环境下的发展趋势
196 1
|
数据可视化 Java 程序员
通过文字图像——代码图形注释自动生成
大家在学(CTRL)习(C)别人代码的时候,看到别人的代码程序,在日志中有很多很酷的代码注释,或者是有一些图形化注释方便理解。之前本人以为都是一个个手敲出来的。然后在网上一番搜索,找到了很多神奇的好网站,以用于图形注释生成。 代码图形注释自动生成技术是一种将代码逻辑和结构可视化的创新工具。它通过解析编程代码,并将代码的功能、结构和逻辑关系转换成直观的图形注释,从而使得程序员能够更加轻松地理解和分析代码。这种技术特别适合于复杂代码的解读,帮助开发人员快速定位代码中的关键部分和潜在问题。此外,对于团队合作和代码教育来说,图形注释可以作为沟通和学习的桥梁,让代码的理解变得更加直观和高效。总的来说,
325 0
|
算法 编译器 C语言
【CMake install 命令】精通CMake安装:灵活、高效的构建和部署
【CMake install 命令】精通CMake安装:灵活、高效的构建和部署
3160 0
|
存储 Linux 计算机视觉
CMake库打包以及支持find_package(二)
CMake库打包以及支持find_package(二)
454 0
|
存储 缓存 IDE
CMake之编写属于自己的Findxxx.cmake文件:定义一个定制化的CMakeLists.txt文件
CMake之编写属于自己的Findxxx.cmake文件:定义一个定制化的CMakeLists.txt文件
297 1
|
JavaScript 前端开发 Java
JDK8中的新特性(Lambda、函数式接口、方法引用、Stream)(一)
JDK8中的新特性(Lambda、函数式接口、方法引用、Stream)(一)