CMake官方教程:https://cmake.org/cmake/help/v3.13/index.html
CMake官方下载地址:https://cmake.org/download/
CMake 简介
CMake 是一个跨平台的自动化构建系统,它使用一个名为CMakeLists.txt
的文件来描述构建过程,可以产生标准的构建文件,如 Unix 的 Makefile 或Windows Visual C++ 的 projects/workspaces 。文件 CMakeLists.txt 需要手工编写,也可以通过编写脚本进行半自动的生成。CMake 提供了比 autoconfig 更简洁的语法。
在 linux 平台下使用 CMake 生成 Makefile 并用之进行编译的流程如下:
1、编写文件CMakeLists.txt
2、执行命令“cmake PATH”或者“ccmake PATH”生成Makefile (PATH是CMakeLists.txt所在的目录)
3、使用make命令进行编译
cmake的基础语法
cmake编写的过程实际上就是编程的过程,在这里需要编写的是 CMakeLists.txt(每个目录一个),使用的是”cmake语言和语法
”。
基本语法规则
1、变量
使用${}
方式取值,但是在IF控制语句
中是直接使用变量名;
2、指令(参数 1 参数 2...)参数
使用括弧括起,参数之间使用空格或分号分开)如下为一些指令的使用语法;
3、指令
是大小写无关
的,参数和变量
是大小写相关
的。但,推荐你全部使用大写指令
。
基本命令的使用语法
CMAKE_MINIMUM_REQUIRED(VERSION <min>[...<max>] [FATAL_ERROR])
设置项目所需的cmake最低版本。如果运行的cmake版本低于所需的<min>
版本,它将停止处理项目并报告错误。如果指定了可选的<max>
版本,则该版本必须至少是<min>
版本。如果运行的cmake版本早于<min>
版本,则额外的…
点将被视为版本组件分隔符
,导致忽略…<max>
部分。
CMAKE_MINIMUM_REQUIRED()
指令应该调用在CMakeLists.txt
文件的最上面,如果存在指令PROJECT()
,也要调用在此指令之前。在一个cmake项目中,每一个文件夹都应该有一个CMakeLists.txt
文件,其中,在项目根目录中的CMakeLists.txt
文件可被称为主CMakeLists.txt
文件,而在此文件中,如果不加入此行指令,以指定cmake的最低版本需求,则会收到警告信息。
PROJECT(projectname [CXX] [C] [Java])
可以用这个指令定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的,默认情况表示支持所有语言。这个指令隐式的定义了两个 cmake 变量:<projectname>_BINARY_DIR
以及<projectname>_SOURCE_DIR
,如果采用的是内部编译,两个变量指的将是工程所在的路径,如果是外部编译,两者所指代的内容会有所不同。同时 cmake 系统也预定义了PROJECT_BINARY_DIR
和 PROJECT_SOURCE_DIR
变量,它们的值分别跟<projectname>_BINARY_DIR
与 <projectname>_SOURCE_DIR
一致。为了统一起见,建议以后直接使用PROJECT_BINARY_DIR
,PROJECT_SOURCE_DIR
,即使修改了工程名称,也不会影响这两个变量。如果使用了<projectname>_SOURCE_DIR
,修改工程名称后,需要同时修改这些变量。
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
SET 指令可以用来显式地构建一个自定义变量,其中VAR
为变量名,VALUE
为变量的值。此外,它还可以用于为已有变量赋予新值。
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"...)
这个指令用于向终端输出用户定义的信息,包含了三种类型:SEND_ERROR
,产生错误,生成过程被跳过。SATUS
,输出前缀为—的信息。FATAL_ERROR
,立即终止所有 cmake 过程。
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除,比如,工程的 example,可能就需要工程构建完成后,再进入 example 目录单独进行构建(当然,你也可以通过定义依赖来解决此类问题)。
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
此指令的作用为构建库,库的类型有三种:SHARED(共享库【动态库】),STATIC(静态库),MODULE(模块):在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待。EXCLUDE_FROM_ALL 参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手工构建。假如创建共享库libhello.so
,libname
项只需写hello
,而不需要写全libhello.so
,cmake 系统会自动为你生成libhello.X
。(共享库的后缀为.so
,静态库的后缀为.a
)
AUX_SOURCE_DIRECTORY(dir VARIABLE)
作用是发现一个目录下所有的源代码文件,并将文件路径列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。因为目前 cmake 还不能自动发现新添加的源文件。比如:AUX_SOURCE_DIRECTORY(. SRC_LIST)
,ADD_EXECUTABLE(main ${SRC_LIST})
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
添加头文件路径
。这条指令可以用来向工程添加多个特定的头文件路径
,路径之间用空格
分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面,你可以通过两种方式来进行控制搜索路径添加的方式:
1、CMAKE_INCLUDE_DIRECTORIES_BEFORE
,通过 SET 这个 cmake 变量为 on
,可以将添加的头文件搜索路径放在已有路径的前面。
2、通过 AFTER 或者 BEFORE 参数,也可以控制是追加还是置前。
LINK_DIRECTORIES 指令
动态链接库或静态链接库的搜索路径,相当于指定gcc的-L参数
TARGET_LINK_LIBRARIES(target library1 <debug | optimized> library2 ...)
FIND_PACKAGE(<PackageName> [version] [EXACT] [QUIET] [MODULE][REQUIRED] [[COMPONENTS] [components...]][OPTIONAL_COMPONENTS components...][NO_POLICY_SCOPE])
用于在系统环境变量指定的目录中寻找Config.cmake
和Config-version.cmake
文件。
设置目标 target 需要链接的共享库和静态库。
FIND_FILE(<VAR> name1 path1 path2 ...)
VAR 变量代表找到的文件全路径,包含文件名。
FIND_LIBRARY(<VAR> name1 path1 path2 ...)
VAR 变量表示找到的库全路径,包含库文件名
FIND_PATH(<VAR> name1 path1 path2 ...)
如果cmake
能在路径path1 path2 ...
中寻找到文件name1
,则会把此文件的路径存储在变量VAR
中,即VAR
变量代表包含name1
这个文件的路径。
FIND_PROGRAM(<VAR> name1 path1 path2 ...)
VAR 变量代表包含这个程序的全路径。
INSTALL 指令
INSTALL 指令包含了各种安装类型,我们需要一个个分开解释:
目标文件的安装:
INSTALL( TARGETS targets...
[
[ARCHIVE|LIBRARY|RUNTIME] [DESTINATION <dir>] [PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...] ] [COMPONENT <component>] [OPTIONAL]
] [...] )
参数中的 TARGETS 后面跟的就是我们通过 ADD_EXECUTABLE 或者 ADD_LIBRARY 定义的目标文件,可能是可执行二进制、动态库、静态库。
目标类型也就相对应的有三种,ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME特指可执行目标二进制。DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX 其实就无效了。如果你希望使用CMAKE_INSTALL_PREFIX 来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>
普通文件的安装:
INSTALL( FILES files... DESTINATION <dir>
[PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>] [RENAME <name>] [OPTIONAL] )
可用于安装一般文件,并可以指定访问权限,文件名是此指令所在路径下的相对路径。如果默认不定义权限 PERMISSIONS,安装后的权限为:OWNER_WRITE, OWNER_READ, GROUP_READ,和 WORLD_READ,即 644 权限。
非目标文件的可执行程序安装(比如脚本之类):
INSTALL( PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>] [RENAME <name>] [OPTIONAL] )
跟上面的 FILES 指令使用方法一样,唯一的不同是安装后权限为:OWNER_EXECUTE, GROUP_EXECUTE, 和 WORLD_EXECUTE,即 755 权限
目录的安装:
INSTALL( DIRECTORY dirs... DESTINATION <dir>
[FILE_PERMISSIONS permissions...] [DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS] [CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[
[PATTERN <pattern> | REGEX <regex>] [EXCLUDE] [PERMISSIONS permissions...]
] [...] )
环境变量
cmake调用系统环境变量
的方式:$ENV{变量名}
,如:MESSAGE(STATUS “HOME dir: $ENV{HOME}”)
。
设置环境变量的方式:SET(ENV{变量名} 值)
cmake定义与调用自定义变量
的方式:定义:SET(变量名 值)
,调用:${变量名}
CMake中包含了大量的内置变量,这些变量的使用方法与自定义变量的是一样的。
工程名称
PROJECT_NAME
返回通过PROJECT 指令
定义的项目名称。
工程路径
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
<projectname>_SOURCE_DIR
这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录
。也就是在 in source 编译时,它跟 CMAKE_BINARY_DIR
等变量一致。PROJECT_SOURCE_DIR
跟其它指令稍有区别,现在,你可以理解为他们是一致的。
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR
这三个变量指代的内容是一致的,如果是 in source 编译,指的就是工程顶层目录
,如果是out-of-source编译
,指的是工程编译发生的目录。PROJECT_BINARY_DIR
跟其他指令稍有区别,现在,你可以理解为他们是一致的。
CMAKE_CURRENT_SOURCE_DIR
指的是当前处理的 CMakeLists.txt文件
所在的路径。
CMAKE_CURRRENT_BINARY_DIR
如果是in-source编译
,它跟CMAKE_CURRENT_SOURCE_DIR
一致,如果是out-ofsource编译
,它指的将是 target 编译目录。
CMAKE_CURRENT_LIST_FILE
输出调用这个变量的 CMakeLists.txt 的完整路径。
编译器
CMAKE_C_COMPILER
指定C编译器,通常CMake运行时能够自动检测C语言编译器。进行嵌入式系统开发时,通常需要设置此变量,指定交叉编译器。
CMAKE_CXX_COMPILER
指定C++编译器
编译器参数
CMAKE_BUILD_TYPE
指定构建的类型:Debug 和 Release。如,CMakeList.txt文件:SET(CMAKE_BUILD_TYPE Debug)
;命令行参数:cmake -D CMAKE_BUILD_TYPE=Release
。
CMAKE_C_FLAGS
CMAKE_CXX_FLAGS
分别指定编译C或C++文件时的编译选项,也可以通过指令ADD_DEFINITIONS()
添加编译选项。
在cmake脚本中,设置编译选项
可以通过add_compile_options命令
,也可以通过set命令
修改CMAKE_CXX_FLAGS
或CMAKE_C_FLAGS
。使用这两种方式在有的情况下效果是一样的,但请注意它们还是有区别的:
add_compile_options命令
添加的编译选项是针对所有编译器的(包括c和c++编译器),而set命令
设置CMAKE_C_FLAGS
或CMAKE_CXX_FLAGS
变量则是分别只针对c和c++编译器的。
例如下面的代码
#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
if(CMAKE_COMPILER_IS_GNUCXX)
add_compile_options(-std=c++11)
message(STATUS "optional:-std=c++11")
endif(CMAKE_COMPILER_IS_GNUCXX)
使用add_compile_options
添加-std=c++11
选项,是想在编译c++代码时加上c++11支持选项。但是因为add_compile_options
是针对所有类型编译器的,所以在编译c代码时,就会产生如下warning
J:\workspace\facecl.gcc>make b64
[ 50%] Building C object libb64/CMakeFiles/b64.dir/libb64-1.2.1/src/cdecode.c.obj
cc1.exe: warning: command line option ‘-std=c++11’ is valid for C++/ObjC++ but not for C
[100%] Building C object libb64/CMakeFiles/b64.dir/libb64-1.2.1/src/cencode.c.obj
cc1.exe: warning: command line option ‘-std=c++11’ is valid for C++/ObjC++ but not for C
Linking C static library libb64.a
[100%] Built target b64
虽然并不影响编译,但看着的确是不爽啊,要消除这个warning,就不能使用add_compile_options,而是只针对c++编译器添加这个option。
所以如下修改代码,则警告消除。
#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
message(STATUS "optional:-std=c++11")
endif(CMAKE_COMPILER_IS_GNUCXX)
举一反三,我们就可以想到,add_definitions这个命令也是同样针对所有编译器,一样注意这个区别。
头文件、库文件和cmake模块路径
CMAKE_INCLUDE_PATH
头文件路径。配合FIND_FILE()
以及FIND_PATH()
使用。如果头文件没有存放在如/usr/include, /usr/local/include
等的常规路径中,则可以通过这个变量进行弥补。但如果不使用 FIND_FILE
和FIND_PATH
,则变量CMAKE_INCLUDE_PATH
是没有任何作用的。
CMAKE_LIBRARY_PATH
库文件路径。配合FIND_LIBRARY()
使用,否则将没有任何作用。
CMAKE_MODULE_PATH
cmake模块路径。cmake为上百个软件包
提供了查找器(finder):FindXXXX.cmake
。当使用非cmake自带的finder
时,需要指定finder
的路径,这就是CMAKE_MODULE_PATH
,配合FIND_PACKAGE()
使用。
程序安装
CMAKE_INSTALL_PREFIX
控制make install
,把make
产生的文件安装到指定的地方。此变量默认的程序安装路径前缀为/usr/local
或%PROGRAMFILES%
。
可执行文件与库文件存放的路径
EXECUTABLE_OUTPUT_PATH
LIBRARY_OUTPUT_PATH
分别用来重新定义最终结果(可执行文件和库文件)的存放目录。
库编译方式
BUILD_SHARED_LIBS
此变量用于控制默认的库编译方式,如果不进行设置,使用ADD_LIBRARY
并没有指定库类型的情况下,默认编译生成的库都是静态库
。如果SET(BUILD_SHARED_LIBS ON)
后,将默认生成动态库
。
文件参数
CMAKE_CURRENT_LIST_LINE
输出这个变量所在的行。
CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS
用来控制 IF ELSE 语句的书写方式。
系统信息
CMAKE_MAJOR_VERSION
:CMAKE 主版本号,比如 2.4.6 中的 2
CMAKE_MINOR_VERSION
:CMAKE 次版本号,比如 2.4.6 中的 4
CMAKE_PATCH_VERSION
:CMAKE 补丁等级,比如 2.4.6 中的 6
CMAKE_SYSTEM
:系统名称,比如 Linux-2.6.22
CMAKE_SYSTEM_NAME
:不包含版本的系统名,比如 Linux
CMAKE_SYSTEM_VERSION
:系统版本,比如 2.6.22
CMAKE_SYSTEM_PROCESSOR
:处理器名称,比如 i686.
UNIX
:在所有的类 UNIX 平台为 TRUE,包括 OS X 和 cygwin
WIN32
:在所有的 win32 平台为 TRUE,包括 cygwin
一些指令的解析:
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/home/OpenCV ..
此指令分四部分:一是cmake
,二是-D CMAKE_BUILD_TYPE=RELEASE
,三是-D CMAKE_INSTALL_PREFIX=/home/OpenCV
,四是..
。cmake
表示对项目进行分析构建,-D
相当于定义,-D
可以理解为告诉cmake后边要设置一个属性的值,每设置一个属性,就需在属性前面加上一个-D
。CMAKE_BUILD_TYPE
表示要编译的类型,类型有debug,release。CMAKE_INSTALL_PREFIX
是安装路径。..
表示要分析的CMakeLists.txt文件
在上一层目录中。
所谓安装,即是是将需要的头文件和库文件拷贝到指定的目录中,默认情况下,会是/usr目录
下,有时是/usr/local目录
下。
第一个工程
现假设一个C++项目中只有一个源文件 main.cpp,代码为:
代码清单1:main.cpp
#include<iostream>
int main()
{
std::cout<<"Hello word!"<<std::endl;
return 0;
}
为了构建该项目,需要编写文件 CMakeLists.txt 并将其与 main.cpp 放在同一个目录中,CMakeLists.txt文件中的内容如下:
代码清单2:CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT(main)
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
ADD_EXECUTABLE(main ${DIR_SRCS})
CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的,符号"#"后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔。
例如对于清单2的 CMakeLists.txt 文件:第一行的命令限定了 CMake 的版本。第二行是一条命令,名称是 PROJECT,参数是 main,该命令表示项目的名称是 main 。第三行使用命令 AUX_SOURCE_DIRECTORY 将当前目录中的源文件名称赋值给变量 DIR_SRCS 。 CMake 手册中对命令 AUX_SOURCE_DIRECTORY 的描述如下:
aux_source_directory(\<dir> \<variable>)
该命令会把参数 <dir>
中所有的源文件名称赋值给参数 <variable>
。 第四行使用命令 ADD_EXECUTABLE
把变量 DIR_SRCS
存储的目录中的源文件编译成一个名称为 main
的可执行文件。
完成了文件 CMakeLists.txt 的编写后需要使用 cmake
或 ccmake
命令生成Makefile
。 ccmake
与命令 cmake
的不同之处在于 ccmake
提供了一个图形化的操作界面。cmake
命令的执行方式如下:
cmake [options] <path-to-source>
这里我们进入了 main.cpp 所在的目录后执行 “cmake .
” 后就可以得到 Makefile 并使用 make 进行编译,如下图所示。
图 1 cmake
的运行结果
图 2 make
的运行结果
图 3 可执行程序main
的运行结果
处理多源文件目录的方法
CMake 处理源代码分布在不同目录中的情况也十分简单。现假设源代码分布的情况如下:
./Project_1_ym
|
+---main.cpp
|
+---src
|
+---Circle.h
|
+---Circle.cpp
对程序文件进行编译,会产生两类文件,一是可执行文件,二是库文件,其中含有main函数的将被编译成可执行文件,其他的则别编译成库文件。而在此多源文件的C++项目中,main.cpp将被编译成可执行文件,src目录下的文件将被编译成库文件。
如下为C++项目中各C++文件的代码清单:
代码清单1:main.cpp
#include <iostream>
#include "Circle.h"
using namespace std;
int main()
{
Circle c(3);
cout<<"Area="<<c.Area()<<endl;
return 1;
}
代码清单2:Circle.h
#ifndef CIRCLE_H
#define CIRCLE_H
class Circle
{
private:
double r;//半径
public:
Circle();//构造函数
Circle(double R);//构造函数
double Area();//求面积函数
};
#endif
代码清单3:Circle.cpp
#include "Circle.h"
Circle::Circle(){this->r=5.0;}
Circle::Circle(double R){this->r=R;}
double Circle::Area(){return 3.14*r*r;}
使用CMake构建C++项目的步骤如下:
(1)在项目根目录Project_1_ym
中创建文件 CMakeLists.txt 。文件内容如下:
代码清单4:根目录Project_1_ym
中的CMakeLists.txt
PROJECT(Project_1_ym)
INCLUDE_DIRECTORIES(src)
ADD_SUBDIRECTORY(src)
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
ADD_EXECUTABLE(main ${DIR_SRCS})
TARGET_LINK_LIBRARIES(main Circle)
相对于以上第一个工程的CMakeLists.txt
文件,该文件添加了下面的内容: 第二行,使用命令INCLUDE_DIRECTORIES
以加入头文件搜索路径,第三行,使用命令 ADD_SUBDIRECTORY
指明本项目包含一个子目录 src 。第六行,使用命令 TARGET_LINK_LIBRARIES
指明可执行文件 main
需要连接一个名为Circle
的链接库。
(2)在子目录src
创建文件CMakeLists.txt。文件内容如下:
代码清单5:子目录src中的 CMakeLists.txt
AUX_SOURCE_DIRECTORY(. DIR_PROJECT_1_YM_SRCS)
ADD_LIBRARY (Circle ${DIR_PROJECT_1_YM_SRCS})
在该文件中使用命令ADD_LIBRARY
将src
子目录中的源文件编译为共享库Circle
。
(3)执行cmake
和make
。
至此,完成了项目中所有CMakeLists.txt文件的编写,进入目录Project_1_ym
中依次执行命令 “cmake .
” 和 “make
” 得到结果如下:
图 1 cmake
的运行结果
图 2 make
的运行结果
图 3 可执行程序main
的运行结果
在执行cmake
的过程中,首先解析目录Project_1_ym
中的CMakeLists.txt
,当程序执行命令ADD_SUBDIRECTORY(src)
时进入目录src
对其中的CMakeLists.txt
进行解析。
在工程中查找并使用其他程序库的方法
在开发软件的时候我们会用到一些函数库,这些函数库在不同的系统中安装的位置可能不同,编译的时候需要首先找到这些软件包的头文件
以及链接库
所在的目录以便生成编译选项
。例如一个需要使用博克利数据库
的项目,需要头文件db_cxx.h
和链接库 libdb_cxx.so
,现在该项目中有一个源代码文件main.cpp
,放在项目的根目录中。如下为在main.cpp
文件中引用头文件和链接库的步骤:
第一步:程序库说明文件
在项目的根目录中创建目录cmake/modules/
,在cmake/modules/
下创建文件 Findlibdb_cxx.cmake ,内容如下:
清单1:文件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/
)
)
文件 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 中。清单5第3-8行的意思是在 /usr/include/ 和 /usr/local/include/ 中查找文件db_cxx.h ,并将db_cxx.h 所在的路径保存在 LIBDB_CXX_INCLUDE_DIR中。
命令 FIND_LIBRARY 同 FIND_PATH 类似,用于查找链接库并将结果保存在变量中。清单5第10-13行的意思是在目录 /usr/lib/ 和 /usr/local/lib/ 中寻找名称为 db_cxx 的链接库,并将结果保存在 LIBDB_CXX_LIBRARIES。
第二步:项目的根目录中的 CMakeLists.txt
在项目的根目录中创建 CmakeList.txt。内容如下:
清单6. 可以查找链接库的 CMakeLists.txt
PROJECT(main)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
SET(CMAKE_SOURCE_DIR .)
SET(CMAKE_MODULE_PATH ${CMAKE_ROOT}/Modules ${CMAKE_SOURCE_DIR}/cmake/modules)
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
ADD_EXECUTABLE(main ${DIR_SRCS})
FIND_PACKAGE( libdb_cxx REQUIRED)
MARK_AS_ADVANCED(
LIBDB_CXX_INCLUDE_DIR
LIBDB_CXX_LIBRARIES
)
IF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
MESSAGE(STATUS "Found libdb libraries")
INCLUDE_DIRECTORIES(${LIBDB_CXX_INCLUDE_DIR})
MESSAGE( ${LIBDB_CXX_LIBRARIES} )
TARGET_LINK_LIBRARIES(main ${LIBDB_CXX_LIBRARIES}18 )
ENDIF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
在该文件中第4行表示到目录 ./cmake/modules 中查找 Findlibdb_cxx.cmake ,8-19 行表示查找链接库和头文件的过程。第8行使用命令 FIND_PACKAGE 进行查找,这条命令执行后 CMake 会到变量 CMAKE_MODULE_PATH 指示的目录中查找文件 Findlibdb_cxx.cmake 并执行。第13-19行是条件判断语句,表示如果 LIBDB_CXX_INCLUDE_DIR 和 LIBDB_CXX_LIBRARIES 都已经被赋值,则设置编译时到 LIBDB_CXX_INCLUDE_DIR 寻找头文件并且设置可执行文件 main 需要与链接库 LIBDB_CXX_LIBRARIES 进行连接。
第三步,执行 cmake
完成 Findlibdb_cxx.cmake 和 CMakeList.txt 的编写后在项目的根目录依次执行 “cmake . ” 和 “make ” 可以进行编译,结果如下图所示:
图 4. 使用其他程序库时 cmake 的执行结果
使用其他程序库时 cmake 的执行结果
使用 cmake 生成 debug 版和 release 版的程序
在 Visual Studio 中我们可以生成 debug 版和 release 版的程序,使用 CMake 我们也可以达到上述效果。debug 版的项目生成的可执行文件需要有调试信息并且不需要进行优化,而 release 版的不需要调试信息但需要优化。这些特性在 gcc/g++ 中是通过编译时的参数来决定的,如果将优化程度调到最高需要设置参数-O3,最低是 -O0 即不做优化;添加调试信息的参数是 -g -ggdb ,如果不添加这个参数,调试信息就不会被包含在生成的二进制文件中。
CMake 中有一个变量 CMAKE_BUILD_TYPE ,可以的取值是 Debug Release RelWithDebInfo 和 MinSizeRel。当这个变量值为 Debug 的时候,CMake 会使用变量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_C_FLAGS_DEBUG 中的字符串作为编译选项生成 Makefile ,当这个变量值为 Release 的时候,工程会使用变量 CMAKE_CXX_FLAGS_RELEASE 和 CMAKE_C_FLAGS_RELEASE 选项生成 Makefile。
现假设项目中只有一个文件 main.cpp ,下面是一个可以选择生成 debug 版和 release 版的程序的 CMakeList.txt :
清单 7
PROJECT(main)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
SET(CMAKE_SOURCE_DIR .)
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
ADD_EXECUTABLE(main ${DIR_SRCS})
第 5 和 6 行设置了两个变量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_CXX_FLAGS_RELEASE, 这两个变量是分别用于 debug 和 release 的编译选项。 编辑 CMakeList.txt 后需要执行 ccmake 命令生成 Makefile 。在进入项目的根目录,输入 “ccmake .” 进入一个图形化界面,如下图所示:
图 5. ccmake 的界面
ccmake 的界面
按照界面中的提示进行操作,按 “c” 进行 configure ,这时界面中显示出了配置变量 CMAKE_BUILD_TYPE 的条目。如下图所示:
图 6. 执行了 configure 以后 ccmake 的界面
执行了 configure 以后 ccmake 的界面
下面我们首先生成 Debug 版的 Makefile :将变量 CMAKE_BUILD_TYPE 设置为 Debug ,按 “c” 进行 configure ,按 “g” 生成 Makefile 并退出。这时执行命令 find * | xargs grep “O0” 后结果如下:
清单 8 find * | xargs grep "O0"的执行结果
CMakeFiles/main.dir/flags.make:CXX_FLAGS = -O0 -Wall -g -ggdb
CMakeFiles/main.dir/link.txt:/usr/bin/c++ -O0 -Wall -g -ggdb
CMakeFiles/main.dir/main.cpp.o -o main -rdynamic
CMakeLists.txt:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
这个结果说明生成的 Makefile 中使用了变量 CMAKE_CXX_FLAGS_DEBUG 作为编译时的参数。
下面我们将生成 Release 版的 Makefile :再次执行命令 “ccmake .” 将变量CMAKE_BUILD_TYPE 设置为 Release ,生成 Makefile 并退出。执行命令 find * | xargs grep “O0” 后结果如下:
清单 9 find * | xargs grep "O0"的执行结果
CMakeLists.txt:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
而执行命令 find * | xargs grep “O3” 后结果如下:
清单 10. find * | xargs grep "O3"的执行结果
CMakeCache.txt:CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
CMakeCache.txt:CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
CMakeFiles/main.dir/flags.make:CXX_FLAGS = -O3 -Wall
CMakeFiles/main.dir/link.txt:/usr/bin/c++ -O3 -Wall
CMakeFiles/main.dir/main.cpp.o -o main -rdynamic
CMakeLists.txt:SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
这两个结果说明生成的 Makefile 中使用了变量 CMAKE_CXX_FLAGS_RELEASE 作为编译时的参数。
学习编写CMakeLists.txt文件
的一个重要手段,应该是不断地阅读自己和他人的CMakeLists.txt文件,并理解之。如下为一些CMakeLists.txt文件内容的链接。
ubuntu下使用CMake创建C++项目:https://blog.csdn.net/github_39611196/article/details/80991436
CMakeLists.txt编写和使用方法:https://blog.csdn.net/liudongdong19/article/details/81366624
cmake教程4(find_package使用):https://blog.csdn.net/haluoluo211/article/details/80559341
CMake如何查找链接库—find_package的使用方法:https://blog.csdn.net/u011092188/article/details/61425924
使用CMake构建OpenCV项目:https://blog.csdn.net/github_30605157/article/details/79839177
cmake 学习笔记(二):https://blog.csdn.net/dbzhang800/article/details/6329068