C/CPP工程实践

CMake构建一个静态链接库的实例

2018-05-05  本文已影响934人  EVANMORE

1. 首先是当前这个例子中要用到的一些命令

1.1. add_library

添加一个lib到工程中,指定这个lib的源文件
函数定义

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            source1 [source2 ...])

<name>是目标库的名字,这个名字在工程内全局唯一,最终编译出来的目标文件名字取决于目标平台(比如说lib<name>.a<name>.lib

STATIC, SHARED, 或MODULE用来指定要创建的lib的类型。

EXCLUDE_FROM_ALL 会在目标文件上设置相应的属性(执行默认make的时候,这个目标文件会被排除在外,不被编译)


add_library(<name> <SHARED|STATIC|MODULE|UNKNOWN> IMPORTED
            [GLOBAL])

一个Imported Lib目标代表一个工程外部的库文件,当前这个工程内灭有命令来编译这个库,并且这个库的IMPORT属性是true。
没有设置GLOBAL的时候,这个目标名称的作用域只在创建它的目录以及子目录;设置了之后,全局可见。

NOTE: Imported Target通常代表一个工程的一个依赖,一般只能作为诸如target_link_libraries()这样命令的右值,不能修改


add_library(<name> OBJECT <src>...)

创建一个特殊的“object library” 目标。这种库只编译源文件生成目标文件,但是不把这些目标文件打包进一个lib。当其他的库或者目标文件要使用这些目标文件的时候,会以这样的形式来添加,objlib是这个库的名字

add_library(... $<TARGET_OBJECTS:objlib> ...)
add_executable(... $<TARGET_OBJECTS:objlib> ...)

add_library(<name> INTERFACE [IMPORTED [GLOBAL]])

创建一个Interface库,一个INTERFACE库不会直接创建编译目标文件,即使这个库可以设置一些属性并且可以被installed,exported和imported。通常来说使用set_property()target_link_libraries(INTERFACE)target_include_directories(INTERFACE)target_compile_options(INTERFACE)target_compile_definitions(INTERFACE)这些函数来设置INTERFACE_*属性,然后这个库可以像其他库一样作为target_link_libraries()命令的参数。

这里还可以通过设置IMPORTED标志来生成一个INTERFACE imported Target,表示这个库文件在工程外。

1.2. target_include_directories

target_include_directories(<target> [SYSTEM] [BEFORE]
  <INTERFACE|PUBLIC|PRIVATE> [items1...]
  [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

指定编译目标文件的时候需要包含的路径和内容, <target> 必须是一个已经通过诸如 add_executable()或者add_library()函数创建了的目标,并且不是一个标注成IMPORTED目标。
INTERFACE, PUBLICPRIVATE这些关键字用来指定后面这些参数的作用范围。PUBLICINTERFACE的条目会产生目标对象的INTERFACE_INCLUDE_DIRECTORIES属性(也就是说添加了的公共默认搜索路径),后面的参数定义了包含路径。

指定的包含路径可以是绝对路径也可以是相对路径,重复调用相同的<target>会把这些条目按次序添加到对象上。如果指定了SYSTEM属性,则意味着这个路径是一个系统路径

target_include_directories 的参数可以使用“生成表达式”,语法是$<...>

包含路径的使用要求对于build树和instal树通常是不太一样的。BUILD_INTERFACEINSTALL_INTERFACE 生成表达式可以用来描述不同的使用要求。

target_include_directories(mylib PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>
  $<INSTALL_INTERFACE:include/mylib>  # <prefix>/include/mylib
)

2. 实例

  1. 首先看一下当前工程的目录
.
├── CMakeLists.txt
├── include
│   └── Logger.hpp
└── src
    └── Logger.cpp

很简单的一个工程,提供了spdlog的一个简单封装,其他文件要使用日志功能的时候只要包含Logger.hpp头文件就可以了。

  1. 下面是CMakeLists.txt文件
set(module_name logger)
project(${module_name} CXX)

message(STATUS "Configuring ${module_name}")

set(sources 
    src/Logger.cpp
)

set(headers
    include/Logger.hpp
)

#依赖开源的interface库spdlog,添加一个interface类型的库目标
add_library(spdlog INTERFACE)
#给这个interface库添加属性,也就是这个spdlog的头文件的相对路径
target_include_directories(
    spdlog
    INTERFACE
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../externals/spdlog/include>"
)

#spdlog依赖的线程库
find_package(Threads)

#添加要编译的logger库
add_library(${module_name} ${sources} ${headers})  
target_link_libraries(${module_name} spdlog ${CMAKE_THREAD_LIBS_INIT})

#和之前一样,指定需要包含的路径(这个时候spdlog库里面已经包含了spdlog的头文件搜索路径了,所以当前这个logger库就不用额外指定了)
target_include_directories(
    ${module_name} 
    PUBLIC
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
)
  1. 使用logger库的主工程
    这个cmake只要把logger库link进来就可以了,logger库内的头文件搜索路径都被一并继承了。
set(module_name sctp_proxy)
project(&{module_name} CXX)

add_subdirectory(libs)

add_executable(${module_name} main.cpp)
target_link_libraries(${module_name} logger)
上一篇下一篇

猜你喜欢

热点阅读