CMake入门笔记

2019-09-04 16:50:35

参考地址  CMake入门笔记

什么是CMake

CMake


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


Make


make工具是一个简化编译工作程序,有了它我们可以进行所谓的“自动化编译”,极大地提高了软件开发的效率。make工具相当于一个Shell,通过解释Makefile的中的命令进行工作。大多数IDE都有这个工具,比如:Visual C++的nmake,Linux下GNU的make。 

make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。


直白点说就是,CMake用来生成makefile或者是工程文件,make是用来解释makefile的。 

以Linux平台下使用gcc编译为例,我们想要编译源文件可以直接在命令行中执行gcc test.c -o test之类的命令,可是文件多了,工程复杂,而且文件不时的还会变,每次这样输多麻烦。 

这个时候make就有了用武之地,写个makefile,以后编译就直接make解释下makefile,根据makefile来执行编译命令。这样在linux上是很好,在windows上或者其他平台上怎么办呢? 

这时候就到cmake出马了,构建工程的时候,不在写makefile,而写CMakeLists.txt。需要编译的时候,先用Cmake解释CMakeLists.txt。如果在Windows平台上,设置编译器是VS,CMake就生成一个VS的工程,然后用VS来运行编译项目。如果是在Linux平台上,编译器是gcc,CMake就生成makefile,然后用make来解释makefile编译项目。 

如图,分别为原始目录、在Linux下选择gcc为编译工具生成的工程文件和在WIndows下选用Visual Studio为编译器生成的工程文件: 



cmake执行后会在cmake执行时所在的目录生成许多文件,根据cmake执行目录是否与源文件同目录,将构建方式分为两种:


out-of-source build,构建输出文件与源文件放到不同目录中;

in-source build :构建输出文件与源文件放到相同目录中。

一般选择out-of-source方式,可以在项目目录下建立一个build文件,然后进入build下,执行cmake ../,使生成的文件都在build文件夹下,以便后期清理。


示例

为了编译与演示方便,示例全部在Linux下进行。


Hello, CMake!

CMake最简单的实例,目录结构如下: 

 

main.c文件:


 #include <stdio.h>


int main(int argc, char *argv[])

{

    printf("Hello, CMake!\r\n");


    return 0;

}

1

2

3

4

5

6

7

8

CMakeLists.txt文件


 # 版本限定

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)                      

# 项目名称

PROJECT(Test1)

# 添加源文件列表变量

SET(SRC_LIST main.cpp)

# 打印编译目录和项目目录路径

MESSAGE(STATUS "This is SOURCE dir " ${PROJECT_SOURCE_DIR})

# 生成可执行文件

ADD_EXECUTABLE(HelloCMake ${SRC_LIST})

1

2

3

4

5

6

7

8

9

10

然后进入项目目录,依次执行(需要确保linux下安装了gcc、make等):


mkdir build

cd build

cmake ../

1

2

3

此时,build文件夹内容如下: 

 

。然后键入make命令,即解析了Makefile文件,build目录下的内容变为: 

 

可以看到,生成了一个HelloCMake的可执行文件,键入./HelloCMake可以运行HelloCMake文件。 

bash执行输出过程如下: 



同一目录多个源文件

在实际使用中,很少会出现这么简单的情况,往往在一个文件夹中会存在多个源文件要求编译,目录如下,这时候只需要在SET后面加上需要编译的源文件就可以了。 

 

CMakeLists.txt文件:


# 版本限定

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)                      

# 项目名称

PROJECT(Test2)

# 添加源文件列表变量

SET(SRC_LIST Stupid.cpp main.cpp)

# 打印编译目录和项目目录路径

MESSAGE(STATUS "This is BINARY dir " ${PROJECT_BINARY_DIR})

MESSAGE(STATUS "This is SOURCE dir " ${PROJECT_SOURCE_DIR})

# 生成可执行文件

ADD_EXECUTABLE(Test2 ${SRC_LIST})

1

2

3

4

5

6

7

8

9

10

11

上述方式虽然可行,但是如果源文件过多,CMakeLists.txt文件编写起来也是一件很吃力的事情,这时候,可以用aux_source_directory来解决这个问题。aux_source_directory可以遍历指定文件夹的下源文件,将它们统一复制给一个变量,然后进行编译。


CMakeLists.txt文件2:


# 版本限定

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)                      

# 项目名称

project(Test3)


# 遍历当前目录所有的源文件,赋值给test3src

aux_source_directory(. test3src)

message(STATUS "src file : " ${test3src})

# 生成可执行文件

add_executable(Test3 ${test3src})

1

2

3

4

5

6

7

8

9

10

不同目录下的多个源文件

为了使源码更为清晰,源码往往会根据不同的功能,将代码放在不同的文件夹下,结构如图: 

 

这时候CmakeLists.txt文件又该如何编写呢?答案如下:


# 版本限定

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)                      

# 项目名称

project(Test3)


# 遍历当前目录所有的源文件,赋值给test3src

aux_source_directory(. main)

aux_source_directory(./stupid stupid)

set(test4src ${main} ${stupid})

message(STATUS "src file : " ${test4src})


# 生成可执行文件

