CMake学习笔记一:初识cmake

时光总嘲笑我的痴心妄想 提交于 2021-02-18 01:56:59

1 cmake简介

1.1 背景知识

cmake 是 kitware 公司以及一些开源开发者在开发几个工具套件(VTK)的过程中衍生品,最终形成体系,成为一个独立的开放源代码项目。项目的诞生时间是 2001 年。其官方网站是 www.cmake.org,可以通过访问官方网站获得更多关于 cmake 的信息。cmake的流行其实要归功于 KDE4 的开发,在 KDE 开发者使用了近 10 年 autotools之后,他们终于决定为 KDE4 选择一个新的工程构建工具,其根本原因用 KDE 开发者的话来说就是:只有少数几个“编译专家”能够掌握 KDE 现在的构建体系。在经历了 unsermake,scons 以及 cmake 的选型和尝试之后,KDE4 决定使用 cmake 作为自己的构建系统。在迁移过程中,进展异常的顺利,并获得了 cmake 开发者的支持。所以,目前的 KDE4 开发版本已经完全使用 cmake 来进行构建。像 kdesvn,rosegarden 等项目也开始使用 cmake,这也注定了 cmake 必然会成为一个主流的构建体系。

1.2 特点

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

  d.可扩展,可以为 cmake 编写特定功能的模块,扩充 cmake 功能。

<br />

2 安装cmake

<span style="color:orange">cmake 目前已经成为各大 Linux 发行版提供的组件,比如 Ubuntu 直接在系统中包含,所以,需要自己动手安装的可能性很小。如果你使用的操作系统没有提供 cmake 或者包含的版本较旧,有以下两种下载方法。 </span>

<span style="color:blue">可以直接在命令行下载cmake:</span>

$ sudo apt-get install cmakek

<span style="color:blue">也可以从官网下载最新版本:</span>

http://www.cmake.org/HTML/Download.html

<br />

3 最简单的例子:HelloWorld

<span style="color:orange">下面选择一个最简单的例子 Helloworld 来演练一下 cmake 的完整构建过程,并不 会深入的探讨 cmake,仅仅展示一个简单的例子,并加以粗略的解释。 </span>

<span style="font-size:16px"><b>1,准备工作 </b></span>

首先,在/backup 目录建立一个 cmake 目录,用来放置我们学习过程中的所有练习。

$ mkdir -p /backup/cmake

然后在 cmake 建立第一个练习目录 t1 。

$ cd /backup/cmake
$ mkdir t1
$ cd t1

在 t1 目录建立 main.c 和 CMakeLists.txt(注意文件名大小写)。

main.c 文件内容:

#include<stdio.h>
int main()
{
    printf("Hello world!\n");
    return 0;
}

CmakeLists.txt 文件内容:

PROJECT (HELLO)
SET(SRC_LIST main.c)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})

<span style="font-size:16px"><b>2,开始构建 </b></span>

所有的文件创建完成后,t1 目录中应该存在 main.c 和 CMakeLists.txt 两个文件。接下来我们来构建这个工程,在这个目录运行(注意命令后面的点号,代表本目录):

$ cmake .

输出大概是这个样子:

-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Check size of void*
-- Check size of void* - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- This is BINARY dir /backup/cmake/t1
-- This is SOURCE dir /backup/cmake/t1
-- Configuring done
-- Generating done
-- Build files have been written to: /backup/cmake/t1

<span style="color:orange">再让我们看一下目录中的内容,你会发现,系统自动生成了: CMakeFiles, CMakeCache.txt, cmake_install.cmake 等文件,并且生成了Makefile。</span>

现在不需要理会这些文件的作用,以后你也可以不去理会。最关键的是,它自动生成了Makefile.。

<span style="color:orange">然后进行工程的实际构建,在这个目录输入 make 命令,大概会得到如下的彩色输出: </span>

Scanning dependencies of target hello
[100%] Building C object CMakeFiles/hello.dir/main.o
Linking C executable hello
[100%] Built target hello

如果你需要看到 make 构建的详细过程,可以使用 make VERBOSE=1 或者 VERBOSE=1 make 命令来进行构建。

<span style="color:orange">这时候,我们需要的目标文件 hello 已经构建完成,位于当前目录,尝试运行一下: </span>

$ ./hello

得到输出:

Hello World from Main 

<span style="color:blue">恭喜您,到这里为止您已经完全掌握了 cmake 的使用方法。</span>

<br />

<span style="font-size:16px"><b>3,简单的解释 </b></span>

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

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

PROJECT (HELLO)
SET(SRC_LIST main.c)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})

<span style="color:red">PROJECT </span>指令的语法是 :

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

<span style="color:red">你可以用这个指令定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的,默认情况表示支持所有语言。</span>

这个指令隐式的定义了两个 cmake 变量: <span style="color:blue"><projectname>_BINARY_DIR 以及<projectname>_SOURCE_DIR,这里就是HELLO_BINARY_DIRHELLO_SOURCE_DIR(所以 CMakeLists.txt 中两个 MESSAGE指令可以直接使用这两个变量),因为采用的是内部编译,两个变量目前指的都是工程所在路径/backup/cmake/t1,</span>后面我们会讲到外部编译,两者所指代的内容会有所不同。

