1. QT 构建系统
我们在创建QT项目时,可以选择的构建系统有三种, 目前最新版默认为 CMake
- qmake : 为 Qt 量身打造的,使用起来非常方便,单设计相对简陋,难于继续扩展。
- CMake:C++ 项目通用的构建工具,虽用起来不太友好, 但是生态完善,功能全面。
- Qbs :Qt Build Suite, 号称新一代的构建工具,比qmake 快,官方称因市场原因弃用。
从QT4.9 开始弃用 Qbs,从QT6 开始以CMake 为重点。
2. CMake下载安装
官方下载地址为:Download | CMake,根据自己的平台选择安装包。 本文使用的是windows平台,使用安装版本可在安装过程选择加入Path路径, 如果使用免安装则需要手动加入Path 路径。
配置好后打开命令行, 输入: cmake
, 可以查看到提示, 说明配置正常.
$ cmake
Usage
cmake [options] <path-to-source>
cmake [options] <path-to-existing-build>
cmake [options] -S <path-to-source> -B <path-to-build>
Specify a source directory to (re-)generate a build system for it in the
current working directory. Specify an existing build directory to
re-generate its build system.
Run 'cmake --help' for more information.
3. CMake的使用
CMake官方提供的使用教程:CMake Tutorial — CMake 3.25.1 Documentation,原文为英文教程, 如果英文比较好可以直接阅读。 本文也是依据该教程来学习使用 CMake,逐步学习。
可以从官网下载代码压缩包, 也可以 clone 此项目: DevWiki/cmake-3.25.1-tutorial-source - cmake-3.25.1-tutorial-source - DevWiki Gitea
可以使用 VS 打开此项目:
3.1 CMakeLists 变量篇
我们可以使用 SET(set)
来定义变量
语法 :SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
指令功能 : 用来显式的定义变量
例子 : SET (SRC_LST main.c other.c)
说明: 用变量代替值,例子中定义 SRC_LST
代替后面的字符串。
我们可以使用 ${NAME}
来获取变量的名称。
cmake 目录变量
环境变量名 | 描述 |
---|---|
CMAKE_BINARY_DIR, PROJECT_BINARY_DIR,<projectname> _BINARY_DIR |
如果是 in source 编译,指得就是工程顶层目录,如果是 out-of-source 编译,指的是工程编译发生的目录。PROJECT_BINARY_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。 |
CMAKE_SOURCE_DIR, PROJECT_SOURCE_DIR,<projectname> _SOURCE_DIR |
工程顶层目录。 |
CMAKE_CURRENT_SOURCE_DIR | 当前处理的 CMakeLists.txt 所在的路径,比如上面我们提到的 src 子目录。 |
CMAKE_CURRRENT_BINARY_DIR | 如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source 编译,他指的是 target 编译目录。 |
EXECUTABLE_OUTPUT_PATH , LIBRARY_OUTPUT_PATH | 最终目标文件存放的路径。 |
PROJECT_NAME | 通过 PROJECT 指令定义的项目名称。 |
cmake 系统信息
系统信息变量名 | 描述 |
---|---|
CMAKE_MAJOR_VERSION | CMAKE 主版本号,比如 2.4.6 中的 2 |
CMAKE_MINOR_VERSION | CMAKE 次版本号,比如 2.4.6 中的 4 |
CMAKE_PATCH_VERSION | CMAKE 补丁等级,比如 2.4.6 中的 6 |
CMAKE_SYSTEM | 系统名称,比如 Linux-2.6.22 |
CMAKE_SYSTEM_NAME | 不包含版本的系统名,比如 Linux |
CMAKE_SYSTEM_VERSION | 系统版本,比如 2.6.22 |
CMAKE_SYSTEM_PROCESSOR | 处理器名称,比如 i686. |
UNIX | 在所有的类 UNIX 平台为 TRUE,包括 OS X 和 cygwin |
WIN32 | 在所有的 win32 平台为 TRUE,包括 cygwin |
cmake 编译选项
编译控制开关名 | 描述 |
---|---|
BUILD_SHARED_LIBS | 使用 ADD_LIBRARY 时生成动态库 |
BUILD_STATIC_LIBS | 使用 ADD_LIBRARY 时生成静态库 |
CMAKE_C_FLAGS | 设置 C 编译选项,也可以通过指令 ADD_DEFINITIONS()添加。 |
CMAKE_CXX_FLAGS | 设置 C++编译选项,也可以通过指令 ADD_DEFINITIONS()添加。 |
3.2 CMake 常用指令
- ADD_DEFINITIONS
语法 :
ADD_DEFINITIONS(-DENABLE_DEBUG -DABC)
向 C/C++编译器添加 -D
定义. 如果你的代码中定义了 #ifdef ENABLE_DEBUG #endif
,这个代码块就会生效。
- ADD_DEPENDENCIES
语法: ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)
定义 target 依赖的其他 target, 确保在编译本 target 之前,其他的 target 已经被构建。
- AUX_SOURCE_DIRECTORY
语法 :
AUX_SOURCE_DIRECTORY(dir VARIABLE)
作用是发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。因为目前 cmake 还不能自动发现新添加的源文件。
比如 :
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})
- ADD_SUBDIRECTORY
语法 :
ADD_SUBDIRECTORY(NAME)
添加一个文件夹进行编译,该文件夹下的 CMakeLists.txt 负责编译该文件夹下的源码. NAME是想对于调用add_subdirectory的CMakeListst.txt的相对路径.
- find_package
语法 :
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE] [REQUIRED] [[COMPONENTS] [components...]] [OPTIONAL_COMPONENTS components...] [NO_POLICY_SCOPE])
查找并从外部项目加载设置。 <PackageName>_FOUND
将设置为指示是否找到该软件包。 找到软件包后,将通过软件包本身记录的变量和“导入的目标”提供特定于软件包的信息。 该 QUIET
选项禁用信息性消息,包括那些如果未找到则表示无法找到软件包的消息 REQUIRED``。REQUIRED
如果找不到软件包,该选项将停止处理并显示一条错误消息。
COMPONENTS
选件后(或 REQUIRED
选件后,如果有的话)可能会列出所需组件的特定于包装的列表 。后面可能会列出其他可选组件 OPTIONAL_COMPONENTS
。可用组件及其对是否认为找到包的影响由目标包定义。
- include_directories
语法 :
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
将给定目录添加到编译器用来搜索包含文件的目录中。相对路径被解释为相对于当前源目录。
包含目录添加到 INCLUDE_DIRECTORIES
当前 CMakeLists
文件的目录属性。它们也被添加到 INCLUDE_DIRECTORIES
当前 CMakeLists
文件中每个目标的target属性。目标属性值是生成器使用的属性值。
- link_libraries
语法 :
link_libraries([item1 [item2 [...]]] [[debug|optimized|general] <item>] ...)
将库链接到以后添加的所有目标。
- ADD_EXECUTABLE
语法 :
ADD_EXECUTABLE(<name> [source1] [source2 ...])
利用源码文件生成目标可执行程序。
- ADD_LIBRARY
语法 :
ADD_LIBRARY(<name> [STATIC | SHARED | MODULE] [source1] [source2 ...])
根据源码文件生成目标库。
STATIC
,SHARED
或者 MODULE
可以指定要创建的库的类型。 STATIC库是链接其他目标时使用的目标文件的存档。 SHARED库是动态链接的,并在运行时加载
- ENABLE_TESTING
语法:
ENABLE_TESTING()
.
控制 Makefile 是否构建 test 目标,涉及工程所有目录。 一般情况这个指令放在工程的主CMakeLists.txt 中.
- ADD_TEST
语法 :
ADD_TEST(testname Exename arg1 arg2 ...)
testname
是自定义的 test 名称,Exename
可以是构建的目标文件也可以是外部脚本等等。 后面连接传递给可执行文件的参数。 如果没有在同一个 CMakeLists.txt 中打开 ENABLE_TESTING()
指令, 任何 ADD_TEST
都是无效的。
- CMAKE_MINIMUM_REQUIRED
语法 :
CMAKE_MINIMUM_REQUIRED
定义 cmake 的最低兼容版本 比如 CMAKE_MINIMUM_REQUIRED(VERSION 2.5 FATAL_ERROR)
如果 cmake 版本小与 2.5,则出现严重错误,整个过程中止。
- EXEC_PROGRAM
在 CMakeLists.txt 处理过程中执行命令,并不会在生成的 Makefile 中执行。 具体语法为:
EXEC_PROGRAM(Executable [directory in which to run]
[ARGS <arguments to executable>]
[OUTPUT_VARIABLE <var>]
[RETURN_VALUE <var>])
用于在指定的目录运行某个程序,通过 ARGS 添加参数,如果要获取输出和返回值,可通过OUTPUT_VARIABLE 和 RETURN_VALUE 分别定义两个变量.
这个指令可以帮助你在 CMakeLists.txt 处理过程中支持任何命令,比如根据系统情况去修改代码文件等等。
- FILE 指令
文件操作指令
语法:
FILE(WRITE filename "message to write"... )
FILE(APPEND filename "message to write"... )
FILE(READ filename variable)
FILE(GLOB variable [RELATIVE path] [globbing expression_r_rs]...)
FILE(GLOB_RECURSE variable [RELATIVE path] [globbing expression_r_rs]...)
FILE(REMOVE [directory]...)
FILE(REMOVE_RECURSE [directory]...)
FILE(MAKE_DIRECTORY [directory]...)
FILE(RELATIVE_PATH variable directory file)
FILE(TO_CMAKE_PATH path result)
FILE(TO_NATIVE_PATH path result)
3.3 CMake 控制指令
- IF 指令
if(<condition>)
<commands>
elseif(<condition>) # optional block, can be repeated
<commands>
else() # optional block
<commands>
endif()
#####
IF(var),如果变量不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或<var>_NOTFOUND 时,表达式为真。
IF(NOT var ),与上述条件相反。
IF(var1 AND var2),当两个变量都为真是为真。
IF(var1 OR var2),当两个变量其中一个为真时为真。
IF(COMMAND cmd),当给定的 cmd 确实是命令并可以调用是为真。
IF(EXISTS dir)或者 IF(EXISTS file),当目录名或者文件名存在时为真。
IF(file1 IS_NEWER_THAN file2),当 file1 比 file2 新,或者 file1/file2 其中有一个不存在时为真,文件名请使用完整路径。
IF(IS_DIRECTORY dirname),当 dirname 是目录时,为真。
IF(variable MATCHES regex)
IF(string MATCHES regex)
- FOREACH 指令
语法:
foreach(<loop_var> <items>)
<commands>
endforeach()
其中 <items>
是以分号或空格分隔的项目列表。记录foreach匹配和匹配之间的所有命令endforeach而不调用。 一旦endforeach评估,命令的记录列表中的每个项目调用一次 <items>
。在每次迭代开始时,变量loop_var将设置为当前项的值。
- WHILE 指令
语法:
while(<condition>)
<commands>
endwhile()
while和匹配之间的所有命令 endwhile()被记录而不被调用。 一旦endwhile()如果被评估,则只要为 <condition>
真,就会调用记录的命令列表。
4. 后记
CMake 不仅仅用于 QT项目, 也在很多C++项目中使用, 学习CMake 是学习 C++ 和 QT 的基础。
本文主要内容参考:
- CMake 官方网站:https://cmake.org/cmake/help/latest/guide/tutorial/index.html
- CMake 教程 | CMake 从入门到应用 :https://aiden-dong.github.io/2019/07/20/CMake%E6%95%99%E7%A8%8B%E4%B9%8BCMake%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%BA%94%E7%94%A8/
如果你觉得本文对你有帮助,欢迎点赞。或者添加我的微信:【dev-wiki】进群交流学习。