CMake编写学习笔记
在学习c++的过程中,不可避免的会使用到cmake
。cmake
的亮点在于编译复杂项目上的应用,是一个跨平台的Makefile生成工具。那么在一个项目的建构中,你是否读得懂别人写的cmake
文件呢?你要在别人的cmake
文件基础上加上自己的内容又要如何编写cmake
文件呢?
因此,学习自己手动编写cmake
文件是一个不得不进行的一个工作。下面是参考一些博客自己学习的笔记:
参考博客如下:
1. CMake编写最简单的模板
#设置cmake最小的版本要求
cmake_minimum_required(VERSION 3.10)
#设置项目名
project(project_name)
#添加可执行文件(第一个参数是生成的可执行文件的名字,第二个参数是源文件)
add_executable(project_name xxx.cpp)
需要注意的是,add_executable
里面的cpp
可能会包含其他cpp
文件,可以在后面依次添加
最简单的示例
步骤
-
在任意目录下创建一个新文件夹,写一段
c++
代码,例如在桌面创建test
文件夹,在里面创建一个cpp
文件cd ~/Desktop mkdir test cd test gedit test.cpp
在里面输入如下内容
#include<iostream> using namespace std; int main(int argc,char* argv[]){ cout <<"这是最简单的cmake测试示例。。。。"<< endl; return 0; }
然后保存就可以了。
-
编写
CMakeLists.txt
文件gedit CMakeLists.txt
然后在
gedit
里输入如下内容:#设置cmake最小的版本要求 cmake_minimum_required(VERSION 3.10) #设置项目名 project(test) #添加可执行文件(第一个参数是生成的可执行文件的名字,第二个参数是源文件) add_executable(test test.cpp)
-
执行
cmake
命令一般都会将
cmake
后的文件都单独放在一个文件夹下,习惯性是build
。因此可以执行下列命令#创建build文件夹 mkdir build #进入build目录下 cd build #执行cmake命令,".."表示的是上一级目录,cmake会自动在上一级目录中查找CMakeLists.txt文件,并执行 cmake ..
执行结果会在
build
文件夹下生成几个文件,可以通过下列命令查看ls #结果如下,生成了如下文件 CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
4.执行生成的Makefile文件
#make会自动寻找当前目录下的Makefile文件 make
结果为生成了如下文件,和上面相比多了test这个可执行文件:
CMakeCache.txt CMakeFiles cmake_install.cmake test Makefile
5.执行test文件
./test
输出结果为:
这是最简单的cmake测试示例。。。。
到这里这个最简单的示例就结束了,之后更复杂的示例的步骤也和这个大同小异。
2. 包含目录结构的项目
2.1 包含头文件和源文件
-
创建一个工程目录,例如:
CMakeTest2
-
在该目录下创建如下目录结构的文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMr9gMTI-1578209038124)(/home/szq/Pictures/2020-01-03 10-27-11屏幕截图.png)]
其中
Student.h
的内容为:// // Created by szq on 2020/1/3. // #ifndef CMAKETEST2_STUDENT_H #define CMAKETEST2_STUDENT_H #include "iostream" using namespace std; class Student{ private: string name;//姓名 string sno;//学号 string gender;//性别 string major;//专业 public: Student(); Student(string name = "",string sno = "",string gender= "",string major = ""); //获取学生姓名 string getName(); //获取学生学号 string getSno(); //获取学生性别 string getGender(); //获取学生专业 string getMajor(); //设置学生姓名 void setName(string name); //设置学生学号 void setSno(string sno); //设置学生性别 void setGender(string gender); //设置学生专业 void setMajor(string major); }; #endif //CMAKETEST2_STUDENT_H
Student.cpp
的内容为:// // Created by szq on 2020/1/3. // #include "Student.h" Student::Student() { } Student::Student(string name, string sno, string gender, string major) { setName(name); setSno(sno); setGender(gender); setMajor(major); } std::string Student::getGender() { return this->gender; } std::string Student::getName() { return this->name; } std::string Student::getSno() { return this->sno; } std::string Student::getMajor() { return this->major; } void Student::setName(string name) { this->name = name; } void Student::setSno(string sno) { this->sno = sno; } void Student::setGender(string gender) { this->gender = gender; } void Student::setMajor(string major) { this->major = major; }
main.cpp
中的内容为:#include <iostream> #include "Student.h" using namespace std; int main() { Student *stu = new Student("szq","M201973343","男","计算机技术"); cout << "学生姓名为:" << stu->getName() << endl; cout << "学生学号为:" << stu->getSno() << endl; cout << "学生性别为:" << stu->getGender() << endl; cout << "学生专业为:" << stu->getMajor() << endl; delete stu; return 0; }
3.编写
CMakeLists.txt
文件,内容如下(注意看注释,比较和上一个CMakeLists
的区别):#设置cmake的最小的版本要求 cmake_minimum_required(VERSION 3.15) #设置工程名,这里和文件夹同名 project(CMakeTest2) #使用include_directories命令来添加包含的头文件目录 include_directories(include) #使用set来设置一个变量SOURCES,变量的值为空格后面的值 #set(SOURCES src/main.cpp src/Student.cpp) #这里也是设置一个变量SOURCES,和上面set的效果一样 file(GLOB SOURCES "src/*.cpp") #添加可执行文件,第二个参数这里使用的是变量,相当于src/main.cpp src/Student.cpp add_executable(CMakeTest2 ${SOURCES})
4.执行
cmake
mkdir build cd build cmake ..
5.执行生成的
Makefile
文件make
6.执行可执行文件
./CMakeTest2
执行后的结果为:
学生姓名为:szq 学生学号为:M201973343 学生性别为:男 学生专业为:计算机技术
2.2 动态库编译
步骤1、2和上面2.1中1、2差不多,只是不用创建main.cpp文件
- 编写
CMakeLists.txt
文件,这里没有使用add_executable(),而是使用add_library()
#设置cmake最小的版本要求
cmake_minimum_required(VERSION 3.10.2)
#设置工程名,这里设置为与文件夹同名
project(CMakeTest3)
#设置cmake的build类型
set(CMAKE_BUILD_TYPE Release)
#添加包含头文件目录
include_directories(include)
#设置变量SOURCES
file(GLOB SOURCES "src/*.cpp")
#通过SOURCES中的源文件生成共享库
add_library(CMakeTest3 SHARED ${SOURCES})
#install指定安装目录,执行sudo apt-get install时动态库将被安装在 /usr/lib目录
install(TARGETS CMakeTest3 DESTINATION /usr/lib)
4.执行cmake
命令
mkdir build
cd build
cmake ..
5.执行Makefile
文件
make
这里生成的文件有所不同,没有生成可执行文件,而是生成了一个动态库。如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KCVM3L0S-1578209038125)(/home/szq/Pictures/2020-01-03 11-09-37屏幕截图.png)]
2.3 静态库编译
步骤1、2和上面2.1中1、2差不多,只是不用创建main.cpp文件
- 编写
CMakeLists
文件,编译静态库只需将add_library中参数SHARED换成STATIC即可:
#设置cmake最小的版本要求
cmake_minimum_required(VERSION 3.10.2)
#设置工程名,这里设置为和文件夹同名
project(CMakeTest4)
#设置cmake的build类型
set(CMAKE_BUILD_TYPE Release)
#添加包含头文件目录
include_directories(include)
#设置变量SOURCES
file(GLOB SOURCES "src/*.cpp")
#通过SOURCES中的源文件生成静态库
add_library(CMakeTest4 STATIC ${SOURCES})
#install指定安装目录,执行sudo apt-get install时,静态库将被安装到/usr/lib目录下
install(TARGETS CMakeTest4 DESTINATION /usr/bin)
- 执行
cmake
指令
mkdir build
cd build
cmake ..
- 执行Makefile文件
make
执行后的结果和上面生成的文件结构有所不同,上面的.so文件变成了.a文件,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4lN2Ov0P-1578209038125)(/home/szq/Pictures/2020-01-03 11-29-51屏幕截图.png)]
2.4 使用静态库或动态库
步骤1、2和上面2.1中1、2差不多,只是不用创建Student.h
和Student.cpp
文件,而是创建main.cpp
main.cpp
内容为:
#include <iostream>
#include "Student.h"
using namespace std;
int main() {
Student *stu = new Student("szq","M201973343","男","计算机技术");
cout << "学生姓名为:" << stu->getName() << endl;
cout << "学生学号为:" << stu->getSno() << endl;
cout << "学生性别为:" << stu->getGender() << endl;
cout << "学生专业为:" << stu->getMajor() << endl;
delete stu;
return 0;
}
- 编写
CMakeLists
文件
#设置cmake最低版本要求
cmake_minimum_required(VERSION 3.10.2)
#设置工程名
project(CMakeTest5)
#设置PROJECT_LINK_LIBS变量,值为libCMakeTest3.so
set(PROJECT_LINK_LIBS libCMakeTest3.so)
#设置链接的动态库文件目录
link_directories(/home/szq/CLionProjects/CMakeTest3/build/)
#设置包含的头文件目录
include_directories(/home/szq/CLionProjects/CMakeTest3/include)
#添加可执行文件
add_executable(CMakeTest5 main.cpp)
#设置要链接的动态库
target_link_libraries(CMakeTest5 ${PROJECT_LINK_LIBS})
上面使用的是动态库,如果要使用2.3生成的静态库,只需将PROJECT_LINK_LIBS设置为libCMakeTest4.a即可
4.执行cmake
指令
mkdir build
cd build
cmake ..
5.执行Makefile
文件
make
执行后生成了可执行文件
6.执行可执行文件
./CMakeTest5
结果为:
学生姓名为:szq
学生学号为:M201973343
学生性别为:男
学生专业为:计算机技术
上面内容说明执行成功了
3. CMake中一些预定义变量
变量 | 说明 |
---|---|
PROJECT_SOURCE_DIR | 工程的根目录 |
PROJECT_BINARY_DIR | 运行cmake 命令的目录,通常是${PROJECT_SOURCE_DIR}/build |
CMAKE_INCLUDE_PATH | 环境变量,并非cmake 变量 |
CMAKE_LIBRARY_PATH | 环境变量 |
CMAKE_CURRENT_BINARY_DIR | target 编译目录,使用ADD_SURDIRECTORY(src bin) 可以更改此变量的值 |
CMAKE_CURRENT_SOURCE_DIR | 当前处理的CMakeLists.txt 所在的路径 |
CMAKE_CURRENT_LIST_FILE | 输出调用这个变量的CMakeLists.txt 的完整路径 |
CMAKE_CURRENT_LIST_LINE | 输出这个变量所在的行 |
CMAKE_MODULE_PATH | 定义自己的cmake 模块所在的路径 SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE 命令来调用自己的模块 |
EXECUTABLE_OUTPUT_PATH | 重新定义目标二进制可执行文件的存放位置 |
LIBRARY_OUTPUT_PATH | 重新定义目标链接库文件的存放位置 |
PROJECT_NAME | 返回通过PROJECT 指令定义的项目名称 |
CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS | 用来控制IF ELSE 语句的书写方式 |
CMAKE_MAJOR_VERSION | cmake 主版本号,如2.8.6中的2 |
CMAKE_MINOR_VERSION | cmake 次版本号,如2.8.6中的8 |
CMAKE_PATCH_VERSION | cmake 补丁等级,如2.8.6中的6 |
CMAKE_SYSTEM | 系统名称,例如Linux-2.6.22 |
CAMKE_SYSTEM_NAME | 不包含版本的系统名,如Linux |
CMAKE_SYSTEM_VERSION | 系统版本,如2.6.22 |
CMAKE_SYSTEM_PROCESSOR | 处理器名称,如i686 |
BUILD_SHARED_LIBS | 控制默认的库编译方式。如果未进行设置,使用ADD_LIBRARY 时又没有指定库类型,默认编译生成的库都是静态库 |
CMAKE_C_FLAGS | 设置C 编译选项 |
CMAKE_CXX_FLAGS | 设置C++ 编译选项 |
4.CMake常用命令
命令 | 语法 | 说明 |
---|---|---|
PROJECT | PROJECT(projectname[CXX][C][JAVA]) | 指定工程名称,并可指定工程支持的语言。支持语言列表可忽略,默认支持所有语言。 |
SET | SET(VAR[VALUE][CACHE TYPE DOCSTRING[FORCE]]) | 定义变量(可以定义多个VALUE ,如SET(SRC_LIST main.c util.c reactor.c) ) |
MESSAGE | MESSAGE([SEND_ERROR|STATUS|FATAL_ERROR] “message to display” …) | 向终端输出用户定义的信息或变量的值。SEND_ERROR ,产生错误,生成过程被跳过;STATUS ,输出前缀为1的信息;FATAL_ERROR ,立即终止所有cmake 过程 |
ADD_EXECUTABLE | ADD_EXECUTABLE(bin_file_name ${SRC_LIST}) | 生成可执行文件 |
ADD_LIBRARY | ADD_LIBRARY(libname [SHARED|STATIC|MODULE][EXCLUDE_FROM_ALL] SRC_LIST) | 生成动态库或静态库;SHARED 动态库;STATIC 静态库;MODULE 在使用dyld 的系统有效。若不支持dyld ,等同于SHARED ;EXCLUDE_FROM_ALL表示该库不会被默认构建 |
SET_TARGET_PROPERTIES | … | 设置输出的名称,设置动态库的版本和API 版本 |
CMAKE_MINIMUM_REQUIRED | CMAKE_MINIMUM_REQUIRED(VERSION version_number [FATAL_ERROR]) | 声明CMake 的版本要求 |
ADD_SUBDIRECTORY | ADD_SUBDIRECTORY(src_dir [binary_dir] [EXCLUDE_FROM_ALL]) | 向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置EXCLUDE_FROM_ALL 含义:将这个目录从编译过程中排除 |
INCLUDE_DIRECTORIES | INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 … ) | 向工程添加多个特定的头文件搜索路径,路径之间用空格分隔,如果路径包含空格,可以使用双引号将它括起来,默认的行为为追加到当前头文件搜索路径的后面。 |
LINK_DIRECTORIES | LINK_DIRECTORIES(dir1 dir2 …) | 添加非标准的共享库搜索路径 |
TARGET_LINK_LIBRARIES | TARGET_LINK_LIBRARIES(target lib1 lib2 …) | 为target 添加需要链接的共享库 |
ADD_DEFINITIONS | ADD_DEFINITIONS(-DENABLE_DEBUG -DABC) | 向C/C++ 编译器添加-D 定义,参数之间用空格分割 |
ADD_DEPENDENCIES | ADD_DEPENDENCIES(target-name depend-target1 depend-target2 …) | 定义target 依赖的其他target ,确保target 在构建之前,其依赖的target 已经构建完毕 |
AUX_SOURCE_DIRECTORY | AUX_SOURCE_DIRECTORY(dir VAR) | 发现一个目录下所有的源代码文件并将列表存储在一个变量中把当前目录下的所有源码文件名赋给变量DIR_HELLO_SRCS |
EXEC_PROGRAM | EXEC_PROGRAM(Executable [dir where to run] [ARGS <args>][OUTPUT_VARIABLE ] [RETURN_VALUE <value>]) | 用于在指定目录运行某个程序(默认为当前CMakeLists.txt 所在目录),通过ARGS 添加参数,通过OUTPUT_VARIABLE 和RETURN_VALUE 获取输出和返回值 |
INCLUDE | INCLUDE(file [OPTIONAL])/INCLUDE(module [OPTIONAL]) | 用来载入CMakeLists.txt 文件/用来载入预定义的cmake 模块;OPTIONAL 参数的左右是文件不存在也不会产生错误,可以载入一个文件,也可以载入预定义模块(模块会在CMAKE_MODULE_PATH 指定的路径进行搜索),载入的内容将在处理到INCLUDE 语句时直接执行 |