生成器:
下面两种方法二选一
source code选择项目根目录,binaries选择项目根目录/build
,再点击左下方的Configure按钮,选择生成器MinGW Makefiles
,点击确定后,会产生下面的列表待填写,其中有三项基本的参数
Name | Value | 含义 |
---|---|---|
CMAKE_BUILD_TYPE | Release | 构建类型: Debug或Release |
CMAKE_GNUtoMS | 不勾选 | 是否同时生成适用于MSVC编译器的.lib库 |
CMAKE_INSTALL_PREFIX | 项目根目录/install | 构建后的可执行程序或库文件的安装目录,可自定义 |
填写完参数后,点击左下方的Generate按钮,会自动在项目根目录/build
中生成Makefile文件。
如果想要修改参数,需要点击左上角菜单栏的File: Delete Cache
删除缓存,再重复上面的操作。
# 进入到项目根目录后执行下面的命令
cmake -G "MinGW Makefiles" `
-B build `
-S . `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_GNUtoMS=false `
-DCMAKE_INSTALL_PREFIX="./install"
# -G: 选择生成器 ("MinGW Makefiles"或"Ninja")
# -B: 指定构建的目录
# -S: 指定源码目录 (CMakeLists.txt所在目录)
# -D: 定义CMake变量
cmake --build build
cmake --install build --prefix ./install
C++ Extension Pack
插件,这是一个套装,会附带另外2个插件。Ctrl Shift P
,输入C_Cpp: Intelli Sense Engine
,将它的值改为default
,确保这个插件的智能提示功能是开启的。Ctrl Shift P
,输入C++ Select IntelliSense Configuration
(中文名叫选择IntelliSense配置
),会弹出选项,选择使用CMake Tools
或者使用${workspaceFolder}/build/compile_commands.json
。cmake_minimum_required(VERSION 3.10.0)
project(project_demo)
# 设置 C++ 标准为 C++20
set(CMAKE_CXX_STANDARD 20)
# 强制要求编译器支持所选的 C++ 标准
set(CMAKE_CXX_STANDARD_REQUIRED ON)
让每次配置时,都重新生成compile_commands.json文件,防止智能提示失败。
set(CMAKE_EXPORT_COMPILE_COMMANDS True)
就是生成一个键值对,方便后面使用。
# 第一个参数是变量名,之后的多个参数是变量值(不支持通配符)。
# 用空格或;或换行隔开。
set(SRC_LIST main.cpp add.cpp div.cpp mult.cpp sub.cpp)
如果项目里的源文件很多,手动罗列出来太麻烦,就可以用此方法自动搜索一个目录下的所有文件。
# 第一个参数是待搜索的目录(不可以指定文件),第二个参数是把搜索到的结果赋值给一个变量。(相当于set的功能)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
# 第一个参数是GLOB或GLOB_RECURSE,第二个参数是变量名,第三个参数是待搜索的文件(可以用通配符)。
# GLOB_RECURSE表示递归搜索目录,会自动搜索每个子目录。
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp)
file(GLOB HEAD_LIST ${CMAKE_CURRENT_SOURCE_DIR}/include/*.cpp)
file(GLOB HEAD_LIST ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
为当前项目添加头文件所在的目录,以便编译器能找到头文件。
如果需要添加多个目录,要分多行写。
# ${PROJECT_SOURCE_DIR}表示当前项目的根目录
include_directories(${PROJECT_SOURCE_DIR}/include)
# ../表示当前CMakeLists.txt的上一层目录
include_directories("../../../src/calculate/include")
# 下面的方法表示绝对路径,注意Windows下要使用\\或者/
include_directories("C:\\c_extensions\\include\\googletest\\googlemock\\include")
# 库文件的全名分为三部分:lib+库名字+.a,只需要指定出库的名字就可以了,另外两部分会自动生成。
# 第二个参数表示:静态库STATIC、动态库SHARED。如果不写,则默认生成静态库。
add_library(math STATIC src/add.cpp src/sub.cpp)
add_library(math SHARED src/add.cpp src/sub.cpp)
对于生成的库文件来说和可执行程序一样都可以指定输出路径。由于在Linux下生成的动态库默认是有执行权限的,所以可以按照生成可执行程序的方式去指定它生成的目录。
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
其实就是通过set命令给EXECUTABLE_OUTPUT_PATH
宏设置了一个路径,这个路径就是可执行文件生成的路径。
由于在Linux下生成的静态库默认不具有可执行权限,所以在指定静态库生成的路径的时候就不能使用EXECUTABLE_OUTPUT_PATH宏了,而应该使用LIBRARY_OUTPUT_PATH,这个宏对应静态库文件和动态库文件都适用。
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 其中demo表示最后生成的可执行程序的名称(不需要与项目名称相同),即会生成demo.exe(如果是Linux,则没有.exe后缀)
# 后面可以添加任意多个源码文件,用空格或;或换行隔开。
add_executable(
demo
main.cpp
${SRC_FILES}
${TEST_SRCS}
)
# 如果中间有子目录,CMake工具会自动生成,无需自己手动创建。
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
如果是系统的静态、动态库,则不需要此方法。
如果是第三方库或自己制作的库,则需要手动指定静态、动态库所在的路径。
注意:搜索库的命令需要写在生成可执行程序命令的前面,否则会出现各种未定义函数的报错。
下面两种方法二选一即可。
link_directories(${PROJECT_SOURCE_DIR}/lib)
如果需要搜索多个库,要分多行写。
# 参数分别是:库名称、库文件名称、库文件所在的目录(可以是绝对路径或相对路径)
find_library(gtest libgtest.a "C:\\c_extensions\\include\\googletest\\build\\lib")
find_library(gmock libgmock.a "C:\\c_extensions\\include\\googletest\\build\\lib")
还有一种写法
find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED)
# 链接静态库(可以用短的库名,也可以用库文件全名)
link_libraries(add sub)
静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存。
注意:在CMake中指定要链接动态库的时候,应该将命令写到生成可执行程序之后。
target_link_libraries既可以链接动态库,也可以链接静态库。
target_link_libraries(
<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
# 第一个参数是可执行程序的名称,之后的参数是库名。
# pthread是可执行程序要加载的动态库,这个库是系统提供的线程库,全名为libpthread.so。
target_link_libraries(
calculate_test
pthread
gtest
gtest_main
gmock
gmock_main
)
如果搜索库目录中同时包含静态库和动态库,不论target_link_libraries命令中用的是简写库名、静态库全名、动态库全名,都是链接静态库。
日志类型:STATUS、WARNING、AUTHOR_WARNING、FATAL_ERROR、SEND_ERROR
(无) :重要消息
STATUS :非重要消息
WARNING :CMake 警告, 会继续执行
AUTHOR_WARNING :CMake 警告 (dev), 会继续执行
SEND_ERROR :CMake 错误, 继续执行,但是会跳过生成的步骤
FATAL_ERROR :CMake 错误, 终止所有处理过程
# 输出一般日志信息
(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")
有时候项目中的源文件并不一定都在同一个目录中,但是这些源文件最终却需要一起进行编译来生成最终的可执行文件或者库文件。如果我们通过file命令对各个目录下的源文件进行搜索,最后还需要做一个字符串拼接的操作,可以使用set命令也可以使用list命令。
# 把三个变量SRC1、SRC2、TEMP合到一起,赋值给SRC1,相当于SRC1被覆盖了。
set(SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
# APPEND表示追加
list(APPEND SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
# REMOVE_ITEM表示移除某个不需要的文件(之后的参数必须是路径,而不是文件名)
list(REMOVE_ITEM SRC_1 ${PROJECT_SOURCE_DIR}/main.cpp)
如果在源文件代码中定义了宏,以DEBUG宏举例:
#include <stdio.h>
int main()
{
#ifdef DEBUG
printf("我是一个程序猿, 我不会爬树...\n");
#endif
return 0;
}
在使用g++编译的时候,想要打印该宏下的代码,需要指定编译参数-D加宏的名称
g++ main.cpp -DDEBUG -o main
在使用CMake构建时,也可以在CMakeLists.txt中打印该宏下的代码
# 写在生成可执行程序的命令之前(参数-D和宏名称之间不要有空格)
add_definitions(-DDEBUG)
宏 | 功能 |
---|---|
PROJECT_SOURCE_DIR | 使用cmake命令后紧跟的目录,一般是工程的根目录 |
PROJECT_BINARY_DIR | 执行cmake命令的目录 |
CMAKE_CURRENT_SOURCE_DIR | 当前处理的CMakeLists.txt所在的路径 |
CMAKE_CURRENT_BINARY_DIR | target 编译目录 |
EXECUTABLE_OUTPUT_PATH | 重新定义目标二进制可执行文件的存放位置 |
LIBRARY_OUTPUT_PATH | 重新定义目标链接库文件的存放位置 |
PROJECT_NAME | 返回通过PROJECT指令定义的项目名称 |
CMAKE_BINARY_DIR | 项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径 |
如果项目很大,或者项目中有很多的源码目录,在通过CMake管理项目的时候如果只使用一个CMakeLists.txt,那么这个文件相对会比较复杂,有一种化繁为简的方式就是给每个源码目录都添加一个CMakeLists.txt文件(头文件目录不需要),这样每个文件都不会太复杂,而且更灵活,更容易维护。
项目根目录下的就是根节点。
建立各个CMake文件之间的联系
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
在 CMake 的 CMakeLists.txt 中也可以进行流程控制,也就是说可以像写 shell 脚本那样进行条件判断和循环。
if(condition)
...
elseif(condition)
...
else()
...
endif()
condition条件有以下符号
foreach(item RANGE 10)
(STATUS "当前遍历的值为: ${item}" )
endforeach()
foreach(item RANGE 10 30 2)
(STATUS "当前遍历的值为: ${item}" )
endforeach()
# 创建 list
set(WORD a b c d)
set(NAME ace sabo luffy)
# 遍历 list
foreach(item IN LISTS WORD NAME)
(STATUS "当前遍历的值为: ${item}" )
endforeach()
# 创建一个列表 NAME
set(NAME luffy sanji zoro nami robin)
# 得到列表长度
list(LENGTH NAME LEN)
# 循环
while(${LEN} GREATER 0)
(STATUS "names = ${NAME}")
# 弹出列表头部元素
list(POP_FRONT NAME)
# 更新列表长度
list(LENGTH NAME LEN)
endwhile()
设置调试目标:按Ctrl Shift P
,输入CMAke: Set Debug Target
,选择对应的目标。
按F7
构建生成目标。(要先有可执行程序,才能进行调试)
按Ctrl F5
打开调试。或者点击左下角CMake插件的调试按钮。
打开CMake (cmake-gui)
软件。
在Where is the source code
中选择或填入源码路径。
在Where to build the binaries
中填入源码路径加/build
,cmake会自动生产build文件夹。
点击Configure
按钮,选择第一项Use default native compilers
,并选择MinGW Makefiles
,会生成一系列配置选项,(并且可能会有报错,不用管,因为这里只是读取CMakeLists.txt
中的配置项),然后可以取消勾选一些不需要的选项。
点击Generate
按钮,会在build文件夹下生成Makefiles
文件。
打开终端,进入到源码文件夹,输入cmake --build build
,即真正开始构建。
构建完成后,生成的动态库或静态库或可执行文件在build
文件夹下。
如果中间失败了,想要重新生成配置选项,可以点击左上角的File
菜单,再点击Delete Cache
。