Linux下CMake使用方法
在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:
- 编写 CMake 配置文件 CMakeLists.txt 。
- 执行命令
cmake PATH
或者ccmake PATH
生成 Makefile 1 1ccmake
和cmake
的区别在于前者提供了一个交互式的界面。。其中,PATH
是 CMakeLists.txt 所在的目录。 - 使用
make
命令进行编译。
本文将从实例入手,一步步讲解 CMake 的常见用法,文中所有的实例代码可以在这里找到。如果你读完仍觉得意犹未尽,可以继续学习我在文章末尾提供的其他资源。
1.单个源文件
对于简单的项目,只需要写几行代码就可以了。例如,假设现在我们的项目中只有一个源文件 main.c ,首先编写 CMakeLists.txt 文件,并保存在与 main.c 源文件同个目录下:
1 | # CMake 最低版本号要求 |
CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的。符号 #
后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔。
对于上面的 ==CMakeLists.txt== 文件,依次出现了几个命令:
cmake_minimum_required
:指定运行此配置文件所需的 CMake 的最低版本;project
:参数值是Demo1
,该命令表示项目的名称是Demo1
。add_executable
: 将名为 main.c 的源文件编译成一个名称为 Demo 的可执行文件。
之后,在当前目录执行 cmake .
,得到 Makefile 后再使用 make
命令编译得到 Demo1 可执行文件。
2.多个源文件
1) 同一目录,多个源文件
上面的例子只有单个源文件。现在假如把 power
函数单独写进一个名为 MathFunctions.c
的源文件里,使得这个工程变成如下的形式:
1 | ./Demo2 |
这个时候,CMakeLists.txt 可以改成如下的形式:
1 | # CMake 最低版本号要求 |
唯一的改动只是在 add_executable
命令中增加了一个 MathFunctions.cc
源文件。这样写当然没什么问题,但是如果源文件很多,把所有源文件的名字都加进去将是一件烦人的工作。更省事的方法是使用 aux_source_directory
命令,该命令会查找指定目录下的所有源文件,然后将结果存进指定变量名。其语法如下:
1 | aux_source_directory(<dir> <variable>) |
因此,可以修改 CMakeLists.txt 如下:
1 | # CMake 最低版本号要求 |
这样,CMake 会将当前目录所有源文件的文件名赋值给变量 DIR_SRCS
,再指示变量 DIR_SRCS
中的源文件需要编译成一个名称为 Demo 的可执行文件。
2) 多个目录,多个源文件
现在进一步将 MathFunctions.h 和 MathFunctions.cc 文件移动到 math 目录下。
1 | ./Demo3 |
于这种情况,需要分别在项目根目录 Demo3 和 math 目录里各编写一个 CMakeLists.txt 文件。为了方便,我们可以先将 math 目录里的文件编译成静态库再由 main 函数调用。
根目录中的 CMakeLists.txt :
1 | # CMake 最低版本号要求 |
该文件添加了下面的内容: 第3行,使用命令 add_subdirectory
指明本项目包含一个子目录 math,这样 math 目录下的 CMakeLists.txt 文件和源代码也会被处理 。第6行,使用命令 target_link_libraries
指明可执行文件 main 需要连接一个名为 MathFunctions 的链接库 。
==子目录中的 CMakeLists.txt==:
1 | # 查找当前目录下的所有源文件 |
在该文件中使用命令 add_library
将 src 目录中的源文件编译为静态链接库。
3.正规一点的组织结构
正规一点来说,一般会把源文件放到src目录下,把头文件放入到include文件下,生成的对象文件放入到build目录下,最终输出的elf文件会放到bin目录下,这样整个结构更加清晰。让我们把前面的文件再次重新组织下,
我们在最外层目录下新建一个CMakeLists.txt,内容如下,
1 | cmake_minimum_required (VERSION 2.8) |
这里出现一个新的命令add_subdirectory()
,这个命令==可以向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置==,具体用法可以百度。
这里指定src目录下存放了源文件,当执行cmake时,就会进入src目录下去找src目录下的CMakeLists.txt,所以在src目录下也建立一个CMakeLists.txt,内容如下,
1 | aux_source_directory (. SRC_LIST) |
这里又出现一个新的命令set
,是用于定义变量的,EXECUTABLE_OUT_PATH
和PROJECT_SOURCE_DIR
是CMake自带的预定义变量,其意义如下,
EXECUTABLE_OUTPUT_PATH :目标二进制可执行文件的存放位置
PROJECT_SOURCE_DIR:工程的根目录
所以,这里set的意思是把存放elf文件的位置设置为工程根目录下的bin目录。
添加好以上这2个CMakeLists.txt后,整体文件结构如下,
下面来运行cmake,不过这次先让我们切到build目录下,然后输入以下命令,cmake ..
Makefile会在build目录下生成,然后在build目录下运行make,
运行ok,我们再切到bin目录下,发现main已经生成,并运行测试,
测试OK!
这里解释一下为什么在build目录下运行cmake?从前面几个case中可以看到,如果不这样做,cmake运行时生成的附带文件就会跟源码文件混在一起,这样会对程序的目录结构造成污染,而在build目录下运行cmake,生成的附带文件就只会待在build目录下,如果我们不想要这些文件了就可以直接清空build目录,非常方便。
另外一种写法:
前面的工程使用了2个CMakeLists.txt,这种写法是为了处理需要生成多个elf文件的情况,最外层的CMakeLists.txt用于掌控全局,使用add_subdirectory
来添加要生成elf文件的源码目录。
如果只生成一个elf文件,那么上面的例子可以只使用一个CMakeLists.txt,可以把最外层的CMakeLists.txt内容改成如下,
1 | cmake_minimum_required (VERSION 2.8) |
==同时,还要把src目录下的CMakeLists.txt删除。==
4.动态库和静态库的编译控制
有时我们只需要编译出动态库,静态库,然后等着让其它程序去使用。让我们看下这种情况该如何使用cmake。首先按照如下重新组织文件,只留下testFunc.h和TestFunc.c,
我们会在build目录下运行cmake,并把生成的库文件存放到lib目录下。
最外层的CMakeLists.txt内容如下,
1 | cmake_minimum_required (VERSION 2.8) |
lib_testFunc目录下的CMakeLists.txt如下,
1 | aux_source_directory (. SRC_LIST) |
这里又出现了新的命令和预定义变量,
add_library: 生成动态库或静态库(第1个参数指定库的名字;第2个参数决定是动态还是静态,如果没有就默认静态;第3个参数指定生成库的源文件)
set_target_properties: 设置输出的名称,还有其它功能,如设置库的版本号等等
LIBRARY_OUTPUT_PATH: 库文件的默认输出路径,这里设置为工程目录下的lib目录
好了,让我们进入build目录下运行cmake ..,成功后再运行make,
cd到lib目录下进行查看,发现已经成功生成了动态库和静态库,
ps:可以看出前面使用
set_target_properties
重新定义了库的输出名字,如果不用set_target_properties
也可以,那么库的名字就是add_library里定义的名字,只是我们连续2次使用add_library指定库名字时,这个名字不能相同,而set_target_properties
可以把名字设置为相同,只是最终生成的库文件后缀不同,这样相对来说会好看点。
5.对库进行链接
既然我们已经生成了库,那么就进行链接测试下。把build里的文件都删除,然后在在工程目录下新建src目录和bin目录,在src目录下添加一个main.c和一个CMakeLists.txt,整体结构如下,
main.c内容如下,
1 |
|
修改工程目录下的CMakeLists.txt,如下,
1 | cmake_minimum_required (VERSION 2.8) |
只是使用add_subdirectory
把src目录添加进来。
src目录下的CMakeLists.txt如下,
1 | aux_source_directory (. SRC_LIST) |
这里出现2个新的命令,
link_directories
: 添加非标准的共享库搜索路径target_link_libraries
: 把目标文件与库文件进行链接
make成功,进入到bin目录下查看,发现main已经生成,并运行,
运行成功!
ps:在lib目录下有testFunc的静态库和动态库,
target_link_libraries (main testFunc)
默认是使用动态库,如果lib目录下只有静态库,那么这种写法就会去链接静态库。也可以直接指定使用动态库还是静态库,写法是:
1
2
3 target_link_libraries (main libtestFunc.so)
#或
target_link_libraries (main libtestFunc.a)
ps: 查看elf文件使用了哪些库,可以使用
readelf -d ./xx
来查看
6.添加编译选项
有时编译程序时想添加一些编译选项,如-Wall
,-std=c++11
等,就可以使用add_compile_options
来进行操作。
这里以一个简单程序来做演示,main.cpp如下
1 |
|
CMakeLists.txt内容如下,
1 | cmake_minimum_required (VERSION 2.8) |
整体目录结构如下,
然后cd到build目录下,执行cmake .. && make
命令,就可以在bin目录下得到main的elf文件
7. 添加控制选项
有时希望在编译代码时只编译一些指定的源码,例如本来要编译生成多个bin或库文件,现在只想生成某些指定的bin或库文件,这时可以使用cmake的option命令。
这里仍然使用例子来解释,假设我们现在的工程会生成2个bin文件,main1和main2,现在整体结构体如下,
外层的CMakeLists.txt内容如下,
1 | cmake_minimum_required(VERSION 2.8) |
这里使用了option
命令,其第一个参数是这个option的名字,第二个参数是字符串,用来描述这个option是来干嘛的,第三个是option的值,ON或OFF,也可以不写,不写就是默认OFF。
然后编写src目录下的CMakeLists.txt,如下
1 | cmake_minimum_required (VERSION 2.8) |
注意,这里使用了if-else
来根据option来决定是否编译main2.c
其中main1.c和main2.c的内容如下,
1 | // main1.c |
1 | // main2.c |
然后cd到build目录下输入cmake .. && make
就可以只编译出main1,如果想编译出main2,就把MYDEBUG
设置为ON,再次输入cmake .. && make
重新编译。
每次想改变MYDEBUG时都需要去修改CMakeLists.txt,有点麻烦,其实可以通过cmake的命令行去操作,例如我们想把MYDEBUG设置为OFF,先cd到build目录,然后输入cmake .. -DMYDEBUG=ON
,这样就可以编译出main1和main2 (在bin目录下)
来源:https://blog.csdn.net/qq_28114615/article/details/90406140
2020.3.29 19:01