当前位置: 首页 > news >正文

【CXX-Qt】1.5 使用CMake构建

在本示例中,我们将演示如何使用CMake将CXX-Qt代码集成到C++应用程序中。Cargo将CXX-Qt代码构建为静态库,然后CMake将其链接到C++可执行文件中。

我们首先需要修改项目结构,以分离项目的不同部分。

tutorial
cpp
qml
rust

将Rust项目移动到rust文件夹中。将qml文件夹移回顶层。

C++可执行文件

为了启动我们的QML应用程序,我们需要一个包含普通main函数的小型main.cpp文件。将其放在cpp文件夹中,以明确区分C++和Rust代码:

#include <QtGui/QGuiApplication>
#include <QtQml/QQmlApplicationEngine>

int main(int argc, char* argv[]){
  QGuiApplication app(argc, argv);

  QQmlApplicationEngine engine;

  const QUrl url(
    QStringLiteral("qrc:/qt/qml/com/kdab/cxx_qt/demo/qml/main.qml"));
  QObject::connect(
    &engine,
    &QQmlApplicationEngine::objectCreated,
    &app,
    [url](QObject* obj, const QUrl& objUrl) {
      if (!obj && url == objUrl)
        QCoreApplication::exit(-1);
    },
    Qt::QueuedConnection);

  engine.load(url);

  return app.exec();
}

你可以根据需要添加更多的C++代码。

在C++中使用Rust QObjects

对于我们在Rust中定义的每个#[cxx_qt::bridge],CXX-Qt都会生成一个对应的C++头文件。要包含任何生成的文件,请使用crate的名称作为包含目录。头文件的名称将是文件夹名称的组合,加上你的#[cxx_qt::bridge]的输入Rust文件名,后跟.cxxqt.h。因此,在我们的情况下:#include <qml_minimal/src/cxxqt_object.cxxqt.h>

📝 注意:任何相对于Cargo.toml文件的文件夹都会被考虑,因此src文件夹也会被包含。

包含生成的头文件后,我们可以像访问任何其他C++类一样访问MyObject C++类。继承它,将信号和槽连接到它,将其放入QVector中,随心所欲地使用它。这就是CXX-Qt的强大之处。

Cargo设置

在我们开始使用CMake构建Qt之前,我们首先需要让我们的Cargo构建准备好。如果你已经使用例如cargo new --lib qml_minimal或cargo init --lib [folder]命令生成了你的项目,你的Cargo.toml应该看起来像这样:

[package]
name = "qml_minimal"
version = "0.1.0"
edition = "2021"

[dependencies]

我们需要做几件事:

  • 指示Cargo创建一个静态库
  • 添加cxx、cxx-qt以及cxx-qt-lib作为依赖项
  • 添加cxx-qt-build作为构建依赖项

如果你已经按照Cargo设置进行了操作,大部分内容应该已经完成。确保将crate-type更改为"staticlib"!
最终,你的Cargo.toml应该看起来类似于这样。

[package]
name = "qml_minimal"
version = "0.1.0"
authors = [
  "Andrew Hayzen <andrew.hayzen@kdab.com>",
  "Gerhard de Clercq <gerhard.declercq@kdab.com>",
  "Leon Matthes <leon.matthes@kdab.com>"
]
edition = "2021"
license = "MIT OR Apache-2.0"

# 这将指示Cargo创建一个静态库,CMake可以链接到它
[lib]
crate-type = ["staticlib"]

[dependencies]
cxx = "1.0.95"
cxx-qt = "0.7"
cxx-qt-lib = { version="0.7", features = ["qt_full"] }

[build-dependencies]
# link_qt_object_files特性是静态链接Qt 6所必需的。
cxx-qt-build = { version = "0.7", features = [ "link_qt_object_files" ] }

我们还需要在Cargo.toml旁边添加一个名为build.rs的脚本:

如果你已经按照Cargo构建教程进行了操作,只需修改现有的build.rs文件。

use cxx_qt_build::{CxxQtBuilder, QmlModule};

fn main() {
    CxxQtBuilder::new()
        .qml_module(QmlModule {
            uri: "com.kdab.cxx_qt.demo",
            rust_files: &["src/cxxqt_object.rs"],
            qml_files: &["../qml/main.qml"],
            ..Default::default()
        })
        .build();
}

