CMake:CMakeLists.txt文件的编写语法

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_DIRPROJECT_SOURCE_DIR变量,它们的值分别跟<projectname>_BINARY_DIR<projectname>_SOURCE_DIR一致。为了统一起见,建议以后直接使用PROJECT_BINARY_DIRPROJECT_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.solibname项只需写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.cmakeConfig-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_FLAGSCMAKE_C_FLAGS。使用这两种方式在有的情况下效果是一样的,但请注意它们还是有区别的:

add_compile_options命令添加的编译选项是针对所有编译器的(包括c和c++编译器),而set命令设置CMAKE_C_FLAGSCMAKE_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_FILEFIND_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后边要设置一个属性的值,每设置一个属性,就需在属性前面加上一个-DCMAKE_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 的编写后需要使用 cmakeccmake 命令生成Makefileccmake 与命令 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_LIBRARYsrc子目录中的源文件编译为共享库Circle

(3)执行cmakemake

至此,完成了项目中所有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

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页