CMakeLists 写法总结

0.前言

之前简单介绍了makefile的写法,但实际工程中基本不会手写makefile,通常情况是会写一个CMakeLists甚至是多层多个CMakeLists来构建整个工程。

关于makefile和CMakeLists的关系:make命令实际上是按照makefile中的内容来执行的,CMakeLists就是用来生成makefile的,是cmake命令按照CMakeLists中的内容生成makefile文件。所以一般来说,复杂工程的编译过程是:

mkdir build && cd build (创建并进入build 文件夹)

cmake .. (cmake命令执行build文件夹之外的CMakeLists中的内容)

make (make命令执行在build文件夹中生成的makefile文件)

对于复杂的工程来说,手写makefile是很复杂的,而手写CMakeLists会简单很多。对于写程序而言,掌握CMakeLists的写法更加实用。

下面会进行4个实例的解释:

1. 最简单的工程,只有build文件夹和一个main.cc文件和一个CMakeLists.txt

2. 简单工程文件,包含3个文件夹build, include, src及一个CMakeLists.txt。

3. 多层CMakeLists.txt,可执行源程序入口在最外层,不断调用里层的库。

4. 多层CMakeLists.txt, 可执行源程序入口在最里层,适用于源码相同,编译环境不同的情况,如交叉编译等。

1. 最简单的工程,只有build文件夹和一个main.cc文件和一个CMakeLists.txt。

最简单的工程文件格式如图所示,其中build 文件夹是用来存放编译的过程文件和生成的可执行程序的结果文件的。这个文件夹通常自己创建,也可以删除。

 代码示例如下:

这里最简单的CMakeLists的写法,包含三个语句。

cmake_minimum_require:表示指定cmake的最小版本,因为有些cmake命令可能高版本才有

project(project name): 用来指定工程的名称

add_executable(execfile, main.cc):注意这句非常关键,是指定生成的可执行文件的名称,以及生成该可执行文件需要用到的源文件,在当前简单的示例中,只有一个源文件。

 题外话:对于build文件夹的理解和CMakeLists也有很大的关系。上面已经说明build文件夹中的内容其实由cmake命令按照我们写的CMakeLists的内容生成的,其中生成的内容就有makefilemakefile我们已经知道主要是各个文件之间的依赖关系。所以,如果makefile中的内容不需要修改,那make命令是可以用新生成的文件覆盖原有生成的内容的,比如仅仅修改了一些源代码的逻辑,那么就可以直接重新make。而如果明确一些中间过程的.o文件不再需要,则需要使用make clean这里需要注意的是make clean也是按照makefile中的内容去clean的,所以前提是makefile不需要修改而如果makefile变化,即文件之间的依赖关系发生改变,简单说我们修改了CMakeLists的内容,那么需要删除整个build文件夹,然后重新新建build文件夹进行编译

2.简单工程文件,包含3个文件夹build, include, src及一个CMakeLists.txt

下面我们增加一些文件,使得整个工程变得复杂一些,同时新增一些CMakeLists.txt当中常用的cmake命令。首先看一下工程目录,可以看到main.cc被放到了src文件夹下,并且与function.cc 文件发生了依赖关系。这个工程框架是更一般的框架,即包括一个src文件夹存放源文件,一个include文件夹存放头文件,build文件夹存放编译过程文件和可执行文件,一个CMakeLists.txt。

相比与第一种工程情况,CMakeLists.txt的写法增加了以下内容

aux_source_directory(),因为编译涉及的源文件不再只有main.cc,本例中虽然只有两个源文件,但今后的工程中可能涉及到的源文件非常的多,所以为了避免add_executable中的第二个参数写的过多和繁琐,用aux_source_directory将涉及到的源文件的文件夹用变量管理起来,这样在add_executable中就可以直接解读变量了。
 

include_directories(),这个语句也非常有用,主要是用来添加第三方依赖的头文件,相当于g++中的-l,也相当于在环境变量中增加路径。一般的我们都会在用到某个头文件的时候在代码中进行include的引用,实际工程中将这些头文件的位置进行归置,可以减少代码中include的复杂程度。具体如以下代码中,将include文件夹的路径添加之后,代码中可以直接include “function.h” 而不用写成 include “../include/function.h”,这在涉及到使用很多第三方库的时候,可以保证条理清晰,不混乱。

