没有deployment target options_Target是个好东西[1]:从编译一个动态库说起

6db84dadbaa4b8ab3da28dc34e66a332.png

Target是个好东西[1]:从编译一个动态库说起

1 旧世界规则

include_directories(
  ${CMAKE_CURRENT_SOURCE_DIR}/include
  )

add_library(my_library_shared SHARED 
  ${CMAKE_CURRENT_SOURCE_DIR}/src/my_library.cpp
  )

add_executable(main main.cpp)

target_link_libraries(main my_library_shared)

我们经常看到或用过上面的写法,对于实验性质的代码这并无不可,但这并非最佳实践。因为,形如include_directories的命令实则将包含目录添加到了全局空间,这使它们会对所有存在于这个工程的构建目标可见。

通俗讲,工程内有a、b两个构建目标,原本header_a仅用于lib_a,但是现在完全不相干的lib_b也可以发现并使用header_a,一旦工程庞大起来,很容易引起头文件污染或冲突。笔者就因这种用法,在同时使用PCL及OpenCV时发生过Flann库头文件冲突的问题。

另一方面,我们有时候也需要细粒度地控制头文件或库在被后续目标使用时的可见性。通俗地说也就是header_a1header_a2都用于lib_a,其中header_a1是库的对外接口,而header_a2则是内部使用的头文件,当lib_b使用lib_a时,我们仅希望接口部分可见而其他部分不可见。后续介绍的用法将同时解决这样的问题,这和OOD中所说的“最少知识原则”是相通的。

最少知识原则:又称迪米特法则,对于OOD来说,又被解释为下面几种方式:一个软件实体应当尽可能少的与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。(百度百科)

注意:在Modern CMake中所有的构建目标都可认为是Target,既包含最终会实际编译的动静态库或可执行文件,也包含不会实际编译的INTERFACE类型的目标(如add_library(lib_a INTERFACE IMPORTED)),私以为基于面向对象的思想理解Target是对Modern CMake核心内容的诠释。

2 推荐用法

工程结构:

├── CMakeLists.txt
├── include
│   └── my_library.h
└── src
    └── my_library.cpp

CMakeLists:

add_library(my_library_shared SHARED "")

target_sources(my_library_shared
  PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/src/my_library.cpp
  )

target_include_directories(my_library_shared
  PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/include
  )

target_link_libraries(my_library_shared
  PUBLIC
    ${SOME_OTHER_LIBRARIES}
  )

3 作用

  • add_library(my_library_shared SHARED ""):声明了一个名为my_library_shared的动态库Target,这里我们没有指定源文件而是用""代替,可以理解为一个空对象
  • target_sources为Target指定了源文件
  • target_include_directories为Target指定了包含目录
  • target_link_libraries为Target指定了需要链接的库

3 要点

  • 在声明一个构建目标之后我们用target_开头的命令来添加构建该目标所需的资源和属性,诸如:源文件、包含目录、链接库、属性、编译标志等
  • 这些命令中使用了PUBLICPRIVATE这样的标记,除此之外还有INTERFACE,它们细粒度的规定了所添加的资源或属性的可见性范围,其中:
  • PRIVATE只用于该Target的构建,不用于使用该Target的其他对象
  • INTERFACE只用于使用该Target的其他对象
  • PUBLIC既用于该Target的构建,也用于使用该Target的其他对象
  • 举个例子:header_a1header_a2都用于lib_a,其中header_a1是库的对外接口设为PUBLIC,而header_a2则是内部使用的头文件设为PRIVATE,当lib_b使用lib_a时,header_a1作为接口应该被lib_b可见,而header_a2lib_a的内部头文件不应可见

4 更多

除源文件、包含目录、链接库以外,我们还需要为Target设置属性和编译标志等,举例如下:

# 为Target设置属性
set_target_properties(my_library_shared
  PROPERTIES
    # -fPIC 生成位置无关代码
    POSITION_INDEPENDENT_CODE 1
    # 设置库版本号为工程主版本号
    SOVERSION ${PROJECT_VERSION_MAJOR}
    # 重设动态库输出的名称,my_library_shared → my_library
    OUTPUT_NAME "my_library"
    # 为Debug模式下生成的库添加后缀
    DEBUG_POSTFIX "_d"
    # 为该库指定PUBLIC可见的头文件,一般用于指定包含外部接口的头文件
    PUBLIC_HEADER
      ${CMAKE_CURRENT_SOURCE_DIR}/include/my_library.h
  )

# 为Target设置编译标志,编译标志的可见性同上所属
target_compile_options(my_library_shared
  PRIVATE
    -std=c++11
  )

参考文献

  • CMake Cookbook

版权声明见README

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值