<span style="color:blue">同时 cmake 系统也帮助我们预定义了 PROJECT_BINARY_DIRPROJECT_SOURCE_DIR变量,他们的值分别跟 HELLO_BINARY_DIRHELLO_SOURCE_DIR 一致。为了统一起见,建议以后直接使用PROJECT_BINARY_DIRPROJECT_SOURCE_DIR,即使修改了工程名称,也不会影响这两个变量。</span>


<span style="color:red">SET</span> 指令的语法是 :

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

现阶段,你只需要了解 <span style="color:red">SET 指令可以用来显式的定义变量即可。 </span>

比如我们用到的是 SET(SRC_LIST main.c),如果有多个源文件,也可以定义成:

SET(SRC_LIST main.c t1.c t2.c)。


<span style="color:red">MESSAGE</span> 指令的语法是:

MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)

<span style="color:red">这个指令用于向终端输出用户定义的信息,包含了三种类型:</span>

  • SEND_ERROR,产生错误,生成过程被跳过。
  • SATUS,输出前缀为—的信息。
  • FATAL_ERROR,立即终止所有 cmake 过程.

我们在这里使用的是 STATUS 信息输出,演示了由 PROJECT 指令定义的两个隐式变量 HELLO_BINARY_DIR 和 HELLO_SOURCE_DIR。


<span style="color:red">ADD_EXECUTABLE</span> 指令的语法是:

ADD_EXECUTABLE(hello ${SRC_LIST})

<span style="color:red">定义了这个工程会生成一个文件名为 hello 的可执行文件,相关的源文件是 SRC_LIST 中定义的源文件列表,</span> 本例中你也可以直接写成 ADD_EXECUTABLE(hello main.c)。


在本例我们<span style="color:red">使用了 ${} 来引用变量,这是 cmake 的变量应用方式</span>,但是,有一些例外,比如在 IF 控制语句,变量是直接使用变量名引用,而不需要${}。如果使用了${}去应用变量,其实 IF 会去判断名为${}所代表的值的变量,那当然是不存在的了。

<span style="color:red">将本例改写成一个最简化的 CMakeLists.txt: </span>

PROJECT(HELLO)
ADD_EXECUTABLE(hello main.c)

<br />

4 基本语法规则

前面提到过,cmake 其实仍然要使用”cmake 语言和语法”去构建,上面的内容就是所谓的”cmake 语言和语法”,最简单的语法规则是:

  1. <span style="color:orange">变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名</span>
  2. <span style="color:orange">指令(参数 1 参数 2... ) 【参数使用括弧括起,参数之间使用空格或分号分开】</span>
  3. <span style="color:orange">指令是大小写无关的,参数和变量是大小写相关的。但推荐你全部使用大写指令。 </span>

<br />

5 关于语法的疑惑

cmake 的语法还是比较灵活而且考虑到各种情况,比如

SET(SRC_LIST main.c)也可以写成 SET(SRC_LIST “main.c”)

是没有区别的,但是假设一个源文件的文件名是 fu nc.c(文件名中间包含了空格)。这时候就必须使用双引号,如果写成了 SET(SRC_LIST fu nc.c),就会出现错误,提示你找不到 fu 文件和 nc.c 文件。这种情况,就必须写成: SET(SRC_LIST “fu nc.c”)

<br />

6 清理工程

运行:

make clean

即可对构建结果进行清理。

<br />

7 内部构建与外部构建

上面的例子展示的是“内部构建”,相信看到生成的临时文件比您的代码文件还要多的时候,估计这辈子你都不希望再使用内部构建。

而外部编译的过程如下:

  1. 首先,请清除 t1 目录中除 main.c CmakeLists.txt 之外的所有中间文件,最关键的是 CMakeCache.txt。
  2. 在 t1 目录中建立 build 目录,当然你也可以在任何地方建立 build 目录,不一定必须在工程目录中。
  3. <span style="color:orange">进入 build 目录,运行 cmake ..</span>(注意,..代表父目录,因为父目录存在我们需要的CMakeLists.txt,如果你在其他地方建立了 build 目录,需要运行 cmake <工程的全路径>),查看一下 build 目录,就会发现了生成了编译需要的 Makefile 以及其他的中间文件。
  4. <span style="color:orange">运行 make 构建工程,就会在当前目录(build 目录)中获得目标文件 hello。</span>

<span style="color:blue">上述过程就是所谓的 out-of-source 外部编译,一个最大的好处是,对于原有的工程没有任何影响,所有动作全部发生在编译目录。</span>通过这一点,也足以说服我们全部采用外部编译方式构建工程。

<br />

8 小结

<span style="color:orange">本小节描述了使用 cmake 构建 Hello World 程序的全部过程,并介绍了三个简单的指令:PROJECT/MESSAGE/ADD_EXECUTABLE 以及变量调用的方法,同时提及了两个隐式变量<projectname>_SOURCE_DIR 及<projectname>_BINARY_DIR,演示了变量调用的方法 。</span>

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!