CMakeLists.txt常用指令总结与练习

前言

CMake语言不区分大小写,但是参数区分大小写
掌握这些常用的指令,90%的情况就可以应对了,剩下的特殊指令遇到了再去查找即可
我们给CMake添加项目配置描述时,通过将CMake编写在CMakeLists.txt文件中,其中CMakeLists.txt文件必须区分大小写,CMake才能够解析

cmake_minimum_required (VERSION XX):CMake最低版本

CMake的最低版本要求:
cmake_minimum_required (VERSION 2.8),说明最低版本要求为2.8

project (demo):CMake工程名

Cmake工程名:
project (demo),这里以名称demo为例,并且支持一切语言
project (demo CXX),这里以名称demo为例,并且支持C语言
project (demo C CXX),这里以名称demo为例,并且支持C/C++语言

add_executable(main main.c):生成可执行文件

指定最终要生成的elf(可执行)文件名称以及所用到的源文件:
add_executable(main main.c),例如,生成的elf文件的名字叫main,使用的源文件是main.c

例如:这种是属于同一目录下只有单个源文件
假设有个main.c的源文件,使用CMake编译只需要在main.c文件同级目录中新建CMakeLists.txt文件,并且写入以上三条即可

//最低版本
cmake_minimum_required (VERSION 2.8)
//工程名称
project (demo)
//生成可执行文件
add_executable(main main.c)

然后在此目录下进行CMake命令,会产生MakeFile以及一些运行时自动生成的文件。所以此文件夹下会存在各种文件:CMakeFiles/CMakeLists.txt/MakeFile/main.c/CMakeCache.txt等等文件。看到MakeFile以后可以在终端输入make生成elf文件main,之后进行./main就可以查看到main.c中的内容了。

aux_source_directory(dir var):指定源文件放入变量

将指定目录下的源文件列表加入到变量中:
aux_source_directory(dir var),第一个参数dir是指定目录,第二个参数var是用于存放源文件列表的变量
aux_source_directory(. SRC_LIST),以当前目录为例,即源文件都是当前目录中,变量名为SRC_LIS。

为什么要这么做呢?前面我们提到add_executable(main main.c)在生成可执行我文件main时所用到的源文件只有一个main.c。如果源文件有两个怎么办,当然假设在同一目录下,add_executable(main main.c main1.c)。三个呢?更好办,直接后续上,100000个呢?不好办了喔。这个时候需要用到上述指令,他会将所有的源文件列表存放在变量SRC_LIST中,然后我们在add_executable(main ${SRC_LIST})生成可执行文时,读取变量即可,注意读取变量的办法。

举例:这种是属于同一目录下有多个源文件在这里插入图片描述
同一个目录下有多个源文件、头文件和一个CMakeLists.txt文件,现在利用CMake进行编译。CMakeLists.txt配置如下:

//最低版本
cmake_minimum_required (VERSION 2.8)
//工程名称
project (demo)
//源文件列表放入变量
aux_source_directory(. SRC_LIST)
//生成可执行文件
add_executable(main ${SRC_LIST})

然后在该目录下CMake生成MakeFile文件之后make生成可执行文件即可。

set(val src):指定源文件放入变量

aux_source_directory()也存在弊端,它会把指定目录下的所有源文件都加进来,可能会加入一些我们不需要的文件(比如头文件),因此我们使用set命令新建变量来将指定目录下的特定源文件放入变量中,这个指令也是我们常看到的。

例如:

set( SRC_LIST ./main.c ./testFunc1.c ./testFunc.c)
将当前目录下的main.c,testFunc1.c testFunc.c

对于set我们稍稍拓展,假设我们的main.c在E:/Thirdparty/x64下,那么写法为:

SET(VTK_SRC E:/Thirdparty/x64/main.c)

该行命令还可以写成如下两种形式

SET(VTK_SRC "E:/Thirdparty/x64/VTK") 或者 SET(VTK_SRC E:\\Thirdparty\\x64\\VTK)

将工程根目录下的testFunc下的testFunc.c源文件放到变量SRC_LIST中,注意根目录的写法
set (SRC_LIST ${PROJECT_SOURCE_DIR}/testFunc/testFunc.c)

include_directories (div div1):向工程添加指定头文件搜索路径

向工程添加多个指定头文件的搜索路径
其中div 和 div1都是包含头文件的文件夹路径,先看用法再来总结。
假设现在有如下目录结构:这种是属于不同目录下有多个源文件(源文件分布在不同的目录中)
在这里插入图片描述
通过CMake对它进行编译,编写CMakeList.txt文件

