【问题标题】:Qt platform DLL qwindows.dll not copied to platform directory on build in Visual Studio with CMake and vcpkg在使用 CMake 和 vcpkg 在 Visual Studio 中构建时,Qt 平台 DLL qwindows.dll 未复制到平台目录
【发布时间】:2022-10-05 03:30:10
【问题描述】:

我提炼了这个非常简单的 CMake 项目,它使用 vcpkg 并构建了一个简单的 Qt gui 应用程序,只显示一个主窗口。我可以让它在 Visual Studio 2022 中成功构建,但我无法让它无缝运行。出现问题是因为 Qt 平台 DLL 没有在后期构建步骤中与其他 DLL 依赖项一起复制到输出位置。

qwindows.dll(或qwindowsd.dll)文件旨在与可执行文件和其他DLL一起复制到输出位置,但位于platforms\子目录中。这不会在构建过程中发生,但是如果我创建目录并手动复制它,那么应用程序就可以工作。

对我来说,这应该作为构建过程的一部分无缝工作,所以我很想知道我做错了什么或者我设置不正确。

现在我知道最简单的 hacky 解决方案是手动放置一个 CMake 后期构建步骤,将适当的 DLL 从 vcpkg_installed 目录复制到输出目录。但这似乎是一个 hack,因为系统应该已经处理了这个问题,否则许多其他人也会遇到这个问题。

所以我要问的问题:

  • 我是否在配置中遗漏了一些琐碎的东西?
  • 我不了解 CMake + Qt 的工作原理吗?
  • 这种 CMake、vcpkg 和 Qt 的组合是否是预期可以工作的受支持配置?

CMakeLists.txt 是(大部分取自 Qt 自己的示例):

cmake_minimum_required(VERSION 3.22 FATAL_ERROR)

project(QtTest LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_standard_project_setup()

add_executable(QtGuiTest
    Source/Main.cpp
    Source/MainWindow.cpp
    Source/MainWindow.hpp
)

target_link_libraries(QtGuiTest PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets)
set_target_properties(QtGuiTest PROPERTIES WIN32_EXECUTABLE ON MACOSX_BUNDLE ON)

CMakePresets.json 是:

{
    "version": 3,
    "cmakeMinimumRequired": {
        "major": 3,
        "minor": 22,
        "patch": 0
    },
    "configurePresets": [
        {
            "name": "default",
            "displayName": "Default",
            "generator": "Visual Studio 17 2022",
            "architecture": "x64",
            "toolchainFile": "$env{VCPKG_ROOT}\\scripts\\buildsystems\\vcpkg.cmake"
        }
    ]
}

vcpkg.json 是:

{
    "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
    "name": "qtguitest",
    "version": "0.1",
    "dependencies": [
        "qtbase"
    ]
}

然后我执行 CMake 以使用 default 预设构建源代码树。


该应用程序的代码实际上是这样的:

// MainWindow.hpp
#pragma once
#include <QtGui>

class MainWindow : public QWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWindow* parent = nullptr);
};

// MainWindow.cpp
#include "MainWindow.hpp"

MainWindow::MainWindow(QWindow* parent)
    : QWindow(parent)
{}

// Main.cpp
#include <QApplication>
#include "MainWindow.hpp"

int main(int argc, char* argv[])
{
    QApplication q_application{argc, argv};
    MainWindow main_window;
    main_window.show();
    return q_application.exec();
}

作为参考,我正在使用:

  • 最新的 Visual Studio 2022 社区版(截至 2022 年 1 月 10 日)
  • 已安装 CMake 3.22(尽管 vcpkg 下载 3.24 并使用它)
  • vcpkg(截至 2022 年 1 月 10 日)
  • 正在安装 Qt 6.3.2

【问题讨论】:

    标签: qt cmake vcpkg


    【解决方案1】:

    据我所知,您必须调用一个名为 windeployqt 的工具来扫描您的源/二进制文件并构建所需内容的列表。比它应该自动部署它。请注意,这可能还不够,因为某些库/插件可能具有外部依赖项,而这些可能不会被考虑在内。一些动态依赖项或插件可能也无法识别,但请查看 windeployqt 帮助了解您可以实现的目标。

    【讨论】:

    • 我认为这就是 vcpkg cmake 脚本一直在做的事情,但我无法很好地跟踪日志来验证这一点。我确实运行了windeployqt 可执行文件,虽然它确实创建了适当的子目录并将平台qwindows.dll 复制到该位置,但它并没有复制其他必要的DLL。因此,它本身并不是一个好的解决方案。
    • 从技术上讲,您需要运行 windeployqt 是正确的,但我无法找到正确的 CMake 咒语以使其在 Visual Studio 中正确运行以进行调试和发布。
    【解决方案2】:

    vcpkg 没有魔法可以自动将 Qt6 dll 部署到需要它们的地方。阅读https://doc.qt.io/qt-6/windows-deployment.html 以了解必要的内容。

    对于 Qt 6.3,请阅读 https://www.qt.io/blog/cmake-deployment-api 。 Qt 有自己的 cmake API 来帮助部署。

    如果您想从 cmake 手动调用 windeployqt,请确保使用 cmake 目标!在 vcpkg 中,目标已被调整以处理发布/调试。

    部署 dll 的另一种方法是部署具有正确路径集的 qt.conf。 &lt;vcpkg_installed&gt;/&lt;triplet&gt;/tools/Qt6 中有一个调试/发布变体,可用作正确设置它的模板

    【讨论】:

    • 对于一个简单方便的开发解决方案,您是否建议创建/复制qt.conf 或集成调用windeployqt(或用于调试的相关批处理文件)?另外,使用 cmake 目标是什么意思?您是否考虑过基于 cmake 的特定 qt 目标?
    • Qt6 在share/Qt6CoreTools/Qt6CoreToolsTargets(-debug|-release).cmake 中有一个用于windeployqt 的导入目标。您可以在 execute_process 中直接使用导入的二进制目标。调用 windeployqt 或使用新的部署 api 可能会更好。
    • 也许我误解了最后一句话,你的意思是使用导入的二进制目标比手动调用windeployqt更好,比使用部署api更好吗?或者相反,最好手动调用 windeployqt 或使用部署 API(构建脚本并运行它等)。
    • 我会说使用部署 api,因为这是上游希望人们在未来使用的。但是,如果您想手动调用 windeployqt,请使用导入的目标(可能是 Qt6::windeployqt,但我没有检查命名)来调用 windeployqt。
    • 谢谢!我添加了一个调用 windeployqt 目标的构建后命令,这似乎是从 Visual Studio 启用简单构建和运行的最简单的事情。部署 API 对于从 VS 构建和运行来说似乎有点矫枉过正,但我​​会研究它以创建安装程序等。
    【解决方案3】:

    感谢此处其他答案的帮助,我能够将这个 sn-p 添加到我上面的 CMake 配置中。

    add_custom_command(TARGET QtGuiTest POST_BUILD 
        COMMAND Qt6::windeployqt
        ARGS $<TARGET_FILE:QtGuiTest>
    )
    

    最终在构建后步骤中以最简单的方式调用windeployqt 应用程序。

    【讨论】: