The Fox is watching you.

利用CMake在vscode中写Qt

CMakeLists能传三代,人走配置长存。

版本要求

Qt版本5.12.3,CMake版本3.22.1,vscode版本1.90.2。为什么是这么奇怪的一个版本组合,主要是因为我对Qt不了解,而据说越更新越难用。估计也用不到什么新版才有的高级特性,而5.12.3是12小版本的最后一个,也是最后直接支持mysql的版本,听起来比较诱人。

也许该经验在其他版本的Qt也能够使用,cmake版本别低于3.1.0,其他情况可以试一试。

无中生有的需求

Qt是自带一个QtCreator的。说实话,还挺好用的。单纯的不想用QtCreator,只是写Qt的时候方便一些,功能性的东西跟vscode没法比。而5.12.3该版本下的QtCreator在创建项目的时候是没有办法选择构建工具的,默认为qmake。简单体验了一下qmake,不算难用,但是cmake在易用性以外都碾压qmake,完全没法比。有趣的是,虽然在创建项目时不能选择cmake构建,在我没有额外安装任何插件的情况下,QtCreator是能正确识别和使用cmake的。这一部分未经考究,究竟是我的配置出了问题还是这个版本有通病。

文件目录

下面是我正在尝试做的一个小项目的目录结构,只选取了最简单的一部分。
目录结构的生成方式是tree,ubuntu下直接apt安装就好,运行起来也很方便:

cd /PATH
tree > tree.md

项目目录结构:

calculatorPro
├── CMakeLists.txt
├── include
│   ├── core
│   └── ui
│       └── mainwindow.h
├── plugins
│   └── CMakeLists.txt
└── src
    ├── CMakeLists.txt
    ├── core
    │   └── CMakeLists.txt
    ├── main.cpp
    └── ui
        ├── CMakeLists.txt
        ├── mainwindow.cpp
        └── mainwindow.ui

配置文件

