Cmake使用

  • 介绍
  • 安装
  • Cmake命令
  • 预定义变量
  • 指令
  • if 用法
  • 逻辑运算
  • foreach 用法
  • 特殊语句
  • 举个栗子
  • 一级CMakeLists.txt内容
  • 二级CMakeLists.txt内容
  • 子文件src.cmake内容
  • Demo项目链接下载
  • 参考网址


介绍

CMake是解决软件跨平台开发,保证code在不同平台编译的高级编译配置工具。
它允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,再根据目标用户的平台生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。

在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:
1. 编写 CMake 配置文件 CMakeLists.txt 。
2. 执行命令 cmake PATH 或者 ccmake PATH 生成 Makefile 1 1ccmake 和 cmake 的区别在于前者提供了一个交互式的界面。其中, PATH 是 CMakeLists.txt 所在的目录。
3. 使用 make 命令进行编译。

安装

Cmake下载地址 :https://cmake.org/download/

windows下载安装包即可,本文以Windows下cmake为例。
Windows x64 Installer:cmake-3.20.0-rc3-windows-x86_64.msi

Cmake的官方教程及其网址(非常齐全,如果你不想阅读官方,请继续看本文的简单说明)

  1. Cmake Tutorial 网址https://cmake.org/cmake/help/latest/guide/tutorial/index.html
  2. Cmake source codehttps://gitlab.kitware.com/cmake/cmake
  3. CMake Wiki https://gitlab.kitware.com/cmake/community

Cmake命令

Cmake管理编译项目主要通过CMakeLists.txt文件,最简单的项目仅仅需要3行,注意文件中的命令习惯使用小写

# 指定cmake当前支持的最低版本,也可指定最高版本==》cmake_minimum_required(VERSION 3.10...3.20)
# 这条命令需放在首位,不然可能影响后续命令的正确执行
cmake_minimum_required(VERSION 3.10)

# set the project name
# 同样可增加项目版本号==》project(Tutorial VERSION 1.0)
project(Tutorial)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# add the executable
add_executable(Tutorial tutorial.cxx)

预定义变量

CMAKE_CXX_COMPILER			# 指定c++编译器
CMAKE_CXX_FLAGS				# c++编译器编译选项
PROJECT_NAME				# 通过 project() 指定项目名称
PROJECT_SOURCE_DIR			# 工程根目录
PROJECT_BINARARY_DIR		# 运行cmake命令的目录,通常是${PROJECT_SOURCE_DIR}/build
EXECUTABLE_OUTPUT_PATH		# 重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH 		# 重新定义目标链接库文件的存放位置
CMAKE_MODULE_PATH			# 项目的外部模块的查找路径,供include()或 find_package()使用。初始为空,由用户设定
CMAKE_PREFIX_PATH			# 项目的外部模块的查找路径,供find_package(), find_program(), find_library(), find_file()和find_path(),初始为空,由用户设定
CMAKE_CURRENT_SOURCE_DIR	# 当前的CMakeList.txt所在的路径
CMAKE_CURRENT_BINARY_DIR	# 编译目录,可使用add_subdirectory来修改
CMAKE_GENERATOR				# 编译器名称
CMAKE_COMMAND				# 可执行文件本身的全路径

上述为部分常用变量,更多参考官网

指令

# set 指令的三种情景:
set(<variable> <value>... [PARENT_SCOPE])	#设置普通变量
set(<variable> <value>... CACHE <type> <docstring> [FORCE])	 #设置缓存条目
set(ENV{<variable>} [<value>])	 #设置环境变量
# 参考网址(https://www.jianshu.com/p/c2c71d5a09e9)

# message指令相当于cout:
message("1.PROJECT_BINARY_DIR = ${PROJECT_BINARY_DIR}")

# option指令:让你可以根据选项值进行条件编译
option(TEST_DEBUG "option for debug" OFF)
if (TEST_DEBUG)
add_definitions(-DTEST_DEBUG)
endif(TEST_DEBUG)

# add_definitions:为源文件的编译添加由-D定义的标志,可直接在code中使用,常用于预定义宏。
code中的使用:
#ifdef DTEST_DEBUG
...
#else 
...
#endif

# add_executable:利用源文件 main.cpp,编译出名为 hello 的可执行文件
set(src main.cpp
		hello.cpp
		util/Const.cpp
		util/Globals.cc)
add_executable(hello ${src})

# include_directories:添加头文件目录
include_directories(${BOOST_PATH})

