CMake初步学习笔记
CMake是什么
就个人理解,CMake
是一个自动化的建构系统,他通过执行CMakeLists.txt
中的一系列脚本,来针对指定项目生成一系列构建文件。然后我们就可以使用构建文件来编译并构建项目。
为什么使用CMake
就我目前学习到的知识而言,CMake具有以下优势
- CMake可以方便我指定用于构建项目的资源与依赖(扫描目录,自动检测);
- CMake是跨平台的,它生成的构建文件(MakeFile)可以运用于在多个不同的平台上编译项目;
最小化的构建脚本
CMake1
中的项目完成了一个A+B程序,以下是CMakeLists.txt
中的内容。
1 | cmake_minimum_required(VERSION 3.10) |
cmake_minimum_required(VERSION 3.10)
指定了最小的CMake
版本,最好在3.10
及以上,不然会有警告(貌似是旧版的一些特性不被支持了)。set(CMAKE_C_COMPILER "C:/MinGW/bin/gcc.exe")
和set(CMAKE_CXX_COMPILER "C:/MinGW/bin/gcc.exe")
指定了C
文件和C++
文件的编译器(我用的是MinGW,以此为例)。set(VAR_NAME VALUE)
语句是CMake
脚本中的变量设置语句。project(CMakeAPlusB)
指定了项目名称。add_executable(a-plus-b a-plus-b.c)
指定项目生成可执行文件的构建文件。
执行以下命令行指令来开始编译
1 | cmake -B D:\Projects\cmake-summary\CMake1\ -G "MinGW Makefiles" |
其中-B
指定编译目录,-G
指定使用的编译器,CMake GUI
也可以完成相应的任务。
执行脚本后,在该目录中会生成MakeFile
构建文件,在目录下执行如下命令行指令
1 | make |
即可编译MakeFile
文件,生成a-plus-b.exe
。
CMake2
中的项目完成了一个A+B脚本,并把它编译为了静态库,脚本大意同上。
1 | cmake_minimum_required(VERSION 3.10) |
之后执行的命令行指令与上方类似,最后会生成a-plus-b.dll
与a-plus-b.dll.a
,前者为动态库文件,后者为静态库文件。若只想要后者的话,将SHARED
改为STATIC
即可。
静态库相较于动态库而言,被引用时会被全部加载,占用资源大但调用内容时效率有所上升;动态库是被引用后每次调用会动态地在资源中搜索,占用资源相对少而调用效率有所下降。
默认配置目录
以上方CMake1
的构建过程为例:
1 | cmake -B D:\Projects\cmake-summary\CMake1\ -G "MinGW Makefiles" |
使用-B
指定编译目录,CMake
会检测目录下是否存在CMakeLists.txt
文件,如果有,就会开始生成构建文件。
默认构建目录
CMake3
的文件夹中,生成的构建文件存放在./build
目录下,这是因为我执行了如下指令:
1 | cmake -S D:\Projects\cmake-summary\CMake3\ -G "MinGW Makefiles" -B "D:\Projects\cmake-summary\CMake3\build\" |
用-S
指定源文件CMakeLists.txt
所在目录,-B
指定生成文件所在的目录,就可以指定将文件构建在相应位置。
此外,CMakeLists.txt
中多了这么一行:
1 | cmake_minimum_required(VERSION 3.10) |
用set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build/)
可以指定可执行文件的生成位置,这里也是在./build
目录下。
指定编译后端
1 | cmake --G |
如上所示,执行cmake -G
后,会展示所有可用的编译器列表,使用-G
命令指定需要的编译后端即可,例如下面的指令:
1 | cmake -S D:\Projects\cmake-summary\CMake3\ -G "MinGW Makefiles" -B "D:\Projects\cmake-summary\CMake3\build\" |
设置变量并使用
正如上方所提到的,set(VAR_NAME VALUE)
语句是CMake
脚本中的变量设置语句,那么以下语句等价:
1 | set(CMAKE_C_COMPILER "C:/MinGW/bin/gcc.exe") |
1 | set(MINGW_COMPILER "C:/MinGW/bin") |
要引用变量,使用${VAR_NAME}
的格式即可。
CMake –build 与 –target
正如上方所提到的,对于生成的MakeFile
文件,用make
命令可以生成相关的可执行文件或库。实际上,如果我们使用Ninja
或Visual Studio
等其它编译器生成构建文件,得到的就会是.ninja
或.sln
构建文件,此时就必须使用xcodebuild
或msbuild
等其它的命令进行编译。如果我们还要配置生成参数的话,不同的生成命令配置方式还不一样。
为了方便跨平台,所以,我们可以用以下命令来代替make
1 | cmake --build . |
如果我们使用的是MakeFile
,此命令相当于在本目录.
执行了make
。
我们还可以用--target
来指定生成目标:
1 | cmake --build . --target help |
在target
后加入参数,来构建指定的目标,如果不指定,就全部构建。
清除构建目录并重新构建
根据个人的一些实践,当我们将一个已经完成了构建并顺利生成了CMakeCache.txt
的项目文件从一个目录移动到另外一个目录的时候,我们必须将CMakeCache.txt
删除,并重新使用cmake
命令构建MakeFile
,否则在执行相关命令时会提示目录不符合。
常用内置变量
引用内置变量的格式和引用自定义变量的没有区别,都是${VAR_NAME}
。
我自己用过的内置变量:
${CMAKE_CURRENT_SOURCE_DIR}
CMakeLists.txt所在的根目录${CMAKE_C_COMPILER}
C编译器位置(不能设置成CPP的!)${CMAKE_CXX_COMPILER}
CPP编译器位置(不能设置成C的!)${EXECUTABLE_OUTPUT_PATH}
输出可执行文件的位置${LIBRARY_OUTPUT_PATH}
输出库的位置
有篇写这个的博客:https://www.cnblogs.com/Braveliu/p/15827857.html,我觉得这个总结的挺全面。
资源管理
参见CMake4
项目,可以使用一些指令来包含资源文件与头文件,并在项目中引用。
1 | cmake_minimum_required(VERSION 3.10) |
使用include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
可以将指定目录下的所有文件作为头文件添加到项目中,这样就可以使用include
引用相应的文件。
使用file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
可以按照格式扫描指定目录,将所有符合格式的文件存放到数组变量中。
add_executable()
可以一次性添加多个文件,只需要把多个变量写在参数里就行。