Set(), set通常用来给一些变量赋值,同时有类似作用的还有option()。个人的使用习惯时set从来给一些默认的变量赋值,如当前CMakeLists.txt中写到的CMAKE_CXX_FLAGS,用来指定编译器,CMAKE_BUILD_TYPE用来指定编译类型等。

Option(),用法同set类似,但个人习惯用它给自定义的变量赋值,用法如当前CMakeLists.txt当中所写的那样。

3. 多层CMakeLists.txt,可执行源程序入口在最外层,不断调用里层的库。

在现实的工程中,我们往往会有比较复杂的文件结构,而子模块生成库供主程序调用就是很典型的用法,掌握这种方法,也就掌握了直接拿别人的库文件和头文件,调用头文件中的函数功能的情况。看例子中文件结构,在2的基础上增加了module文件夹,里面包含一个源文件和头文件,以及单独所用在submodule的CMakeLists.txt。在主函数中,引用subtest头文件,并调用subtest()函数。

|——— build
|——— include
|    |—— function.h
|——— src
|    |—— function.cc
|——— submodule
|    |—— submodule.cc
|    |—— submodule.h
|    |—— CMakeLists.txt
|——— test3.cc
|——— CMakeLists.txt

首先看subsubmodule中的CMakeLists,里面只有两个语句

# 将当前目录下的源文件添加到SUB_DIR_LIB_SRCS变量
aux_source_directory(. SUB_DIR_SRC_LIST)

# 生成静态库
add_library(submodule ${SUB_DIR_SRC_LIST})

aux_source_directory():和之前的说的一样,用变量表示所有需要编译的源文件

add_library():这体现出和主函数文件的不同,除了主函数文件,其他源文件编译是以生成库的方式被主函数调用的。这个语句就是将涉及的需要编译的源文件编译成一个指定名字的动态库或者静态库,供上一层CMakeLists中进行调用

然后再看上一层CMakeLists.txt

# cmake最小版本需求
cmake_minimum_required(VERSION 3.0.0)

# 设置此项目的名称
project(test3) 

# 设置工程名,通常在复杂的工程中会用到,会引用${PROJECT_NAME}
set(PROJECT_NAME test3)

# 指定编译器
# CMAKE_C_FLAGS_DEBUG          ----  C 编译器
# CMAKE_CXX_FLAGS_DEBUG        ----  C++ 编译器
# -std=c++11  使用 C++11
# -g:只是编译器,在编译的时候,产生调试信息。
# -wall:生成所有警告信息。以下是具体的选项,可以单独使用
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11   -g  -Wall")

# 指定编译类型,debug 或者为 release
# debug 会生成相关调试信息,可以使用 GDB 进行
# release 不会生成调试信息。当无法进行调试时查看此处是否设置为 debug.
set(CMAKE_BUILD_TYPE Debug)

# 打印消息
MESSAGE("test3") 

# 将包含源文件的文件夹放入参数中,并给一个命名
# 可以多次使用,包含多个存放源文件的文件夹
aux_source_directory(./src SRC_LIST)
aux_source_directory(. SRC_LIST)

# 这里直接将上面的命名放入第二个参数中即可
add_executable(${PROJECT_NAME} ${SRC_LIST})

include_directories(./include)

# 添加子目录,这样上一层CMakeLists才能找到并调用子目录的CMakeLists
add_subdirectory(submodule)

# 设置编译时依赖的submodule静态库
target_link_libraries(${PROJECT_NAME} submodule)


新增了两个语句

add_subdirectory(),这是将我们除了src文件夹之外的包含源文件的文件夹路径告诉编译器,让编译器能够找到子文件夹及其中的CMakeLists.txt

target_link_libraries(),给当前的主程序链接子文件夹中生成的依赖库,这样主程序中使用到相应函数时,才能够在依赖的库里找到。

  • 8
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值