Skip to content

0.7.2 backports #1259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions .github/workflows/github-cxx-qt-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,11 @@ jobs:
clang_format_path: /home/runner/.local/bin/clang-format
cargo_dir: ~/.cargo
rustc_wrapper: sccache
build_type: Release
- name: Ubuntu 24.04 (gcc) Qt6
os: ubuntu-24.04
qt_version: 6
aqt_version: '6.8.0'
aqt_version: '6.9.0'
aqt_arch: 'linux_gcc_64'
aqt_host: 'linux'
cores: 4
Expand All @@ -262,6 +263,7 @@ jobs:
clang_format_path: /home/runner/.local/bin/clang-format
cargo_dir: ~/.cargo
rustc_wrapper: sccache
build_type: Release
packages-extra: >-
libgl1-mesa-dev
libvulkan-dev
Expand All @@ -288,13 +290,14 @@ jobs:
cxx: clang++
# sccache is disabled due on macOS to https://github.com/mozilla/sccache/issues/2092
cmake_args: -DSCCACHE=OFF
build_type: Release
- name: macOS 14 (clang) Qt6
os: macos-14
qt_version: 6
aqt_version: '6.8.0'
aqt_version: '6.9.0'
aqt_arch: 'clang_64'
aqt_host: 'mac'
dyld_framework_path: /Users/runner/work/cxx-qt/Qt/6.8.0/macos/lib
dyld_framework_path: /Users/runner/work/cxx-qt/Qt/6.9.0/macos/lib
# https://doc.qt.io/qt-6.7/macos.html#target-platforms
macosx_deployment_target: '13.0'
cores: 3
Expand All @@ -309,6 +312,7 @@ jobs:
cxx: clang++
# sccache is disabled on macOS due to https://github.com/mozilla/sccache/issues/2092
cmake_args: -DSCCACHE=OFF
build_type: Release

- name: Windows 2022 (MSVC) Qt5
os: windows-2022
Expand All @@ -326,6 +330,7 @@ jobs:
cc: cl
cxx: cl
rustc_wrapper: sccache
build_type: Release
- name: Windows 2022 (MSVC2019) Qt6
os: windows-2022
qt_version: 6
Expand All @@ -342,10 +347,12 @@ jobs:
cc: cl
cxx: cl
rustc_wrapper: sccache
- name: Windows 2022 (MSVC2022) Qt6
build_type: Release
# Use a Debug build to ensure we can build and run tests in Debug mode with MSVC
- name: Windows 2022 (MSVC2022) Qt6 Debug
os: windows-2022
qt_version: 6
aqt_version: '6.8.0'
aqt_version: '6.9.0'
aqt_arch: 'win64_msvc2022_64'
aqt_host: 'windows'
cores: 4
Expand All @@ -358,6 +365,7 @@ jobs:
cc: cl
cxx: cl
rustc_wrapper: sccache
build_type: Debug

