CMake入门实践(一) 什么是cmake

一.CMake简介

CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。只是 CMake 的组态档取名为 CMakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的projects/workspaces),然后再依一般的建构方式使用。这使得熟悉某个集成开发环境(IDE)的开发者可以用标准的方式建构他的软件,这种可以使用各平台的原生建构系统的能力是 CMake 和 SCons 等其他类似系统的区别之处。

cmake 的特点主要有:

  1. 开放源代码,使用类 BSD 许可发布。http://cmake.org/HTML/Copyright.html
  2. 跨平台,并可生成 native 编译配置文件,在 Linux/Unix 平台,生成 makefile,在苹果平台,可以生成 xcode,在 Windows 平台,可以生成 MSVC 的工程文件。
  3. 能够管理大型项目,KDE4 就是最好的证明。
  4. 简化编译构建过程和编译过程。Cmake 的工具链非常简单:cmake+make。
  5. 高效虑,按照 KDE 官方说法,CMake 构建 KDE4 的 kdelibs 要比使用 autotools 来构建 KDE3.5.6 的 kdelibs 快 40%,主要是因为 Cmake 在工具链中没有 libtool
  6. 可扩展,可以为 cmake 编写特定功能的模块,扩充 cmake 功能。

在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:

  1. 编写 CMake 配置文件 CMakeLists.txt 。
  2. 执行命令 cmake PATH 或者 ccmake PATH 生成 Makefile。其中, PATH 是 CMakeLists.txt 所在的目录。
  3. 使用 make 命令进行编译。

二.Hello World

没有”Hello World”的教程不是好教程.这里首先举一个Hello world的例子给大家做一个对于CMake的感性理解.
详细的项目代码可以见: LearningCMake/HelloWorld/

比如我现在项目目录叫做HelloWorld,其中建立两个文件,分别是main.cppCMakeLists.txt(注意文件名大小写):

main.cpp文件中就是非常简单的输出Hello world的代码了,如下:

//main.cpp
#include <iostream>
int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

CmakeLists.txt 文件内容:

cmake_minimum_required(VERSION 3.9)
project(HelloWorld)

set(CMAKE_CXX_STANDARD 11)

add_executable(HelloWorld main.cpp)

所有的文件创建完成后,HelloWorld目录中应该存在 main.cppCMakeLists.txt 两个文件. 接下来我们来构建这个工程,在这个目录运行:

cmake .

注意命令后面的点号,代表本目录.

输出大概是这个样子:

-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/enningxie/xiekun/WorkSpace/LearningCMake/HelloWorld

再让我们看一下目录中的内容:
你会发现,系统自动生成了:CMakeFiles, CMakeCache.txt, cmake_install.cmake 等文件,并且生成了Makefile. 现在不需要理会这些文件的作用,以后你也可以不去理会。最关键的是,它自动生成了Makefile.

然后进行工程的实际构建,在这个目录输入make 命令,大概会得到如下的彩色输出:

Scanning dependencies of target HelloWorld
[ 50%] Building CXX object CMakeFiles/HelloWorld.dir/main.cpp.o
[100%] Linking CXX executable HelloWorld
[100%] Built target HelloWorld

如果你需要看到 make 构建的详细过程,可以使用 make VERBOSE=1 来进行构建。
这时候,我们需要的目标文件 HelloWorld已经构建完成,位于当前目录,尝试运行一下:
./HelloWorld得到输出:

Hello,World!

三.基本用法解释

上面用一个非常简单的HelloWorld例子简单介绍了构建使用cmake构建项目的完整过程.但是其中还有很多地方新手可能不是很理解.比如CMake怎么使用. CMakeList.txt 里面到底是一些什么东西等等. 这部分就来讲一下上面那个简单的项目到底用了一些什么东西.更加复杂的以后再讲到,这里确保把最基本的一些给理清楚.

重新看一下 CMakeLists.txt,这个文件是 cmake 的构建定义文件,文件名
是大小写相关的,如果工程存在多个目录,需要确保每个要管理的目录都存在一个
CMakeLists.txt。(关于多目录构建,后面我们会提到,这里不作过多解释)。

上面例子中的 CMakeLists.txt 文件内容如下:

cmake_minimum_required(VERSION 3.9)

