返回

CMake命令行构建Qt项目:解决Qt5_DIR变量丢失问题

windows

CMake 命令行构建 Qt 项目时 Qt5_DIR 变量丢失问题

在使用 CMake 构建包含 Qt 库的大型项目时,遇到一个问题:通过命令行或者 VSCode 调用 CMake 构建时,Qt5_DIR 变量在配置项目后会从 CMake 缓存中消失。

例如,使用如下命令配置项目:

cmake -DQt5_DIR=C:/Qt/Qt5.12.12/5.12.12/msvc2017_64/lib/cmake/Qt5 -S .. -B .

配置完成后,Qt5_DIR 变量就没了。 如果后续通过 CMake GUI 更改变量,就会报错,提示找不到 Qt5_DIR。只有在 CMake GUI 中手动设置 Qt5_DIR,这个变量才会保留,直到下次通过命令行(或者 VSCode)构建。

问题原因分析

问题出在 find_package(Qt5 ...) 的处理方式上。 尽管在命令行用 -DQt5_DIR=... 指定了路径, 但是 find_package 会查找并设置 Qt5 各个组件的路径 (如 Qt5Widgets_DIR, Qt5Core_DIR 等)。 一旦找到并设置了这些组件路径,Qt5_DIR 变量就不再被直接使用了,所以看起来像是 "消失" 了。CMake 实际上使用更具体的 Qt5<Component>_DIR 变量。

通过 CMake GUI 设置 Qt5_DIR 后,这个值会被显式地保存在 cache 中,所以不会消失。但是命令行执行会重新运行 find_package, 覆盖掉 GUI 写入的Qt5_DIR 值。

解决方案

针对这个问题,有几种可行的解决办法:

1. 使用 CACHE 强制变量进入缓存

在命令行上传递 Qt5_DIR 变量时, 使用CACHE强制变量进入缓存并且设置类型。

  • 原理: CMake 变量默认是内部(internal)的,不会显示在 GUI 中,也不会持久化到 CMakeCache.txt。CACHE 会将变量强制放入缓存,并指定变量类型(例如 PATH、STRING 等),使其持久化。

  • 操作步骤:

    修改命令行参数如下:

    cmake -DQt5_DIR:PATH=C:/Qt/Qt5.12.12/5.12.12/msvc2017_64/lib/cmake/Qt5 -S .. -B .
    

    或者

        cmake -D "Qt5_DIR:PATH=C:/Qt/Qt5.12.12/5.12.12/msvc2017_64/lib/cmake/Qt5" -S .. -B .
    

    给变量加上了类型PATH, 注意这种类型和文件或者路径不完全相同, 他仅表示一种cmake支持的, 便于进行处理的变量。

  • 说明:
    这里用 Qt5_DIR:PATH=... 明确告诉 CMake, Qt5_DIR 是一个路径类型的缓存变量。

2. 直接设置 Qt5 组件变量

直接在命令行中设置所有必要的 Qt5<Component>_DIR 变量, 完全绕过Qt5_DIR

  • 原理: 由于 CMake 实际上用的是 Qt5<Component>_DIR 变量,那么干脆直接把它们都设置好, 这样就不需要再依赖Qt5_DIR

  • 操作步骤:

    找出所有需要的 Qt5 组件(例如 Widgets, Core, SerialPort 等),然后在命令行中设置:

    cmake -DQt5Widgets_DIR=C:/Qt/Qt5.12.12/5.12.12/msvc2017_64/lib/cmake/Qt5Widgets \
          -DQt5Core_DIR=C:/Qt/Qt5.12.12/5.12.12/msvc2017_64/lib/cmake/Qt5Core \
          -DQt5SerialPort_DIR=C:/Qt/Qt5.12.12/5.12.12/msvc2017_64/lib/cmake/Qt5SerialPort \
          -S .. -B .
    
  • 说明:
    这种方法更麻烦, 需要设置多个变量, 但是也最彻底。

3. 在 CMakeLists.txt 中使用 set 强制写入缓存

  • 原理:
    即使命令行没有传入 Qt5_DIR,也可以在 CMakeLists.txt 内部,通过 set 命令结合 CACHE 强制将其写入缓存。

  • 操作步骤:
    CMakeLists.txt 文件,find_package 之后增加设置Qt5_DIR的代码。

    find_package(Qt5 COMPONENTS Widgets Core SerialPort QML Quick LinguistTools Sql REQUIRED)
    
    set(Qt5_DIR "C:/Qt/Qt5.12.12/5.12.12/msvc2017_64/lib/cmake/Qt5" CACHE PATH "Path to Qt5")
    
    
  • 说明:
    这个方案下, 虽然仍然在 CMakeLists.txt 中写死了Qt的路径,但至少保证命令行和 GUI 的行为一致。需要修改Qt的路径, 只需要更改这一个位置就可以了.

4. 使用环境变量

设置名为 Qt5_DIR 的系统环境变量。

  • 原理:
    如果系统环境变量里存在Qt5_DIR, CMake会优先使用环境变量.

  • 操作步骤:

  1. 设置系统环境变量Qt5_DIR,值为 C:/Qt/Qt5.12.12/5.12.12/msvc2017_64/lib/cmake/Qt5.
  2. 在运行 CMake 命令时,无需再使用 -DQt5_DIR
  • 说明:
    如果有多套Qt环境, 可以考虑不要用系统环境变量.

5. (进阶)创建自定义的 CMake 模块

将设置 Qt5_DIR 的逻辑封装到自定义的 CMake 模块中。

  • 原理
    可以将查找 Qt5 、设置相关变量等一系列操作封装起来。

  • 操作步骤:

  1. 创建一个名为 FindMyQt5.cmake 的文件 (放在项目中的 cmake 子文件夹).
```cmake
# FindMyQt5.cmake

# Try to find Qt5, if Qt5_DIR is not already set
if(NOT Qt5_DIR)
    # 这里可以根据实际情况编写更灵活的查找逻辑
    # 比如检查环境变量、注册表等
    set(Qt5_DIR "C:/Qt/Qt5.12.12/5.12.12/msvc2017_64/lib/cmake/Qt5" CACHE PATH "Path to Qt5")
endif()

find_package(Qt5 COMPONENTS Widgets Core SerialPort QML Quick LinguistTools Sql REQUIRED)
```

2. 在 `CMakeLists.txt` 文件开头,添加模块路径并包含它:

    ```cmake
    cmake_minimum_required(VERSION 3.24)
    project(testprog)

    # 添加模块搜索路径 (假设 FindMyQt5.cmake 在 cmake 子目录下)
    list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

    # 引入自定义模块
    include(FindMyQt5)
     ...

    ```
  • 说明:
    这个方案需要一定的 cmake 的进阶使用知识. 这种方案提供了最大的灵活性和可重用性.

总结

总的来说,使用 -DQt5_DIR:PATH=... 这种形式来指定 Qt5_DIR 是最直接、最清晰的方法。当然也可以根据你的项目管理习惯和复杂程度,采用其它办法。 选择一个自己顺手的就好.