这是在构建时生成和编译我们的MyObject类的C++代码的地方。

每个使用#[cxx_qt::bridge]宏的Rust源文件都需要包含在此脚本中。在我们的情况下,这仅是src/cxxqt_object.rs文件。

这也是定义QML模块的地方,带有QML URI和版本。然后,模块中的文件和资源以与qt_add_qml_module CMake函数相同的方式公开。

注意,为了使CXX-Qt工作,必须找到qmake可执行文件。这是因为CXX-Qt依赖于qmake来定位系统上必要的Qt库和头文件。

通常,CXX-Qt提供的用于导入crate的CMake代码应该已经处理了这一点。

要覆盖qmake的路径,你可以将QMAKE选项传递给cxx_qt_import_crate,以确保CMake和Cargo使用相同的Qt二进制文件。
我们还需要删除src/main.rs并将其替换为src/lib.rs文件。该文件只需要包含一行:

pub mod cxxqt_object;

这只是确保我们的Rust模块包含在我们的库中。

你也可以在库中添加其他Rust模块。

CMake设置

现在在你的项目文件夹的根目录中添加一个CMakeLists.txt文件。像任何其他使用Qt的C++项目一样启动CMakeLists.txt文件。对于此示例,我们支持使用CMake的Qt5和Qt6:

cmake_minimum_required(VERSION 3.24)

project(example_qml_minimal)

# Rust始终在*-msvc目标上链接到非调试Windows运行时
# 注意最好在命令行上设置此选项,以确保所有目标一致
# https://github.com/corrosion-rs/corrosion/blob/master/doc/src/common_issues.md#linking-debug-cc-libraries-into-rust-fails-on-windows-msvc-targets
# https://github.com/rust-lang/rust/issues/39016
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
  set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
endif()

set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CXXQT_QTCOMPONENTS Core Gui Qml QuickControls2 QuickTest Test)
    set(CXXQT_QTCOMPONENTS ${CXXQT_QTCOMPONENTS} QmlImportScanner)
if(NOT USE_QT5)
    find_package(Qt6 COMPONENTS ${CXXQT_QTCOMPONENTS})
endif()
if(NOT Qt6_FOUND)
    find_package(Qt5 5.15 COMPONENTS ${CXXQT_QTCOMPONENTS} REQUIRED)
endif()

使用FetchContent下载CXX-Qt的CMake代码:

find_package(CxxQt QUIET)
if(NOT CxxQt_FOUND)
    include(FetchContent)
    FetchContent_Declare(
        CxxQt
        GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git
        GIT_TAG v0.7.0
    )

    FetchContent_MakeAvailable(CxxQt)
endif()

这为你提供了几个围绕Corrosion的包装器,Corrosion是一个用于将Rust库集成到CMake中的工具:

cxx_qt_import_crate - corrosion_import_crate的包装器。它支持与corrosion_import_crate相同的参数,并带有三个新参数:

  • QT_MODULES(必需) - 要链接到的Qt模块。在此处指定相应的CMake目标。
  • CXX_QT_EXPORT_DIR(可选) - 手动指定CXX-Qt工件将导出到的路径。
    • 这通常不是必需的。但是,如果你在同一CMake构建配置中使用不同的特性集导入相同的crate,则需要指定单独的CXX_QT_EXPORT_DIR,以避免多个版本的crate导出到同一目录。
      QMAKE(可选) - 覆盖QMAKE可执行文件的路径
      cxx_qt_import_qml_module - 此函数导入QML模块作为新目标。它需要以下参数:
      TARGET_NAME - 指定此函数将创建的CMake目标的名称
      URI - 要导入的qml模块的URI - 这需要与CxxQtBuilder::qml_module调用中的URI完全匹配。
      SOURCE_CRATE 导出QML模块的crate(此crate必须已使用cxx_qt_import_crate导入)。
# CXX-Qt(使用Corrosion)创建一个与crate同名的CMake目标。
cxx_qt_import_crate(
    MANIFEST_PATH rust/Cargo.toml 
    CRATES qml_minimal 
    QT_MODULES Qt::Core Qt::Gui Qt::Qml Qt::QuickControls2)

