9
在工程中查找并使用其他程序库的方法
在开发软件的时候我们会用到一些函数库,这些函数库在不同的系统中安装的位置可能不
同,编译的时候需要首先找到这些软件包的头文件以及链接库所在的目录以便生成编译选
项。例如一个需要使用博克利数据库项目,需要头文件
db_cxx.h
和链接库
libdb_cxx.so
,
现在该项目中有一个源代码文件
main.cpp
,放在项目的根目录中。
9.1
程序库说明文件
在项目的根目录中创建目录
cmake/modules/
,在
cmake/modules/
下创建文件
Findlibdb_cxx.cmake
,内容如下:
文件
Findlibdb_cxx.cmake
MESSAGE(STATUS "Using bundled FindLibdb.cmake...")
# 查找头文件路径
FIND_PATH(
LIBDB_CXX_INCLUDE_DIR
db_cxx.h
/usr/include/
/usr/local/include/
)
# 查找库文件路径
FIND_LIBRARY(
LIBDB_CXX_LIBRARIES
NAMES db_cxx
PATHS /usr/lib/ /usr/local/lib/
)
# 检查是否找到了库和头文件
IF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
SET(LIBDB_CXX_FOUND TRUE)
ENDIF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
文件
Findlibdb_cxx.cmake
的命名要符合规范
: FindlibNAME.cmake
,其中
NAME
是函数库的
名称。
Findlibdb_cxx.cmake
的语法与
CMakeLists.txt
相同。这里使用了三个命令:
MESSAGE
,
FIND_PATH
和
FIND_LIBRARY
。
命令
MESSAGE
会将参数的内容输出到终端。
命令
FIND_PATH
指明头文件查找的路径,原型如下:
find_path(<VAR> name1 [path1 path2 ...])
该命令在参数
path*
指示的目录中查找文
件
name1
并将查找到的路径保存在变量
VAR
中。第
3
行到
8
行的意思是
在
/usr/include/
和
/usr/local/include/
中查找文件
db_cxx.h
,并将
db_cxx.h
所在的路径保存
在
LIBDB_CXX_INCLUDE_DIR
中。
命令
FIND_LIBRARY
同
FIND_PATH
类似,用于查找链接库并将结果保存在变量中。第
10
行
到
13
行的意思是在目录
/usr/lib/
和
/usr/local/lib/
中寻找名称为
db_cxx
的链接库,并将结
果保存在
LIBDB_CXX_LIBRARIES
。
9.2
项目的根目录中的
CmakeList.txt
在项目的根目录中创建
CmakeList.txt
:
PROJECT(main)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
SET(CMAKE_SOURCE_DIR ".")
# 设置 CMake 模块路径
SET(CMAKE_MODULE_PATH
${CMAKE_ROOT}/Modules
${CMAKE_SOURCE_DIR}/cmake/modules)
# 获取所有源文件
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
# 添加可执行文件
ADD_EXECUTABLE(main ${DIR_SRCS})
# 查找 libdb_cxx 包
FIND_PACKAGE(libdb_cxx REQUIRED)
# 将 libdb_cxx 的路径标记为高级变量
MARK_AS_ADVANCED(
LIBDB_CXX_INCLUDE_DIR
LIBDB_CXX_LIBRARIES)
# 检查是否找到了 libdb_cxx 的库和头文件
IF(LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
MESSAGE(STATUS "Found libdb libraries")
# 包含 libdb_cxx 的头文件目录
INCLUDE_DIRECTORIES(${LIBDB_CXX_INCLUDE_DIR})
# 打印 libdb_cxx 的库文件路径
MESSAGE(${LIBDB_CXX_LIBRARIES})
# 将 libdb_cxx 的库文件链接到 main 可执行文件
TARGET_LINK_LIBRARIES(main ${LIBDB_CXX_LIBRARIES})
ENDIF(LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
下面是上述代码的详细解释:
-
PROJECT(main)
: 声明了一个CMake项目,项目名称为"main"。
-
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
: 指定CMake的最低版本要求为2.6。
-
SET(CMAKE_SOURCE_DIR .)
: 设置CMAKE_SOURCE_DIR变量,其值为当前目录(.
表示当前目录)。
-
SET(CMAKE_MODULE_PATH ...)
: 设置CMAKE_MODULE_PATH变量,它决定了CMake查找模块的路径。在这里,它包括了${CMAKE_ROOT}/Modules
和${CMAKE_SOURCE_DIR}/cmake/modules
两个路径。
-
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
: 获取当前目录下的所有源文件,并将它们存储在DIR_SRCS变量中。
-
ADD_EXECUTABLE(main ${DIR_SRCS})
: 添加一个名为"main"的可执行文件,该文件由DIR_SRCS变量中的源文件构建。
-
FIND_PACKAGE(libdb_cxx REQUIRED)
: 使用CMake的FIND_PACKAGE命令查找libdb_cxx包。REQUIRED
表示这个包是必需的,如果找不到,CMake将停止并给出错误。
-
MARK_AS_ADVANCED(...)
: 将指定的变量标记为高级变量,这意味着它们不会在简单的cmake ..
命令中自动设置或修改。
-
IF(LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
: 检查是否找到了libdb_cxx的头文件目录和库文件。
MESSAGE(STATUS "Found libdb libraries")
: 如果找到了libdb_cxx,打印一条状态消息表示已找到。INCLUDE_DIRECTORIES(${LIBDB_CXX_INCLUDE_DIR})
: 将libdb_cxx的头文件目录添加到编译器的搜索路径中。MESSAGE(${LIBDB_CXX_LIBRARIES})
: 打印找到的libdb_cxx库文件路径。TARGET_LINK_LIBRARIES(main ${LIBDB_CXX_LIBRARIES})
: 将找到的libdb_cxx库文件链接到"main"可执行文件中。
-
ENDIF(LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
: 结束IF语句。
总的来说,这段代码定义了一个CMake项目,该项目构建一个名为"main"的可执行文件,并尝试找到并链接一个名为libdb_cxx的库。如果找到该库,它还会将其头文件目录添加到编译器的搜索路径中。
9.3 <name>_FOUND
标准的
Find
模块应该定义下面几个变量:
<name>_FOUND
<name>_INCLUDE_DIR or <name>_INCLUDES
<name>_LIBRARY or <name>_LIBRARIES
你可以通过
<name>_FOUND
来判断模块是否被找到
,
如果没有找到
,
按照工程的需要关闭某
些特性、给出提醒或者中止编译。
我们再来看一个的例子
,
通过
<name>_FOUND
来控制工程特性
:
FIND_PACKAGE(JPEG)
IF(JPEG_FOUND)
SET(optionalSources ${optionalSources} jpegview.c)
INCLUDE_DIRECTORIES(${JPEG_INCLUDE_DIR})
SET(optionalLibs ${optionalLibs} ${JPEG_LIBRARIES})
ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT)
ENDIF(JPEG_FOUND)
通过判断系统是否提供了
JPEG
库来决定程序是否支持
JPEG
功能。
10
常用命令
10.1 project
命令
命令语法:
project(<projectname> [languageName1 languageName2 ... ] )
命令简述:用于指定项目的名称
使用范例:
project(Main)
10.2 cmake_minimum_required
命令
命令语法:
cmake_minimum_required(VERSION major[.minor[.patch[.tweak]]]
[FATAL_ERROR])
命令简述:用于指定需要的
CMake
的最低版本
使用范例:
cmake_minimum_required(VERSION 2.8)
10.3 aux_source_directory
命令
命令语法:
aux_source_directory(<dir> <variable>)
命令简述:用于将
dir
目录下的所有源文件的名字保存在变量
variable
中
使用范例:
aux_source_directory(. DIR_SRCS)
10.4 add_executable
命令
命令语法:
add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL]
source1 source2 … sourceN)
命令简述:用于指定从一组源文件
source1 source2 … sourceN
编译出一个可执行文件且命
名为
name
使用范例:
add_executable(Main ${DIR_SRCS})
10.5 add_library
命令
命令语法:
add_library([STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1
source2 … sourceN)
命令简述:用于指定从一组源文件
source1 source2 … sourceN
编译出一个库文件且命名为
name
使用范例:
add_library(Lib ${DIR_SRCS})
10.6 add_dependencies
命令
命令语法:
add_dependencies(target-name depend-target1 depend-target2 …)
命令简述:用于指定某个目标(可执行文件或者库文件)依赖于其他的目标。这里的目标
必须是
add_executable
、
add_library
、
add_custom_target
命令创建的目标
10.7 add_subdirectory
命令
命令语法:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
命令简述:用于添加一个需要进行构建的子目录
使用范例:
add_subdirectory(Lib)
10.8 target_link_libraries
命令
命令语法:
target_link_libraries(<target> [item1 [item2 [...]]] [[debug|optimized|general] ] …)
命令简述:用于指定
target
需要链接
item1 item2 …
。这里
target
必须已经被创建,链接的
item
可以是已经存在的
target
(依赖关系会自动添加)
使用范例:
target_link_libraries(Main Lib)
10.9 set
命令
命令语法:
set(<variable> <value> [[CACHE <type> <docstring> [FORCE]] | PARENT_SCOPE])
命令简述:用于设定变量
variable
的值为
value
。如果指定了
CACHE
变量将被放入
Cache
(缓存)中。
使用范例:
set(ProjectName Main)
10.10
unset
命令
命令语法:
unset(<variable> [CACHE])
命令简述:用于移除变量
variable
。如果指定了
CACHE
变量将被从
Cache
中移除。
使用范例:
unset(VAR CACHE)
10.11
message
命令
命令语法:
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR]
“message to display” …)
命令简述:用于输出信息
使用范例:
message(“Hello World”)
10.12
include
命令
用来载入
CMakeLists.txt
文件,也用于载入预定义的
cmake
模块
.
INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])
OPTIONAL
参数的作用是文件不存在也不会产生错误。
你可以指定载入一个文件,如果定义的是一个模块,那么将在
CMAKE_MODULE_PATH
中
搜索这个模块并载入。
载入的内容将在处理到
INCLUDE
语句时直接执行。
10.13
include_directories
命令
命令语法:
include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)
命令简述:用于设定目录,这些设定的目录将被编译器用来查找
include
文件
使用范例:
include_directories(${PROJECT_SOURCE_DIR}/lib)
10.14
find_file
命令
命令语法:
find_file(<VAR> name1 [path1 path2 ...])
VAR
变量代表找到的文件全路径,包含文件名
10.15
find_path
命令
命令语法:
find_path(<VAR> name1 [path1 path2 ...])
命令简述:用于查找包含文件
name1
的路径,如果找到则将路径保存在
VAR
中(此路径
为一个绝对路径),如果没有找到则结果为
<VAR>-NOTFOUND
。默认的情况下,
VAR
会被
保存在
Cache
中,这时候我们需要清除
VAR
才可以进行下一次查询(使用
unset
命令)。
使用范例:
find_path(LUA_INCLUDE_PATH lua.h ${LUA_INCLUDE_FIND_PATH})
if(NOT LUA_INCLUDE_PATH)
message(SEND_ERROR "Header file lua.h not found")
endif()
10.16
find_library
命令
命令语法:
find_library(<VAR> name1 [path1 path2 ...])
命令简述:用于查找库文件
name1
的路径,如果找到则将路径保存在
VAR
中(此路径为
一个绝对路径),如果没有找到则结果为
<VAR>-NOTFOUND
。一个类似的命
令
link_directories
已经不太建议使用了
示例
:
FIND_LIBRARY(libX X11 /usr/lib)
IF(NOT libX)
MESSAGE(FATAL_ERROR “libX not found”)
ENDIF(NOT libX)
10.17
find_package
命令
命令语法:
find_package(<name> [major.minor] [QUIET] [NO_MODULE]
[[REQUIRED|COMPONENTS] [componets...]])
用来调用预定义在
CMAKE_MODULE_PATH
下的
Find<name>.cmake
模块,你也可以自己定
义
Find<name>
模块,通过
SET(CMAKE_MODULE_PATH dir)
将其放入工程的某个目录中供工
程使用。
10.18
add_definitions
命令
命令语法:
add_definitions(-DFOO -DBAR …)
命令简述:用于添加编译器命令行标志(选项),通常的情况下我们使用其来添加预处理
器定义
使用范例:
add_definitions(-D_UNICODE -DUNICODE)
如果要添加其他的编译器开关,可以通过
CMAKE_C_FLAGS
变量和
CMAKE_CXX_FLAGS
变量
设置。
10.19
execute_process
命令
命令语法:
execute_process(COMMAND <cmd1> [args1...]]
[COMMAND <cmd2> [args2...] [...]]
[WORKING_DIRECTORY <directory>]
[TIMEOUT <seconds>]
[RESULT_VARIABLE <variable>]
[OUTPUT_VARIABLE <variable>]
[ERROR_VARIABLE <variable>]
[INPUT_FILE <file>]
[OUTPUT_FILE <file>]
[ERROR_FILE <file>]
[OUTPUT_QUIET]
[ERROR_QUIET]
[OUTPUT_STRIP_TRAILING_WHITESPACE]
[ERROR_STRIP_TRAILING_WHITESPACE])
命令简述:用于执行一个或者多个外部命令。每一个命令的标准输出通过管道转为下一个
命令的标准输入。
WORKING_DIRECTORY
用于指定外部命令的工作目录,
RESULT_VARIABLE
用于指定一个变量保存外部命令执行的结果,这个结果可能是最后一个执行的外部命令的
退出码或者是一个描述错误条件的字符串,
OUTPUT_VARIABLE
或者
ERROR_VARIABLE
用于
指定一个变量保存标准输出或者标准错误,
OUTPUT_QUIET
或者
ERROR_QUIET
用于忽略标
准输出和标准错误。
使用范例:
execute_process(COMMAND ls)
10.20
file
命令
命令简述:此命令提供了丰富的文件和目录的相关操作(这里仅说一下比较常用的)
使用范例:
#
目录的遍历
# GLOB
用于产生一个文件(目录)路径列表并保存在
variable
中
#
文件路径列表中的每个文件的文件名都能匹配
globbing expressions
(非正则表达式,
但是类似)
#
如果指定了
RELATIVE
路径,那么返回的文件路径列表中的路径为相对于
RELATIVE
的
路径
# file(GLOB variable [RELATIVE path] [globbing expressions]...)
#
获取当前目录下的所有的文件(目录)的路径并保存到
ALL_FILE_PATH
变量中
file(GLOB ALL_FILE_PATH ./*)
#
获取当前目录下的
.h
文件的文件名并保存到
ALL_H_FILE
变量中
#
这里的变量
CMAKE_CURRENT_LIST_DIR
表示正在处理的
CMakeLists.txt
文件的所在的目
录的绝对路径(
2.8.3
以及以后版本才支持)
file(GLOB ALL_H_FILE RELATIVE ${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/*.h)
-
file(GLOB variable [RELATIVE path] [globbing expressions]...)
- variable:这是一个变量名,用于保存找到的文件或目录的路径。
- RELATIVE path:可选参数,指定一个相对路径,找到的文件或目录的路径将是相对于这个相对路径的。
- globbing expressions:这些是用来匹配文件名的模式。例如,
*.txt
会匹配所有 .txt
结尾的文件。
-
使用范例
| cmake`file(GLOB ALL_FILE_PATH ./*)` |
这会找到当前目录下的所有文件和目录,并将它们的路径保存到 ALL_FILE_PATH
变量中。
| cmake`file(GLOB ALL_H_FILE RELATIVE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/*.h)` |
这会找到当前目录下的所有 .h
文件,并将它们的相对路径保存到 ALL_H_FILE
变量中。这里使用了 RELATIVE
关键字和 ${CMAKE_CURRENT_LIST_DIR}
变量来指定相对路径。
简单来说,file(GLOB ...)
是一个非常有用的命令,它可以帮助您自动地收集文件和目录的路径,特别是当您不知道文件的确切名称或只知道其模式时。