因为是记录向的,所以可能会比较繁琐,主要是让自己很久以后能看懂,避免称为魔法(

最初

QtCreator生成的项目,所有的文件都堆在了一起。主要是main.cpp、mainwindow.cpp、mainwindow.h、mainwindow.ui。其中mainwindow.ui是QtCreator能直接图形化设计的基础,也是后面需要vscode+QtCreator的原因。平铺的结构看起来比较混乱,难以管理,遂尝试其他结构。

根目录

根目录的CMakeLists.txt的主要作用是铺垫和组织。

# 设置最低的 CMake 版本要求为 3.1.0
cmake_minimum_required(VERSION 3.1.0)

# 定义项目名称为 CalculatorPro,版本为 0.1.0,使用 C++ 语言
project(CalculatorPro VERSION 0.1.0 LANGUAGES CXX)

# 设置 C++ 标准为 C++11
set(CMAKE_CXX_STANDARD 11)

# 强制使用 C++11 标准,防止使用其他版本的 C++ 标准
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 指定生成的可执行文件的输出目录为 ${CMAKE_BINARY_DIR}/bin
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# 指定生成的库文件的输出目录为 ${PROJECT_BINARY_DIR}/lib
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

# 启用自动处理 Qt 的 MOC、RCC 和 UIC 文件
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

# 查找并加载 Qt5 的 Widgets 组件
find_package(Qt5 COMPONENTS Widgets REQUIRED)

# 将 Qt5Widgets 的头文件路径添加到编译器的头文件搜索路径中
include_directories(
    ${Qt5Widgets_INCLUDE_DIRS}
)

# 处理 include/ui/mainwindow.h 文件中的 Qt 特殊语法,生成相应的 MOC 文件
qt5_wrap_cpp(header_SRC include/ui/mainwindow.h)

# 添加项目中 core 和 ui 目录下的头文件路径到编译器的头文件搜索路径中
include_directories(
    ${PROJECT_SOURCE_DIR}/include/core
    ${PROJECT_SOURCE_DIR}/include/ui
)

# 确保所有编译的代码都是位置无关的,以满足 Qt 使用 -reduce-relocations 选项的要求。
# 自动应用 -fPIC 编译选项,减少重定位并提高性能和安全性。
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# 添加 src 目录及其 CMake 配置
add_subdirectory(src)

# 添加 plugins 目录及其 CMake 配置
add_subdirectory(${CMAKE_SOURCE_DIR}/plugins)

# 定义生成可执行文件的目标,将 main.cpp 和生成的 MOC 文件一起编译
add_executable(${PROJECT_NAME} src/main.cpp
    ${header_SRC}
)

# 链接目标程序所需的库,包括 core、ui、plugins 和 Qt5 Widgets 库
target_link_libraries(${PROJECT_NAME} core ui plugins Qt5::Widgets)

cmake常用变量

  • CMAKE_BINARY_DIR: 表示构建目录的根目录,即生成编译文件的顶层目录。
  • PROJECT_BINARY_DIR: 表示当前项目的构建目录,通常与 CMAKE_BINARY_DIR 相同。
  • CMAKE_SOURCE_DIR: 表示项目源代码的根目录,即顶层的 CMakeLists.txt 文件所在的目录。
  • PROJECT_SOURCE_DIR: 表示当前项目的源代码目录,通常与 CMAKE_SOURCE_DIR 相同。
  • CMAKE_CURRENT_BINARY_DIR: 表示当前处理的 CMakeLists.txt 文件所在子目录的构建目录。
  • CMAKE_CURRENT_SOURCE_DIR: 表示当前处理的 CMakeLists.txt 文件所在子目录的源代码目录。
  • EXECUTABLE_OUTPUT_PATH: 指定生成的可执行文件的输出目录。
  • LIBRARY_OUTPUT_PATH: 指定生成的库文件的输出目录。

关于部分语句

  1. set(CMAKE_AUTOMOC ON)

    • moc 是 Qt 中用于处理包含 Qt 特殊宏(如 Q_OBJECT)的 C++ 头文件的工具。启用 CMAKE_AUTOMOC 后,CMake 会自动查找需要使用 moc 处理的头文件,并生成对应的 .moc 文件,免去了手动处理的麻烦。
  2. set(CMAKE_AUTORCC ON)

    • rcc 是 Qt 的资源编译器,用于将 Qt 资源文件(如图片、图标、XML 文件等)编译成 C++ 代码,以便在应用程序中以资源的方式使用。启用 CMAKE_AUTORCC 后,CMake 会自动查找项目中的资源文件,并调用 rcc 编译它们。
  3. set(CMAKE_AUTOUIC ON)

    • uic 是 Qt 的用户界面编译器,用于将 Qt Designer 创建的 .ui 文件(XML 格式)转换成相应的 C++ 代码。启用 CMAKE_AUTOUIC 后,CMake 会自动查找项目中的 .ui 文件,并调用 uic 编译它们,生成相应的 C++ 源文件。
  4. set(CMAKE_POSITION_INDEPENDENT_CODE ON)

    • Qt 库在构建时使用了 -reduce-relocations 选项。这个选项要求生成的位置无关代码,以减少动态链接时的重定位操作,从而提升运行效率和安全性。为了与使用 -reduce-relocations 选项的 Qt 库兼容,代码也必须是位置无关的。否则,链接器可能会出现错误,因为静态库或可执行文件中的代码无法正确与 Qt 库进行链接。设置CMAKE_POSITION_INDEPENDENT_CODE以确保所有编译的目标代码都使用位置无关代码。这会自动将 -fPIC 编译选项应用到所有目标中,从而满足 Qt 库的要求。

plugins

file(GLOB PLUGINS_SOURCES "*.cpp")

# SHARED是动态库,STATIC是静态库
add_library(plugins SHARED
    ${PLUGINS_SOURCES}
)

# 将头文件路径添加到目标库的包含路径中
target_include_directories(plugins PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

src

src一级的配置文件只是用来添加:

add_subdirectory(core)
add_subdirectory(ui)

src下面以ui为例:

file(GLOB UI_SOURCES "*.cpp")
file(GLOB UI_FILES "*.ui")

qt5_wrap_ui(WRAP_FILES ${UI_FILES})

#确保编译器能够找到 qt5_wrap_ui 生成的头文件,避免编译错误
include_directories(${CMAKE_BINARY_DIR}/src/ui)

add_library(ui STATIC
    ${UI_SOURCES}
    ${WRAP_FILES}
)

规划

目前先做起来,别的遇到困难再说。

添加新评论