CMake常用命令总结
- 前言
- cmake_minimum_required (VERSION XX):CMake最低版本
- project (demo):CMake工程名
- add_executable(main main.c):生成可执行文件
- aux_source_directory(dir var):指定源文件放入变量
- set(val src):指定源文件放入变量
- include_directories (div div1):向工程添加指定头文件搜索路径
- add_subdirectory(src bin):向当前工程添加存放源文件的子目录
- set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
- add_library (testFunc_shared SHARED ${SRC_LIST}):构建静态、动态库
- set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
- set_target_properties用来设置动态静态库的名称和版本号
- find_library (<VAR> name [path1 path2 ...]):在指定目录下查找指定库文件,并把库文件的绝对路径存放到变量里
- target_link_libraries:将目标文件与库文件进行链接
- 指令应用—静态和动态库的构建
- 链接库
- 总结与思考:
前言
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"
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.a
、libtestFunc.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.a
、 libtestFunc.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()
所需打印的内容,输出程序最后想要的结果。
总结与思考:
1、生成静态与动态库文件的时候是没有add_executable命令的,因为只是构建出库文件,由库文件可以link出可执行文件,并没有直接产生出可执行文件。
2、CMake直接产生可执行文件还是先产生库文件,再进行链接产生可执行文件,CMakeLists.txt
文件的编写都是很套路性,最基本的、常用的记住,,其他的现常就行了噻。
3、困死了,摆一会儿~