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的类型。
-
STATIC
, 代表静态链接库,编译的时候link到工程中; -
SHARED
,代表动态链接库,运行时候加载; -
MODULE
,是一些插件,运行时候使用dlopen-like的功能进行动态加载;
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
, PUBLIC
和 PRIVATE
这些关键字用来指定后面这些参数的作用范围。PUBLIC
和INTERFACE
的条目会产生目标对象的INTERFACE_INCLUDE_DIRECTORIES
属性(也就是说添加了的公共默认搜索路径),后面的参数定义了包含路径。
指定的包含路径可以是绝对路径也可以是相对路径,重复调用相同的<target>会把这些条目按次序添加到对象上。如果指定了SYSTEM
属性,则意味着这个路径是一个系统路径
target_include_directories
的参数可以使用“生成表达式”,语法是$<...>
。
包含路径的使用要求对于build树和instal树通常是不太一样的。BUILD_INTERFACE
和 INSTALL_INTERFACE
生成表达式可以用来描述不同的使用要求。
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>
$<INSTALL_INTERFACE:include/mylib> # <prefix>/include/mylib
)
2. 实例
- 首先看一下当前工程的目录
.
├── CMakeLists.txt
├── include
│ └── Logger.hpp
└── src
└── Logger.cpp
很简单的一个工程,提供了spdlog的一个简单封装,其他文件要使用日志功能的时候只要包含Logger.hpp头文件就可以了。
- 下面是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>"
)
- 使用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)