用了几次CMakeLists进行编译,下一次再用还是感觉乱乱的,这一次,把思路整理一下,认真学习一下CMakeLists应如何编写。
1、CMakeLists的作用
将一个.c或.cpp文件编译成目标文件时,CMakeLists定制整个编译流程,当编译涉及多个文件时,用来安排文件编译的先后顺序。
2、如何编写CMakeLists,一些基本语法
需要先了解一些基础的语法(只列出了一些用过的,其他的之后用过再补充呀~)
指令1:cmake_minimum_required
基本格式:cmake_minimum_required(VERSION <min>[...<max>])
说明:该指令指明了项目编译时要求的cmake的最低或最高版本。
例如:cmake_minimum_required(VERSION 3.17) #要求cmake的最低版本是3.17,编译时环境中的cmake版本要高于3.17才可顺利进行编译。
如果环境中的cmake版本为3.16.3,低于要求的最低版本3.17,编译时就会出现如下错误:
备注:也可以使用“...”(三个点)来连接cmake的最低版本和最高版本,例如cmake_minimum_required(VERSION 3.2...3.17)表示要求cmake版本在3.2和3.17之间。
指令2:project
基本格式:project(ProjectName)
说明:指明编译项目的名称,其中ProjectName表示项目名称,设置好后,后面可用变量PROJECT_NAME来指代项目名称。
例如:project(demo) 创建的项目名称为demo .
指令3:add_compile_options
基本格式:add_compile_options(<option> ...)
说明:为当前路径和下层路径的目标增加编译器命令行选项。需要注意的是,这个命令是针对所有类型编译器的,这里添加的选项会在所有的编译器中运用,比如-std=c++11是针对C++的编译器参数,也会被运用在C语言编译器中,可能会产生一些警告。可以使用其他命令单独设置某一编译器的编译选项。
例如:add_compile_options(-std=c++11)表示为所有编译器添加c++11支持。
指令4:include_directories
基本格式:include_directories([AFTER | BEFORE] [SYSTEM] dir1 [dir2…])
说明:用于在构建build中设置包含目录,主要是将指定目录添加到编译器的头文件搜索目录。默认情况下,该指令会将目录添加到列表最后(AFTER选项),可以使用AFTER或BEFORE选项来指定将目录添加到列表的前面还是后面。如果使用SYSTEM选项,会把指定目录当成系统的搜索目录,作用范围只在当前的CMakeLists.txt。
例如:include_directories(/usr/include/abc /usr/include/xxx)是将“/usr/include/abc”和“/usr/include/xxx”这两个目录添加至编译器的头文件搜索目录(两个目录用空格分隔)。
指令5:aux_source_directory
基本格式:aux_source_directory(<dir> < variable >)
说明:用于查找目录中的所有源文件(编译的就是源文件~)。收集dir目录中的所有源文件名称,并将列表存储在variable中,之后使用$ {variable}指代目录中的所有源文件。
例如:aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/demo DIR_SRCS)是指查找CMakeLists.txt所在目录下,demo目录中的所有源文件,将名称保存在DIR_SRCS变量中,后面需要源文件时,可以使用${DIR_SRCS}.
指令6:link_directories
基本格式:link_directories(directory1 [directory2 ...])
说明:用于添加库文件目录,在编译项目时依赖了某些库文件,使用本命令添加搜索目录(对应着directory1,directory2 …),就会在目录中搜索库文件。
备注:此命令尽可能避免使用,防止搜索库路径本地化而不准确,可使用target_link_directories来提供搜索路径或添加链接库。
指令7:target_link_directories
基本格式:target_link_directories(<target> <INTERFACE | PUBLIC | PRIVATE> [item1] [<INTERFACE | PUBLIC | PRIVATE> [item2...] ...])
说明:为特定的目标(target)添加链接库目录, INTERFACE、PUBLIC、PRIVATE是可选范围参数,比如target1 PUBLIC链接/map/bin/文件夹,该文件夹中有一些头文件,若target2链接了target1,就会链接到/map/bin/下的头文件(头文件传播)。该目标必须是已经通过add_executable()或add_library()等命令创建出来的目标,所以该命令应该放在add_executable()或add_library()这两个命令之后。
例如:add_executable(${PROJECT_NAME} ${DIR_SRCS})
target_link_directories(${PROJECT_NAME} /home/workspace/)
使用add_executable指令生成可执行文件,如前文所述,${DIR_SRCS}指代目录中所有源文件,用这些源文件生成可执行文件,可执行文件名称为${PROJECT_NAME}(与项目名称同名,前文中设成了demo).这个生成的可执行文件就是target_link_directories命令中的target,我们为该target添加了库文件搜索目录/home/workspace/ .
指令8:add_library
基本格式:add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [source1] [source2] [...])
说明:该命令是将指定的源文件生成链接文件,然后添加到工程中去。其中,source1,source2…是源文件(通常是.cpp和.h等), name是生成的链接文件的名称,STATIC表示将源文件生成静态链接库,SHARED表示生成动态链接库。
例如:add_library(xlog STATIC xlog.cpp)是将xlog.cpp文件编译成静态链接库文件,名字设置成xlog(实际生成的静态链接库文件名称为libxlog.a)。
指令9:add_executable
基本格式:add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] [source1][source2...])
说明:通过编译源文件来创建一个可执行文件,其中,name是生成的可执行文件的名称,source是创建可执行文件所需要的源文件。源文件可以使用aux_source_directory指令返回的变量(将源文件保存在这个变量中),也可以是指定的.cpp文件(注意路径)。
例如:add_executable(hello ${DIR_SRCS})使用aux_source_directory指令返回的变量DIR_SRCS,用变量内的源文件生成可执行文件,名称为hello.
指令10:target_link_libraries
基本格式:target_link_libraries(<target> [item1] [item2] [...] [[debug|optimized|general] <item>] ...)
说明:将目标文件与库文件进行链接,当编译项目时,可能依赖一些动态库,指令9是将目标与库文件链接起来。其中target是通过add_executable()和add_library()指令生成已经创建的目标文件。而[item]表示库文件(没有后缀)。
例如:target_link_libraries(myProject hello) 中target是myProject,链接libhello.so库。
3、使用set设置一些变量
第一种:set直接设置变量的值
例如:set(ROOTDIR /home/workspace) #给变量ROOTDIR赋值为/home/workspace,后面使用${ ROOTDIR }就表示/home/workspace这个路径。
第二种:设置编译选项
例如:set(CMAKE_BUILD_TYPE “Release”)#设置构建类型,“Release”表示用于构建的库或可执行文件,没有调试符号;也可设成“Debug”,表示生成的库或可执行文件包含调试符号,通常用于开发和调试期间。需要注意,这一设置需要在添加target之前。
例如:set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")#针对C++编译器设置编译选项,无默认值。本例中给出的指令表示C++编译器添加c++11的支持。
4、有了语法基础,如何编译一个简单的项目?
Step1:创建一个文件夹,名称为hello,包含如下两个文件:
hello.cpp是想要编译的文件,代码内容为:
#include<iostream>
using namespace std;
int main(){
std::cout << "hello world!!"<<std::endl;
}
接下来编写CMakeLists.txt编译该源文件,
cmake_minimum_required(VERSION 3.2)#设置cmake的最低版本,编译环境中的cmake要高于这个版本
project(hello)#设置项目名称
add_executable(hello hello.cpp)#生成可执行文件hello,使用的源文件是hello.cpp.
Step2:编译
mkdir build #在hello文件夹下创建build文件夹
cd build #进入到build文件夹
cmake .. #该命令会使用CMakeLists.txt生成Makefile文件
make -j #该命令会使用Makefile文件编译项目
step3:编译完成后,进入build文件夹,可以看到名为hello的可执行文件
执行如下命令:
./hello #运行生成的可执行文件
输出如下:
这样就完成了一个简单的例子。一些复杂的情况可参考上述指令进行添加。