指定CMake的最低版本要求
cmake_minimum_required (VERSION 2.8)
指定工程名
project (demo)
将包含头文件的路径加入,也就是加入文件夹路径
include_directories (test_func test_func1)
将源文件列表存放到变量中
aux_source_directory (test_func SRC_LIST)
aux_source_directory (test_func1 SRC_LIST1)
生成两个可执行文件可执行文件
add_executable (main main.c ${SRC_LIST} ${SRC_LIST1})

如上图所示我们在main.c里面include(包含)了testFunc.h和testFunc1.h两个头文件,所以需要一个指令来指定头文件的所在位置,否则无法进行编译。include_directories命令就是这样一个作用,用来向工程添加多个指定头文件的搜索路径,路径之间用空格分隔。

当然除了在CMakeLists.txt文件中使用include_directories命令来添加头文件搜索路径以外,也可以通过在main.c中包含此路径:

#include "test_func/testFunc.h"
#include "test_func1/testFunc1.h"

link_directories :添加需要链接的库文件目录

相当于环境变量中增加LD_LIBRARY_PATH的路径的作用(export LD_LIBRARY_PATH)

LINK_DIRECTORIES("/opt/MATLAB/R2012a/bin/glnxa64"),相当于export LD_LIBRARY_PATH= $LD_LIBRARY_PATH:$MATLAB/bin/glnxa64


一般在大型的项目中,都是以

link_directories(
	${COMMONAPI_LIBDIR}	
	${COMMONAPI_SOMEIP_LIBDIR}
	${Boost_LIBRARY_DIR}
)

也可以具体化的去写

link_directories(
	${COMMONAPI_LIBDIR}	
	/home/ubuntu/Documents/....../lib
	/usr/local/....../lib
)

link_libraries: 添加需要链接的库文件路径,注意这里是全路径

比如:
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so")
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")

add_subdirectory(src bin):向当前工程添加存放源文件的子目录

将src子目录加入工程并且指定编译输出(包含编译中间结果)路径为bin目录。如果不进行bin目录的指定,那么编译结果包括中间结果就存放在build/src下了

当然上面都是源文件和头文件混房的情况,在将来的大项目中文件数目不止各位数,文件也都是按照包进行分类的。例如:src下面存放源文件,include下面存放头文件,build下面存放编译过程中产生的一些文件,最终输出的elf文件会放到bin目录下,这样结构更加清晰。通常单文件源文件的形式是最简单的。

将文件目录进行整理,源文件都放入src中,头文件都放入include中,最外层目录中添加一个CMakeLists.txt文件,src源文件目录下也添加一个CMakeLists.txt文件。外面的CMakeLists.txt用来进行cmake时使用,里面的CMakeLists.txt是让外面的通过add_subdirectory来调用的。
在这里插入图片描述

此时我们外层的CMakeLists.txt配置:

cmake_minimum_required (VERSION 2.8)
project (demo)
向工程添加包含源文件的子目录
add_subdirectory (src)

add_subdirectory这个指令向当前工程添加存放源文件的子目录。src目录下存放了源文件,当执行cmake时,就会进入src目录下去找src目录下的CMakeLists.txt,所以在src目录下也建立一个CMakeLists.txt,其中内容为:

将该目录下的所有源文件列表添加到变量SRC_LIST中
aux_source_directory (./ SRC_LIST) ——> 可以替换成set(SRC_LIST ./)
添加包含头文件搜索路径,../include表示上一级目录中的include中
include_directories (../include)
产生可执行文件main,产生可执行文件所需要的源文件来自${SRC_LIST},即变量所存放的源文件列表
add_executable (main ${SRC_LIST})
将编译输出即可执行文件放在根目录的bin下,此指令介绍见下面
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

然后在build目录下cmake ..,生成MakeFile文件,再make生成bin下的可执行文件。在build目录下运行cmake,生成的附带文件就会待在build目录下,就不会跟源码文件混在一起,如果我们不想要这些文件了就可以直接清空build目录,非常方便。

到这儿应该对于CMake的运行有了一定的了解,CMake生成可执行文件,CMakeLists.txt文件中包含基本的几项设置:

1、CMake最低版本的指定
2、工程名称的设置
3、源文件列表的添加
4、头文件搜索路径的指定
5、编译输出位置的指定
6、可执行文件的生成

上面的小项目编译我们使用了两个CMakeLIsts.txt文件进行,通过上面的运行所需条件整理成一个,只留一个外层的文件,内层的CMakeLIsts.txt文件可以删掉