cxx_qt_import_qml_module(qml_minimal_qml_module
    URI "com.kdab.cxx_qt.demo"
    SOURCE_CRATE qml_minimal)
    ```
这将创建两个新的CMake目标:

qml_minimal - 由我们的crate导出的静态库
qml_minimal_qml_module - 由我们的crate导出的QML模块
    _qml_module目标将自动链接到qml_minimal目标,因此链接到_qml_module对于我们的可执行目标来说已经足够
最后,我们可以创建CMake可执行目标并将其链接到我们的crate:

```cmake
add_executable(example_qml_minimal cpp/main.cpp)

# 链接到qml模块,该模块又链接到Rust qml_minimal库
target_link_libraries(example_qml_minimal PRIVATE qml_minimal_qml_module)

# 如果我们使用静态链接的Qt,则需要导入任何qml插件
qt_import_qml_plugins(example_qml_minimal)

你的项目现在应该具有类似于以下结构:

$ tree -I target/ -I tests
.
├── CMakeLists.txt
├── cpp
│   └── main.cpp
├── qml
│   └── main.qml
└── rust
    ├── build.rs
    ├── Cargo.toml
    └── src
        ├── cxxqt_object.rs
        └── lib.rs
        ```
像构建任何其他CMake项目一样构建项目:

```bash
$ cmake -S . -B build
$ cmake --build build

如果由于任何原因失败,请查看examples/qml_minimal文件夹,其中包含完整的示例代码。

现在应该配置并编译我们的项目。如果成功,你现在可以运行我们的小项目。

$ ./build/examples/qml_minimal/example_qml_minimal

你现在应该看到显示我们的MyObject状态的两个标签,以及调用我们的两个Rust函数的两个按钮。

Windows上的MSVC

如果你在Windows上使用MSVC生成器构建CXX-Qt,你需要确保在CMake中设置了set(CMAKE_MSVC_RUNTIME_LIBRARY “MultiThreadedDLL”)(或使用-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL标志)在构建Debug配置时。此标志是确保使用正确的C运行时库所必需的。然后你可以使用cmake --build build --config Debug进行构建。

此问题是由cc crate中的一个错误引起的(如在此拉取请求中所述),该错误尚未合并。具体来说,问题是cc生成的代码始终链接到MultiThreaded运行时,即使在Debug模式下构建时也是如此。我们希望一旦cc crate修复合并并发布,此步骤将不再必要。

相关文章:

  • 《深入理解AOP编程:从基础概念到Spring实现》
  • C++中std::shuffle 的使用
  • MySQL 多列 IN 查询详解:语法、性能与实战技巧
  • 当 Selenium 的 click() /send_keys()等方法失效时:JavaScript 在 UI 自动化测试中的神奇用法
  • 工作记录 2017-02-06
  • gitlab 提交pr
  • 搭建Nginx
  • springboot第三站(1) web开发引入
  • Docker下载,包含Win、Mac
  • The test of the entire book_《Effective Modern C++》notes
  • Spring Boot集成PageHelper:轻松实现数据库分页功能
  • Linux系统之qrencode工具的安装与基本使用
  • 云安全相关博客阅读(四)
  • 使用静态库动态库也要头文件
  • 【Netty】消息分发处理方式
  • Unity shader管道液体流入并流出效果
  • Spring Boot 静态访问配置属性的解决方案
  • EditRocket for Mac v5.0.2 文本编辑器 支持M、Intel芯片
  • 从信息熵上看图像
  • RISCV虚拟化环境搭建
  • 无人机穿越大理千年古塔落券洞内,涉事“飞手”被行拘10日
  • 75岁亚当·费舍尔坐镇,再现80分钟马勒《第九交响曲》
  • 马上评|不再提“智驾”,新能源车企回归理性
  • 姜再冬大使会见巴基斯坦副总理兼外长达尔
  • 湖北奥莱斯轮胎公司逃避监管排放大气污染物被罚25万元
  • 特朗普称美军舰商船应免费通行苏伊士运河,外交部:反对任何霸凌言行