# link_directories:添加链接的库文件目录
link_directories(directory1 directory2 …)
link_directories(${BOOST_PATH}/lib64-msvc-14.0)

# add_subdirectory与subdirs:添加一个子目录并构建该子目录
add_subdirectory(sub output) 
subdirs(workstation)

# configure_file:拷贝文件
configure_file(<input> <output>
               [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
               [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
configure_files("${opencv_bin}/debug" "${PROJECT_OUTPUT_DIR}/debug")
configure_files("${opencv_bin}/release" "${PROJECT_OUTPUT_DIR}/release")  

# file:功能很多,近列举常用
file(GLOB LUA_FILE_LIST "scripts/*.lua") #查找路径下.lua文件并构成一个文件列表变量
file(COPY ${LUA_FILE_LIST} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/scripts") #复制lua文件到构建目录的scripts/

# target_link_libraries:设置要链接的库文件的名称
target_link_libraries(${proj_name} algorithm 
rpcrt4
ws2_32
netapi32
wsock32
opengl32.lib
glu32.lib
debug ${glew_lib}/glew32d.lib
optimized ${glew_lib}/glew32.lib
debug ${ftgl_lib}/ftgl_static_D.lib
optimized ${ftgl_lib}/ftgl_static.lib
debug ${free_type_lib}/freetyped.lib
optimized ${free_type_lib}/freetype.lib)

# set_target_properties:设置目标的一些属性来改变它们构建的方式
set_target_properties(${proj_name} PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")# 设置项目release以应用程序启动
set_target_properties(${proj_name} PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")# 设置项目debug以应用程序启动
set_target_properties(${proj_name} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/../output/$(Configuration)")# 设置项目输出路径

# set_property:给定的作用域内设置一个命名的属性
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})# 设置系统的启动项

# find_package:查找第三方库
find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIR})
target_link_libraries(curltest ${CURL_LIBRARY})

if 用法

# 对项目进行有条件的编译
if(WIN32)
	message(STATUS "Now is windows")
elseif(APPLE)
	message(STATUS "Now is Apple systens.")
elseif(UNIX)
	message(STATUS "Now is UNIX-like OS's.")
endif()

逻辑运算

# 取反运算
if(NOT <condition>)

# 与运算
if(<cond1> AND <cond2>)

# 或运算
if(<cond1> OR <cond2>)

foreach 用法

# 将当前路径下的所有源文件列表赋值给变量SRC_LIST,然后遍历SRC_LIST中的文件,并持续输出源文件的名称。
aux_source_directory(. SRC_LIST)
foreach(F ${SRC_LIST})
     message(${F})
endforeach(F)
# while循环
while(condition)
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  ...
endwhile(condition)

特殊语句

if(COMMAND command-name)
# 如果给定名称是可以调用的命令,宏或函数,则为true。

if(POLICY policy-id)
# 如果给定名称是现有策略(格式为CMP<NNNN>),则为True 。

if(TARGET target-name)
# 如果给定名称是通过调用调用创建的现有逻辑目标名称,则为True。 add_executable(), add_library(), 要么 add_custom_target() 已经被调用的命令(在任何目录中)。

if(TEST test-name)
# 如果给定名称是由测试人员创建的现有测试名称,则为True add_test() 命令。

if(EXISTS path-to-file-or-directory)
# 如果指定的文件或目录存在,则为True。仅针对完整路径定义行为。解析符号链接,即,如果命名的文件或目录是符号链接,则如果符号链接的目标存在,则返回true。

if(file1 IS_NEWER_THAN file2)
# 如果file1是更新版本file2或两个文件之一不存在,则为true 。仅针对完整路径定义行为。如果文件时间戳完全相同,则IS_NEWER_THAN比较将返回true,这样,在出现平局的情况下将发生任何相关的生成操作。这包括为file1和file2传递相同文件名的情况。

if(IS_DIRECTORY path-to-directory)
# 如果给定名称是目录,则为True。仅针对完整路径定义行为。

if(IS_SYMLINK file-name)
# 如果给定名称是符号链接,则为True。仅针对完整路径定义行为。

if(IS_ABSOLUTE path)
# 如果给定路径是绝对路径,则为True。

if(<variable|string> MATCHES regex)
# 如果给定的字符串或变量的值与给定的常规条件匹配,则为true。有关正则表达式格式,请参见正则表达式规范。 ()组被捕获在CMAKE_MATCH_<n> 变量。

if(<variable|string> LESS <variable|string>)
# 如果给定的字符串或变量的值是有效数字且小于右边的数字,则为true。

if(<variable|string> GREATER <variable|string>)
# 如果给定的字符串或变量的值是有效数字并且大于右侧的数字,则为true。

if(<variable|string> EQUAL <variable|string>)
# 如果给定的字符串或变量的值是有效数字并且等于右侧的数字,则为true。

if(<variable|string> LESS_EQUAL <variable|string>)
# 如果给定的字符串或变量的值是有效数字且小于或等于右侧的数字,则为true。

if(<variable|string> GREATER_EQUAL <variable|string>)
# 如果给定的字符串或变量的值是有效数字并且大于或等于右侧的数字,则为true。

if(<variable|string> STRLESS <variable|string>)
# 如果给定的字符串或变量的值在字典上小于右侧的字符串或变量,则为true。

if(<variable|string> STRGREATER <variable|string>)
# 如果给定的字符串或变量的值在字典上大于右侧的字符串或变量,则为true。

if(<variable|string> STREQUAL <variable|string>)
# 如果给定的字符串或变量的值在字典上等于右侧的字符串或变量,则为true。

if(<variable|string> STRLESS_EQUAL <variable|string>)
# 如果给定的字符串或变量的值在字典上小于或等于右侧的字符串或变量,则为true。

if(<variable|string> STRGREATER_EQUAL <variable|string>)
# 如果给定的字符串或变量的值在字典上大于或等于右侧的字符串或变量,则为true。

if(<variable|string> VERSION_LESS <variable|string>)
# 按组件的整数版本号比较(版本格式为 major[.minor[.patch[.tweak]]],省略的组件视为零)。任何非整数版本组件或版本组件的非整数结尾部分均会在该点处有效截断字符串。

if(<variable|string> VERSION_GREATER <variable|string>)
# 按组件的整数版本号比较(版本格式为 major[.minor[.patch[.tweak]]],省略的组件视为零)。任何非整数版本组件或版本组件的非整数结尾部分均会在该点处有效截断字符串。

if(<variable|string> VERSION_EQUAL <variable|string>)
# 按组件的整数版本号比较(版本格式为 major[.minor[.patch[.tweak]]],省略的组件视为零)。任何非整数版本组件或版本组件的非整数结尾部分均会在该点处有效截断字符串。

if(<variable|string> VERSION_LESS_EQUAL <variable|string>)
# 按组件的整数版本号比较(版本格式为 major[.minor[.patch[.tweak]]],省略的组件视为零)。任何非整数版本组件或版本组件的非整数结尾部分均会在该点处有效截断字符串。

if(<variable|string> VERSION_GREATER_EQUAL <variable|string>)
# 按组件的整数版本号比较(版本格式为 major[.minor[.patch[.tweak]]],省略的组件视为零)。任何非整数版本组件或版本组件的非整数结尾部分均会在该点处有效截断字符串。

if(<variable|string> IN_LIST <variable>)
# 如果给定元素包含在命名列表变量中,则为true。

if(DEFINED <name>|CACHE{<name>}|ENV{<name>})
# 如果<name>定义了具有给定的变量,缓存变量或环境变量,则为true 。变量的值无关紧要。请注意,宏参数不是变量。

if((condition) AND (condition OR (condition)))
# 首先评估括号内的条件,然后像前面的示例一样评估其余条件。如果有嵌套的括号,则将最里面的括号作为包含它们的条件的一部分进行评估。

举个栗子

如果你想快速上手cmake的相关知识可以直接跳到本章节,帮助你快速理解项目组织结构,请看本章示例

一个完整的项目会包含多个第三方库内部开发的库、以及自身项目的相关结构。
基于对项目纯源码的便捷管理,同时对依赖的第三方库、内部库方便升级和维护,做出以下设置:
1、对于第三方库和内部库,统一归纳在文件夹utils中(这里把内部库也做第三方库处理)
2、对于demo项目本身文件夹结构如下图:
demo
├── build
├── output
├── code
│ ├── CMakeLists.txt 一级 CMakeLists.txt
│ ├── other
│ └── workstation
│ │ ├── CMakeLists.txt 二级 CMakeLists.txt
│ │ ├── gui
│ │ ├── login // 登录模块
│ │ │ ├── src.cmake
│ │ │ ├── login.h
│ │ │ └──login.cpp
│ │ ├── setting // 设置模块
│ │ │ ├── src.cmake
│ │ │ ├── setting.h
│ │ │ └──setting.cpp

demo项目下分为build文件夹(项目编译过程的产生的项目资源);
output文件夹(执行cmake后的生成的文件通常分为debug和release);
code(需要我们重点关注的文件夹,纯源代码文件夹)

code下可能会包含多个项目,那么所用到的一些公共的依赖就会放在一级CMakeLists.txt中,这里以workstation项目代码为例,包括了gui资源文件夹,login文件夹,setting文件夹,子模块文件夹都包含src.camke文件。

一级CMakeLists.txt内容

cmake_minimum_required (VERSION 3.4.0)    # 指定最低cmake版本
project(demo)   # 设置项目名称
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")   
# set project output directory
string(REGEX REPLACE "/code" "" PROJECT_OUTPUT_DIR ${PROJECT_SOURCE_DIR})
set(PROJECT_OUTPUT_DIR "${PROJECT_OUTPUT_DIR}/output")
set(EXECUTABLE_OUTPUT_PATH "${PROJECT_OUTPUT_DIR}")
set(LIBRARY_OUTPUT_PATH "${PROJECT_OUTPUT_DIR}")

#build setting: just unicode version
add_definitions(-D_BIND_TO_CURRENT_VCLIBS_VERSION=1)
add_definitions(-D_UNICODE -DUNICODE)
add_definitions(-DPROTOBUF_USE_DLLS)

SET(CMAKE_DEBUG_POSTFIX d)

# 检查是否升级
option (CHECK_UPDATE_FOR_SOFTWARE_MACRO "UPDATE FOR SOFTWARE" ON)
option (GENERATE_DEBUG_INFO "always generate debug info" OFF)
# 添加宏
if(CHECK_UPDATE_FOR_SOFTWARE_MACRO)
add_definitions(-DCHECK_UPDATE_FOR_SOFTWARE_MACRO)
endif()

# 选择utils库路径
find_path(UTILS_PATH NAMES)
include(${UTILS_PATH}/utils_path.cmake)

# 查找并包含头文件
include_directories("${glm_include}")

# boost库选择
include_directories(${BOOST_PATH})
if((MSVC_VERSION GREATER 1919) AND (MSVC_VERSION LESS 1930))
  file(GLOB boost_FILE_LIST_d "${BOOST_PATH}/lib64-msvc-14.2/*mt-gd-*.dll")
  file(GLOB boost_FILE_LIST "${BOOST_PATH}/lib64-msvc-14.2/*mt-*.dll")
  file(COPY ${boost_FILE_LIST_d} DESTINATION "${PROJECT_OUTPUT_DIR}/debug") #复制文件到debug/
  file(COPY ${boost_FILE_LIST} DESTINATION "${PROJECT_OUTPUT_DIR}/release") #复制文件到release/
  link_directories(${BOOST_PATH}/lib64-msvc-14.2)
elseif((MSVC_VERSION GREATER 1909) AND (MSVC_VERSION LESS 1920))
  file(GLOB boost_FILE_LIST_d "${BOOST_PATH}/lib64-msvc-14.2/*mt-gd-*.dll")
  file(GLOB boost_FILE_LIST "${BOOST_PATH}/lib64-msvc-14.2/*mt-*.dll")
  file(COPY ${boost_FILE_LIST_d} DESTINATION "${PROJECT_OUTPUT_DIR}/debug") #复制文件到debug/
  file(COPY ${boost_FILE_LIST} DESTINATION "${PROJECT_OUTPUT_DIR}/release") #复制文件到release/
  link_directories(${BOOST_PATH}/lib64-msvc-14.1)
endif()

## mongodb 3.4
set(CMAKE_PREFIX_PATH ${UTILS_PATH}/mongocxx-3.4.0)
find_package(mongocxx REQUIRED)
find_package(bsoncxx REQUIRED)

set (CMAKE_SKIP_RULE_DEPENDENCY TRUE)

# 新增子项目文件夹
subdirs(workstation)
#subdirs(other)

二级CMakeLists.txt内容

# 添加子文件夹的src.cmake文件
include_directories(${PROJECT_SOURCE_DIR})
include("${PROJECT_SOURCE_DIR}/workstation/login/src.cmake")
include("${PROJECT_SOURCE_DIR}/workstation/setting/src.cmake")

# 上述文件set到 src
set(src 
  ${login_header} ${login_src}
  ${setting_header} ${setting_src}
)

# QT需要开启的相关属性
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

# 生成exe
add_executable(demo WIN32 ${src}) 

# copy文件
add_custom_command ( TARGET demo POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:mongo::mongocxx_shared> $<TARGET_FILE_DIR:demo>
  COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:mongo::bsoncxx_shared> $<TARGET_FILE_DIR:demo>
  COMMAND ${CMAKE_COMMAND} -E copy_if_different "${UTILS_PATH}/mongocxx-3.4.0/bin/$<CONFIG>/libmongoc-1.0.dll"  $<TARGET_FILE_DIR:demo>
  COMMAND ${CMAKE_COMMAND} -E copy_if_different "${UTILS_PATH}/mongocxx-3.4.0/bin/$<CONFIG>/libbson-1.0.dll" $<TARGET_FILE_DIR:demo>
)

# 添加依赖lib文件
target_link_libraries(demo
  PRIVATE mongo::mongocxx_shared
  PRIVATE mongo::bsoncxx_shared
  Qt5::Widgets
  Qt5::Charts
  Qt5::Core
  Qt5::Gui
) 

# if选择
if (GENERATE_DEBUG_INFO)
target_link_options(demo PUBLIC /DEBUG)
endif(GENERATE_DEBUG_INFO)

# 设置输出属性为Windows应用
set_target_properties(demo  PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
set_target_properties(demo  PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
set_target_properties(demo  PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/../output/$(Configuration)")

# 设置项目启动项(没有起作用)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT demo)

# 查找相关库文件
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets Charts PrintSupport WebEngine WebEngineCore WebEngineWidgets WinExtras)
include_directories("${mongocxx_lib}")

# 拷贝dll文件 
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Cored.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Widgetsd.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Guid.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Chartsd.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Core.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Widgets.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Gui.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Charts.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Quickd.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Quick.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5QuickWidgetsd.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5QuickWidgets.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5WebEngineCored.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5WebEngineCore.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5PrintSupportd.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5PrintSupport.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Networkd.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Network.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Qmld.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Qml.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)

FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Positioningd.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5Positioning.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5WebEngineWidgetsd.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5WebEngineWidgets.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5WinExtrasd.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5WinExtras.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5WebChanneld.dll DESTINATION ${PROJECT_OUTPUT_DIR}/debug/)
FILE(COPY ${_qt5Widgets_install_prefix}/bin/Qt5WebChannel.dll DESTINATION ${PROJECT_OUTPUT_DIR}/release/)

# 拷贝dll文件的第二种方法 
file(GLOB qt_platforms "${_qt5Widgets_install_prefix}/plugins/platforms/*")
file(COPY ${qt_platforms} DESTINATION "${PROJECT_OUTPUT_DIR}/debug/platforms")
file(COPY ${qt_platforms} DESTINATION "${PROJECT_OUTPUT_DIR}/release/platforms")

子文件src.cmake内容

# 设置文件夹路径变量dir
set(dir "${PROJECT_SOURCE_DIR}/workstation/login")

# 设置.cpp文件变量
set(login_src
${dir}/main.cpp
${dir}/login.cpp
)
# 设置.h文件变量
set(login_header
${dir}/login.h
)
# 在VS的项目工程下会新建login模块,主要是文件的归档
source_group(workstation\\login FILES ${login_header} ${login_src})

注意CMakeLists.txt文件中==增加subdirs(workstation)==可以自动查找所在文件夹下的CMakeLists.txt文件,下级CMakeLists.txt文件可以直接使用上级CMakeLists.txt中的变量
同时,在CMakeLists.txt中可以包括其他文件的src.cmake文件(上级对下级文件的使用),需要显式的调用

include("${PROJECT_SOURCE_DIR}/workstation/login/src.cmake")

第三方utils_path的文件夹需要包括编译Ok的第三方库文件加上.cmake文件,文件内容如下

set(utils_path ${UTILS_PATH})
# boost库文件
set(BOOST_PATH "${utils_path}/boost_1_73_0")

set(google_test_include "${UTILS_PATH}/google_unit_test/include")
set(google_test_lib "${UTILS_PATH}/google_unit_test/lib")
set(google_test_bin "${UTILS_PATH}/google_unit_test/bin")
set(vs_dlls "${utils_path}/vs_dlls")

set(mongocxx_include "${utils_path}/mongocxx-3.4.0/include")
set(mongocxx_lib "${utils_path}/mongocxx-3.4.0/lib")
set(mongocxx_bin "${utils_path}/mongocxx-3.4.0/bin")

参考网址

如果你想更加熟悉cmake的相关内容可以查看。
官方相关网址:https://cmake.org/cmake/help/latest/guide/tutorial/index.html