最低版本的指定
cmake_minimum_required (VERSION 2.8)
工程名称的设置
project (demo)
源文件列表的添加
aux_source_directory (src SRC_LIST) ——> 可以替换成set(SRC_LIST src)
添加包含头文件搜索路径,../include表示上一级目录中的include中
include_directories (include)
产生可执行文件main,产生可执行文件所需要的源文件来自${SRC_LIST},即变量所存放的源文件列表
add_executable (main ${SRC_LIST})
将编译输出即可执行文件放在根目录的bin下,此指令介绍见下面
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

将编译结果,elf文件的输出位置定在根目录下bin目录中

EXECUTABLE_OUTPUT_PATH :目标二进制可执行文件的存放位置
PROJECT_SOURCE_DIR:工程的根目录
所以set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin),这里的意思是把存放elf文件的位置设置为工程根目录下的bin目录
testFunc_static就是CMake的预定义变量

add_library (testFunc_shared SHARED ${SRC_LIST}):构建静态、动态库

testFunc_shared :生成动态或者静态库的名称
SHARED : 构建动态库,如果是静态库:STATIC
${SRC_LIST} :生成库所使用的源文件

下面代码是同时构建出静态和动态库,且名字不同,但一般要求名字相同,只是后缀不同,见后面set_target_properties指令

add_library (testFunc_shared SHARED ${SRC_LIST})
add_library (testFunc_static STATIC ${SRC_LIST})

生成名字为libtestFunc_shared、libtestFunc_static的静态和动态库,源文件均来自变量SRC_LIST

要注意上面一行中的名字前面都默认加了lib前缀

set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

将库文件的输出位置定义在根目录下的lib目录中
LIBRARY_OUTPUT_PATH 是CMake的预定义变量,表示库文件的输出地址
${PROJECT_SOURCE_DIR}/lib表示根目录下的lib目录
所以 set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)表示库文件的默认输出路径为工程目录下的lib目录

set_target_properties用来设置动态静态库的名称和版本号

用来设置输出的名称,对于动态库,还可以设置动态库的版本号
一般情况下我们在为一个项目同时构建动态和静态库的时候,会用到这个语句:

add_library (testFunc_shared SHARED ${SRC_LIST})
add_library (testFunc_static STATIC ${SRC_LIST})

但是这个语句构建的动态库和静态库名字不同,一般要求静态和动态的名称相同,只是后缀不同而已。静态库的拓展名一般为:.a 或者.lib,动态库的拓展名为:.so或者.dll。静态库在编译是时可以直接整合到目标文件中,生成的可执行文件可以独立运行。动态库则相反,不会整合到目标文件中,生成的可执行文件也不能独立运行。

对于静态和动态库的名字保持相同,后缀不同,有可爱就开始走极端啦,下面这么搞就行了嘛

add_library (testFunc SHARED ${SRC_LIST})
add_library (testFunc STATIC ${SRC_LIST})

不好意思,零分,还要赏你50大板外加100个嘴巴子,这样只会构建一个动态库不会构建出静态库的
这时候就需要用到指令set_target_properties。
通过上面的指令我们已经构建出libtestFunc_shared、libtestFunc_static的静态、动态库文件,名字不同,我们采用下面指令进行更改,将其名称都改为libtestFunc,在生成静态、动态库文件之后文件名称分别为:libtestFunc.alibtestFunc.so

set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")

设置动态库版本号:

VERSION 1.2版本号,SOVERSION 1 API版本
set_target_properties (testFunc_static PROPERTIES VERSION 1.2 SOVERSION 1)

find_library ( name [path1 path2 …]):在指定目录下查找指定库文件,并把库文件的绝对路径存放到变量里

该指令的优势在于在build目录进行cmake … 的时候就进行库文件的查找,避免了在链接时不存在库文件的错误
例如:find_library(TESTFUNC_LIB testFunc HINTS ${PROJECT_SOURCE_DIR}/testFunc/lib)

在指定目录根目录下的testFunc/lib目录中去查找库文件
TESTFUNC_LIB:变量,用于存放库文件路径
testFunc :库名称
固定参数 HINTS

target_link_libraries:将目标文件与库文件进行链接

例如:target_link_libraries (main ${TESTFUNC_LIB})
${TESTFUNC_LIB}:变量,里面存放的是库文件的路径,可以抽象看作是库文件
main :这是由add_exexutable产生的可执行文件的名称
也就是将可执行文件与库文件链接起来

指令应用—静态和动态库的构建

下面通过一个简单的静态和动态库的构建,来熟悉常用的CMake指令。
还记得我们前面总结的利用CMake生成可执行文件的几项基本设置吗?CMake产生库文件也是一样的,我们也浅浅的写两句。

CMake生成库文件时,CMakeLists.txt文件中包含基本的几项设置:
1、CMake最低版本的指定
2、工程名称的设置
3、源文件列表的添加
4、头文件搜索路径的指定
5、加入生成库文件的指令
6、库文件的默认输出路径设置