runs-on: ${{ matrix.os }}
name: ${{ matrix.name }}
Expand Down Expand Up @@ -481,19 +489,19 @@ jobs:
run: >-
cmake ${{ matrix.cmake_args }}
-D USE_QT5=${{ matrix.qt_version == 5 }}
-D CMAKE_BUILD_TYPE=Release
-D CMAKE_BUILD_TYPE=${{ matrix.build_type }}
-G Ninja
-S . -B build
env:
RUSTC_WRAPPER: ${{ matrix.rustc_wrapper }}
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
- name: "Build"
run: cmake --build build --config Release --parallel ${{ matrix.cores }}
run: cmake --build build --config ${{ matrix.build_type }} --parallel ${{ matrix.cores }}
env:
RUSTC_WRAPPER: ${{ matrix.rustc_wrapper }}
- name: "Test"
run: ctest ${{ matrix.ctest_args }} -C Release -T test --output-on-failure --parallel ${{ matrix.cores }}
run: ctest ${{ matrix.ctest_args }} -C ${{ matrix.build_type }} -T test --output-on-failure --parallel ${{ matrix.cores }}
working-directory: ./build
env:
# Use the version of clang-format from pip
Expand Down
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased](https://github.com/KDAB/cxx-qt/compare/v0.7.1...HEAD)
## [Unreleased](https://github.com/KDAB/cxx-qt/compare/v0.7.2...HEAD)

## [0.7.2](https://github.com/KDAB/cxx-qt/compare/v0.7.1...v0.7.2) - 2025-04-28

### Added

- `impl cxx_qt::Initialize for X {}` as shorthand for `impl cxx_qt::Constructor<()> for X {}`
- `extern "RustQt"` blocks no longer have to be marked `unsafe`

### Fixed

- cxx-qt-build: Use shorter paths to avoid file path length limitations on Windows
- CMake: Use Release Qt DLLs and runtime for MSVC Debug builds
- Fix some generated links to book page
- `#[inherit]` attributes are no longer ignored in `extern "C++Qt"` blocks - they now trigger an error

## [0.7.1](https://github.com/KDAB/cxx-qt/compare/v0.7.0...v0.7.1) - 2025-03-04

Expand Down
18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ resolver = "2"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/KDAB/cxx-qt/"
version = "0.7.1"
version = "0.7.2"

# Note a version needs to be specified on dependencies of packages
# we publish, otherwise crates.io complains as it doesn't know the version.
[workspace.dependencies]
cxx-qt = { path = "crates/cxx-qt", version = "0.7.1" }
cxx-qt-macro = { path = "crates/cxx-qt-macro", version = "0.7.1" }
cxx-qt-build = { path = "crates/cxx-qt-build", version = "0.7.1" }
cxx-qt-gen = { path = "crates/cxx-qt-gen", version = "0.7.1" }
cxx-qt-lib = { path = "crates/cxx-qt-lib", version = "0.7.1" }
qt-build-utils = { path = "crates/qt-build-utils", version = "0.7.1" }
cxx-qt-lib-extras = { path = "crates/cxx-qt-lib-extras", version = "0.7.1" }
cxx-qt = { path = "crates/cxx-qt", version = "0.7.2" }
cxx-qt-macro = { path = "crates/cxx-qt-macro", version = "0.7.2" }
cxx-qt-build = { path = "crates/cxx-qt-build", version = "0.7.2" }
cxx-qt-gen = { path = "crates/cxx-qt-gen", version = "0.7.2" }
cxx-qt-lib = { path = "crates/cxx-qt-lib", version = "0.7.2" }
qt-build-utils = { path = "crates/qt-build-utils", version = "0.7.2" }
cxx-qt-lib-extras = { path = "crates/cxx-qt-lib-extras", version = "0.7.2" }

cc = { version = "1.0.100", features = ["parallel"] }
# Ensure that the example comments are kept in sync
Expand All @@ -47,7 +47,7 @@ cc = { version = "1.0.100", features = ["parallel"] }
# ./book/src/getting-started/4-cargo-executable.md
# ./book/src/getting-started/5-cmake-integration.md
# TODO: Can we re-export cxx from cxx-qt, so people don't need to manually add this anymore?
cxx = "1.0.95"
cxx = "1.0.144"
cxx-build = { version = "1.0.95", features = [ "parallel" ] }
cxx-gen = "0.7.121"
proc-macro2 = "1.0"
Expand Down
13 changes: 13 additions & 0 deletions book/src/bridge/extern_rustqt.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ For more information on inheritance and how to override methods see the [Inherit
### Traits

The [`Default` trait](https://doc.rust-lang.org/std/default/trait.Default.html) needs to be implemented for the `#[qobject]` marked struct either by hand or by using the derive macro `#[derive(Default)]`. Or the [`cxx_qt::Constructor`](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Constructor.html) trait needs to be implemented for the type.
In order to simply implement the `Constructor` trait, the following shorthand is available:

```rust,ignore
impl cxx_qt::Initialize for x {}
```

is equivalent to writing

```rust,ignore
impl cxx_qt::Constructor<()> for x {}
```

inside the bridge.

For further documentation see the [traits page](./traits.md).

Expand Down
2 changes: 1 addition & 1 deletion book/src/bridge/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ For further documentation, refer to the documentation of the individual traits:
- [CxxQtType](https://docs.rs/cxx-qt/latest/cxx_qt/trait.CxxQtType.html) - trait to reach the Rust implementation of a `QObject`
- This trait is automatically implemented for any `#[qobject]` type inside `extern "RustQt"` blocks.
- [Constructor](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Constructor.html) - custom constructor
- [Initialize](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Initialize.html) - execute Rust code when the object is constructed
- [Initialize](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Initialize.html) - execute Rust code when the object is constructed, or as shorthand for an empty constructor
- [Threading](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Threading.html) - marker trait whether CXX-Qt threading should be enabled
31 changes: 28 additions & 3 deletions book/src/getting-started/5-cmake-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ Download CXX-Qts CMake code with FetchContent:

```cmake,ignore
{{#include ../../../examples/qml_minimal/CMakeLists.txt:book_cmake_find_cxx_qt_start}}
GIT_TAG 0.7.1
GIT_TAG 0.7.2
{{#include ../../../examples/qml_minimal/CMakeLists.txt:book_cmake_find_cxx_qt_end}}
```

Expand Down Expand Up @@ -207,6 +207,31 @@ You should now see the two Labels that display the state of our `MyObject`, as w

### Windows with MSVC

If you're building CXX-Qt on Windows using MSVC generator, you need to ensure that `set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")` is set in CMake (or use the `-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL` flag) when building with the `Debug` configuration. This flag is necessary to ensure that the correct C Runtime Library is used. Then you can build using `cmake --build build --config Debug`.
MSVC provides multiple versions of its runtime library.
Unfortunately the Debug and Release versions are not binary compatible.
The recommendation by Microsoft is to not mix different runtimes.

This issue is caused by a bug in the [cc](https://docs.rs/cc/latest/cc/index.html) crate (as described in [this pull request](https://github.com/rust-lang/cc-rs/pull/717)), which has not been merged yet. Specifically, the problem is that cc generated code always links to the MultiThreaded runtime, even when building in Debug mode. We hope that this step won't be necessary in the future, once the cc crate fix is merged and released.
See also: <https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-170>

Currently, Rust by default [links to the Multi-Threaded **Release** DLL runtime](https://github.com/rust-lang/rust/issues/39016).
This is a mismatch with the default CMake MSVC Debug configurations, which uses the Multi-Threaded **Debug** DLLs.

To resolve this mismatch, we currently recommend to stick with the Multi-Threaded Release DLL runtime (`/MD`) **for the entire program**!

For CMake, make sure to call `set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")` (or use the `-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL` flag) when building with the `Debug` configuration.
See also the [Corrosion documentation](https://corrosion-rs.github.io/corrosion/common_issues.html#linking-debug-cc-libraries-into-rust-fails-on-windows-msvc-targets).

Additionally, the Qt Debug DLLs also use the Debug runtime.
We can force Qt to use the Release DLLs instead in the Debug configuration by setting the `MAP_IMPORTED_CONFIG_DEBUG` property to `"RELEASE"` on **all Qt components** that are linked into the final binary.

```cmake
# Note: The Qt:: targets are ALIAS targets that do not support setting properties directly.
# We therefore need to resolve the target names to either Qt5 or Qt6 directly.
set_property(
TARGET Qt6::Core Qt6::Gui Qt6::Qml Qt6::Test Qt6::QuickControls2
PROPERTY MAP_IMPORTED_CONFIG_DEBUG "RELEASE")
```

We hope that in the future the Rust ecosystem can be configured to use the Debug runtime, so that these additional configurations are not necessary.

Note: These issues do not apply to Cargo-only builds, as these always use the Release runtime and Release Qt DLLs.
49 changes: 44 additions & 5 deletions crates/cxx-qt-build/src/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ pub(crate) const INCLUDE_VERB: &str = "create symlink";
#[cfg(not(unix))]
pub(crate) const INCLUDE_VERB: &str = "deep copy files";

pub(crate) fn gen() -> PathBuf {
// Use a short name due to the Windows file path limit!
out().join("cxxqtgen")
}

// Clean a directory by removing it and recreating it.
pub(crate) fn clean(path: impl AsRef<Path>) -> Result<()> {
let result = std::fs::remove_dir_all(&path);
Expand All @@ -37,14 +42,47 @@ pub(crate) fn clean(path: impl AsRef<Path>) -> Result<()> {

/// The target directory, namespaced by crate
pub(crate) fn crate_target() -> PathBuf {
target().join("crates").join(crate_name())
let path = target();
if is_exporting() {
path.join("crates").join(crate_name())
} else {
// If we're not exporting, use a shortened path
// The paths for files in the OUT_DIR can get pretty long, especially if combined with
// Corrosion/CMake.
// This is an issue, as Windows has a maximum path length of 260 characters.
// The OUT_DIR is already namespaced by crate name, so we don't need to prefix again.
// See also: https://github.com/KDAB/cxx-qt/issues/1237
path
}
}

/// The target directory, namespaced by plugin
pub(crate) fn module_target(module_uri: &str) -> PathBuf {
target()
.join("qml_modules")
.join(module_name_from_uri(module_uri))
module_export(module_uri).unwrap_or_else(|| {
out()
// Use a short name due to the Windows file path limit!
.join("cxxqtqml")
.join(module_name_from_uri(module_uri))
})
}

/// The export directory, namespaced by QML module
///
/// In conctrast to the crate_export directory, this is `Some` for downstream dependencies as well.
/// This allows CMake to import QML modules from dependencies.
///
/// TODO: This may conflict if two dependencies are building QML modules with the same name!
/// We should probably include a lockfile here to avoid this.
pub(crate) fn module_export(module_uri: &str) -> Option<PathBuf> {
// In contrast to crate_export, we don't need to check for the specific crate here.
// QML modules should always be exported.
env::var("CXX_QT_EXPORT_DIR")
.ok()
.map(PathBuf::from)
.map(|dir| {
dir.join("qml_modules")
.join(module_name_from_uri(module_uri))
})
}

/// The target directory or another directory where we can write files that will be shared
Expand All @@ -54,7 +92,8 @@ pub(crate) fn target() -> PathBuf {
return export;
}

out().join("cxx-qt-build").join("target")
// Use a short name due to the Windows file path limit!
out().join("cxxqtbuild")
}

/// The export directory, if one was specified through the environment.
Expand Down
2 changes: 1 addition & 1 deletion crates/cxx-qt-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ fn generate_cxxqt_cpp_files(
header_dir: impl AsRef<Path>,
include_prefix: &str,
) -> Vec<GeneratedCppFilePaths> {
let cxx_qt_dir = dir::out().join("cxx-qt-gen");
let cxx_qt_dir = dir::gen();
std::fs::create_dir_all(&cxx_qt_dir).expect("Failed to create cxx-qt-gen directory!");
std::fs::write(cxx_qt_dir.join("include-prefix.txt"), include_prefix).expect("");

Expand Down
4 changes: 2 additions & 2 deletions crates/cxx-qt-gen/src/generator/cpp/inherit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ mod tests {

use super::*;
use crate::generator::cpp::property::tests::require_header;
use crate::parser::inherit::ParsedInheritedMethod;
use crate::parser::CaseConversion;
use crate::{parser::inherit::ParsedInheritedMethod, syntax::safety::Safety};

fn generate_from_foreign(
method: ForeignItemFn,
base_class: Option<&str>,
) -> Result<GeneratedCppQObjectBlocks> {
let method = ParsedInheritedMethod::parse(method, Safety::Safe, CaseConversion::none())?;
let method = ParsedInheritedMethod::parse(method, CaseConversion::none())?;
let inherited_methods = vec![&method];
let base_class = base_class.map(|s| s.to_owned());
generate(&inherited_methods, &base_class, &TypeNames::default())
Expand Down
3 changes: 1 addition & 2 deletions crates/cxx-qt-gen/src/generator/cpp/property/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use syn::ForeignItemFn;

use crate::naming::Name;
use crate::parser::CaseConversion;
use crate::syntax::safety::Safety;
use crate::{
generator::naming::property::{NameState, QPropertyNames},
parser::signals::ParsedSignal,
Expand All @@ -26,7 +25,7 @@ pub fn generate(idents: &QPropertyNames, qobject_name: &Name) -> Option<ParsedSi
fn #notify_rust(self: Pin<&mut #cpp_class_rust>);
};

Some(ParsedSignal::parse(method, Safety::Safe, CaseConversion::none()).unwrap())
Some(ParsedSignal::parse(method, CaseConversion::none()).unwrap())
} else {
None
}
Expand Down
9 changes: 7 additions & 2 deletions crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,13 @@ impl GeneratedRustFragment {
// Build the signals
for signal in &extern_cxxqt_block.signals {
let qobject_name = type_names.lookup(&signal.qobject_ident)?;

generated.append(&mut generate_rust_signal(signal, qobject_name, type_names)?);
generated.append(&mut generate_rust_signal(
signal,
qobject_name,
type_names,
// Copy the same safety as the extern C++Qt block into the generated extern C++
extern_cxxqt_block.unsafety.map(|_| quote! { unsafe }),
)?);
}

Ok(generated)
Expand Down
Loading
Loading