project(HelloWorld)

set(CMAKE_CXX_STANDARD 11)

add_executable(HelloWorld main.cpp)

先看第一行cmake_minimum_required(VERSION 3.9),这行看意思都能够直接看出来了,表示指定运行此配置文件所需的 CMake 的最低版本;所以看到其他的CMakeList文件没有写这个也是正常的.但是还是推荐写,是因为避免引起cmake不同版本之间构建错误的问题.

然后看第二行,project(HelloWorld) 该命令指定项目的名称,比如这里的项目名称为HelloWorld.更加详细的来说, project 指令的语法是:

project(projectname [CXX] [C] [Java])

你可以用这个指令定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的,
默认情况表示支持所有语言。所以我们常见使用的时候,就直接使用project(projectname) 就够了. 这个指令隐式的定义了两个 cmake 变量:
_BINARY_DIR 以及_SOURCE_DIR,这里就是
HelloWorld_BINARY_DIRHelloWorld_SOURCE_DIR.
因为采用的是内部编译,两个变量目前指的都是工程所在路径LearningCMake/HelloWorld/,后面我们会讲到外部编译,两者所指代的内容会有所不同。

同时 cmake 系统也帮助我们预定义了 PROJECT_BINARY_DIRPROJECT_SOURCE_DIR变量,他们的值分别跟 HelloWorld_BINARY_DIRHelloWorld_SOURCE_DIR 一致。 为了统一起见,建议以后直接使用 PROJECT_BINARY_DIRPROJECT_SOURCE_DIR,即
使修改了工程名称,也不会影响这两个变量。如果使用了
<projectname>_SOURCE_DIR,修改工程名称后,需要同时修改这些变量。

然后来看第三行set(CMAKE_CXX_STANDARD 11) 这里是把之后的编译选项设置为了C++ 11,

set 指令的语法是:

set (VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

现阶段,你只需要了解 set 指令可以用来显式的定义变量即可。
比如我们用到的是 set(SRC_LIST main.c),如果有多个源文件,也可以定义成:
set(SRC_LIST main.c t1.c t2.c)

然后我们来看最后一行add_executable(HelloWorld main.cpp),这行的作用就是将名为 main.cpp 的源文件编译成一个名称为 HelloWorld 的可执行文件。同样的,更加详细的用法如下:

add_executable(executable_name ${SRC_LIST})

定义了这个工程会生成一个文件名为 executable_name 的可执行文件,相关的源文件是 SRC_LIST
定义的源文件列表.

四.内部构建和外部构建

内部构建和外部构建这个词这么装逼,其实实际上面非常简单,别被吓到.

通俗一点,内部构建就是在项目内部,有CMakeList.txt的地方,直接cmake .,比如我们前面讲的简单案例都是最简单的内部构建. 结果你也看见了,就是在项目下面生成了很多的临时文件.

外部构建就是不直接在项目下面运行cmake, 而是自己建立一个接受cmake之后的临时文件的文件夹,然后再该文件夹下面调用cmake <CMakeList_path> 来构建.运行 make 构建工程,就会在当前目录(build 目录)中获得目标文件 hello。上述过程就是所谓的out-of-source外部编译,一个最大的好处是,对于原有的工程没有任何影响,所有动作全部发生在编译目录。

有编译过OpenCV或者其他类似项目的童鞋,都应该知道,编译OpenCV的时候,我们常常自己建立一个build目录,然后在该目录下面运行cmake,最终临时文件就会在这个目录下面生成.

这里直接举一个例子,完整项目代码:LearningCMake/4.HelloWorld4/

这里代码结构和CMakeList.txt都和第一个最简单的是差不多的.但是这里有一个不同,就是在里面建立了一个build文件夹.然后再build文件夹里面运行cmake ..,结果就是之前的临时文件都存放在了这个build文件夹里面,同时在这个文件夹里面直接make可以得到最终的可执行文件.

到了这里,结合CMakeList.txt文件中每个命令语句和生成执行文件的详细步骤.你应该对于cmake的运作过程有了一个基本的了解.当然这并不等于全部,有了这些基础,后面慢慢介绍更多更加常用的用法.

猜你喜欢

转载自blog.csdn.net/xierhacker/article/details/79445339
今日推荐