假设现在有如下目录结构:
在这里插入图片描述
要生成动态库与静态库文件,第一步我们肯定是编写CMakeLists.txt文件

指定CMake最低版本
cmake_minimum_required (VERSION 3.5)
指定工程的名称
project (demo)
引入源文件列表到变量中,源文件只有一个
set(SRC_LIST  ${PROJECT_SOURCE_DIR}/testFunc/testFunc.c)
指定头文件搜索路径
include_directories (${PROJECT_SOURCE_DIR}/testFunc/testFunc.h)
添加生成库的指令
add_library (testFunc_shared SHARED ${SRC_LIST})
add_library (testFunc_static STATIC ${SRC_LIST})
将动态库与静态库名称改成相同的,区别就在于文件后缀名不同
set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")
设置库文件的输出地址,在根目录下的lib中
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

testFunc.c文件内容为:
在这里插入图片描述
testFunc.h文件内容为:
在这里插入图片描述
同上,为了让编译过程中生成的各种文件保持在一个文件中,我们在build中进行cmake ..,后面的..是为了与CMakeLists.txt文件在同一级目录下。
通过cmake生成MakeFile之后进行make,完成之后切换到根目录lib下就可以看到生成的动态与静态库文件libtestFunc.alibtestFunc.so
在这里插入图片描述
到这里库文件就构建完成了,==构建库文件是为了给其他函数所调用,也就是说库文件是编译好的二进制文件,其他程序加上头文件就可调用起来,但库文件调用前需要进行link,==那么如何调用呢?如何通过link动态库生成可执行文件呢?请往下看。

链接库

前面我们已经构建出静态与动态库,也就是已经拿到了有项目编译好的二进制文件,但这个文件没有办法直接运行出结果来,需要其他程序来调用,也就是要将库文件进行link,通过链接库文件生成可执行文件elf。
我们重新建立项目,目录结构如下图所示,将上面所构建出的静态与动态库文件拷贝到testFunc/lib下进行连接,同时将testFunc.h头文件也拷贝过来。
在这里插入图片描述
其中源文件main.c为,我们调用了testFunc.c中的函数show(),而且show()的函数原型就在testFunc.h头文件中,我们现在新建源文件通过link库文件的方法去调用show()函数,如果最后可执行文件的结果显示:Hello World 52000就说明正确。

#include <stdio.h>
#include <testFunc.h>

int main(void){
	int AAA = 52000;
	show(AAA);
	return 0;
}

现在我们开始编写CMakeLists.txt文件

指定CMake最低版本
cmake_minimum_required (VERSION 3.5)
指定工程名称
project (demo)
将二进制可执行文件放入到根目录下的bin中
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
将源文件列表放入到变量SRC_LIST中
set (SRC_LIST ${PROJECT_SOURCE_DIR}/src/main.c)
添加头文件搜索路径
include_directories (${PROJECT_SOURCE_DIR}/testFunc/inc)
在${PROJECT_SOURCE_DIR}/testFunc/lib指定目录下去查找库文件,将库文件路径放入变量中
find_library(TESTFUNC_LIB testFunc HINTS ${PROJECT_SOURCE_DIR}/testFunc/lib)
产生可执行文件,生成可执行文件的源文件来源于变量SRC_LIST
add_executable (main ${SRC_LIST})
将可执行文件与库文件连接起来
target_link_libraries (main ${TESTFUNC_LIB})

然后我们切换到build目录下进行 cmake .. && make。再切回根目录下bin目录中查看已经存在main可以执行文件,./main即可输出show()所需打印的内容,输出程序最后想要的结果。

在这里插入图片描述

set(CMAKE_BUILD_TYPE “Debug”)设置调试模式

前几天遇到一个问题,来更新一下帖子。在Linux系统上采用Cmake+make的方式调试C语言代码的时候,发现打的断点根本停不下来,直接一镜到底非常丝滑。这个时候上网查找了一下原因才发现,是因为在CMakeLists.txt中缺少了配置,需要加上配置:set(CMAKE_BUILD_TYPE "Debug"),之后成功解决。

总结与思考:

1、生成静态与动态库文件的时候是没有add_executable命令的,因为只是构建出库文件,由库文件可以link出可执行文件,并没有直接产生出可执行文件。
2、CMake直接产生可执行文件还是先产生库文件,再进行链接产生可执行文件,CMakeLists.txt文件的编写都是很套路性,最基本的、常用的记住,,其他的现常就行了噻。
3、困死了,摆一会儿~

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江南霹雳堂雷家雷无桀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值