add_executable(Test4 ${test4src})

1

2

3

4

5

6

7

8

9

10

11

12

13

编译动态库与静态库

在前面的示例中,我们都是直接生成了可执行文件,而更多的时候,我们需要生成的不是可执行文件,而是一个可以让复用的库文件,这时候只需要将add_executable(Name ${srcTemp})修改为add_library(Name ${srcTemp})就可以了。add_library可以在中间增加一个参数表示动态库或静态库,默认无参数为静态库。


# 版本限定

cmake_minimum_required(VERSION 2.8)


# 遍历当前目录所有的源文件,赋值给STUPID_SRC

aux_source_directory(. STUPID_SRC)

# 编译STUPID_SRC为静态库

add_library(stupid STATIC ${STUPID_SRC})

# 编译STUPID_SRC为动态库

# add_library(stupid SHARED ${STUPID_SRC})

1

2

3

4

5

6

7

8

9

库文件和头文件导出到指定位置

编译出的库文件是要使用的,而库文件的使用一般需要库文件和头文件。虽然可以编译出库后自己整理库文件和头文件,但是终归是一件麻烦事,尤其是工程比较庞大的时候。这时候,我们可以使用INSTALL来将库文件和头文件导出到指定位置。


# 版本限定

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

SET(OUTPUT ${CMAKE_CURRENT_BINARY_DIR})

AUX_SOURCE_DIRECTORY(. STUPID_SRC)

# 设置库文件输出目录

# SET(LIBRARY_OUTPUT_PATH ${LIB_DIRS})

# 创建动态库

ADD_LIBRARY(stupid SHARED ${STUPID_SRC})

# 导出动态库

INSTALL(TARGETS stupid DESTINATION ${OUTPUT}/libs)

# 导出头文件

INSTALL(DIRECTORY . DESTINATION ${OUTPUT}/include/stupid 

    FILES_MATCHING PATTERN "*.h")

1

2

3

4

5

6

7

8

9

10

11

12

13

然后进入项目目录/build/ 目录下,依次执行:


cmake ../

make 

make install

1

2

3

即可在build下生成以下结构: 

 

include为头文件目录,其中包含了stupid/stupid.h文件。libs为库文件目录,其中包含了libstupid.so文件。


使用动态库/静态库

在上一个示例中,我们生成并导出了动态库,现在来使用它。复制出上个示例导出的libs及include文件夹,形成以下结构: 

 

CMakeLists.txt文件内容如下:


# 版本限定

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)                      

# 项目名称

PROJECT(Test8)

# 遍历当前目录所有的源文件,赋值给test3src

AUX_SOURCE_DIRECTORY(. main)

MESSAGE(STATUS "src file : " ${main})

# 指定库文件搜索目录,必须为绝对路径

LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/libs)

# 生成可执行文件

ADD_EXECUTABLE(Test8 ${main})

# 指定头文件目录

INCLUDE_DIRECTORIES(./include)

# 指定目标需要连接的库

TARGET_LINK_LIBRARIES(Test8 stupid)


  • 2020-12-18 17:15:29

    coTurn stun服务器搭建,禁用turn

    https://github.com/coturn/coturn 在这里git clone 下来然后编译安装,一切默认即可。编译后,也可以不用安装。在编译目录下bin文件夹下有turnserver turnutils_stunclient turnutils_uclient 这三个等一下会用到的软件。

  • 2020-12-18 17:26:25

    coturn配置文件详细解释

    Coturn 是webrtc,p2p视频通话必不少的,主要包含2个主要功能stun服务, turn服务 Coturn 的githup地址为 https://github.com/coturn/coturn/

  • 2020-12-21 06:26:16

    为UIView添加点击事件

    最近经常碰到要将UIImageView和UILabel看成整体的情况,我于是就将他俩用UIView包起来,那么怎么给一个UIView添加点击事件,可以这么实现:

  • 2020-12-21 09:00:20

    Window.matchMedia() 方法详解

    matchMedia() 返回一个新的 MediaQueryList 对象,表示指定的媒体查询字符串解析后的结果。 matchMedia() 方法的值可以是任何一个 CSS @media 规则 的特性, 如 min-height, min-width, orientation 等。 MediaQueryList 对象有以下两个属性:

  • 2020-12-21 09:42:42

    iframe.contentWindow 操作iframe

    注:iframe.contentWindow这里,返回的是iframe的window对象,所以后面可以接着调用document方法,再接着调用getElementByTagName。那么就可以对iframe里面的元素进行操作了。

  • 2020-12-21 14:00:19

    iframe + postMessage跨域通信

    在实际项目开发中可能会碰到在 a.com 页面中嵌套 b.com 页面,这时第一反应是使用 iframe,但是产品又提出在 a.com 中操作,b.com 中进行显示,或者相反。

  • 2020-12-22 12:02:41

    ios开发优秀的开源框架,demo集合

    期待大家和我们一起共同维护,同时也期望大家随时能提出宝贵的意见(直接提交issues即可)。请广大网友只按照目录结构(即使目录结构有问题)添加三方库,并提交pull request。目录问题大家提出issues后楼主会及时更改的。