Skip to content

Commit ee3c261

Browse files
MSVC: Use Qt Release runtime in Debug config (#1256)
* MSVC: Link Qt components to Release runtime * Windows CI: Build Qt6 in Debug To ensure we can actually build and run tests in Debug mode, run at least one Windows CI runner in Debug mode. * Document MSVC runtime mismatch issues * qml_multi_crates: Map config for Qt::Network
1 parent a662b2e commit ee3c261

File tree

9 files changed

+120
-9
lines changed

9 files changed

+120
-9
lines changed

.github/workflows/github-cxx-qt-tests.yml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ jobs:
285285
clang_format_path: /home/runner/.local/bin/clang-format
286286
cargo_dir: ~/.cargo
287287
rustc_wrapper: sccache
288+
build_type: Release
288289
- name: Ubuntu 24.04 (gcc) Qt6
289290
os: ubuntu-24.04
290291
qt_version: 6
@@ -301,6 +302,7 @@ jobs:
301302
clang_format_path: /home/runner/.local/bin/clang-format
302303
cargo_dir: ~/.cargo
303304
rustc_wrapper: sccache
305+
build_type: Release
304306
packages-extra: >-
305307
libgl1-mesa-dev
306308
libvulkan-dev
@@ -327,6 +329,7 @@ jobs:
327329
cc: clang
328330
cxx: clang++
329331
rustc_wrapper: sccache
332+
build_type: Release
330333
- name: macOS 14 (clang) Qt6
331334
os: macos-14
332335
qt_version: 6
@@ -348,6 +351,7 @@ jobs:
348351
cc: clang
349352
cxx: clang++
350353
rustc_wrapper: sccache
354+
build_type: Release
351355

352356
- name: Windows 2022 (MSVC) Qt5
353357
os: windows-2022
@@ -366,6 +370,7 @@ jobs:
366370
cc: cl
367371
cxx: cl
368372
rustc_wrapper: sccache
373+
build_type: Release
369374
- name: Windows 2022 (MSVC2019) Qt6
370375
os: windows-2022
371376
qt_version: 6
@@ -383,7 +388,9 @@ jobs:
383388
cc: cl
384389
cxx: cl
385390
rustc_wrapper: sccache
386-
- name: Windows 2022 (MSVC2022) Qt6
391+
build_type: Release
392+
# Use a Debug build to ensure we can build and run tests in Debug mode with MSVC
393+
- name: Windows 2022 (MSVC2022) Qt6 Debug
387394
os: windows-2022
388395
qt_version: 6
389396
aqt_version: '6.9.0'
@@ -400,6 +407,7 @@ jobs:
400407
cc: cl
401408
cxx: cl
402409
rustc_wrapper: sccache
410+
build_type: Debug
403411

404412
runs-on: ${{ matrix.os }}
405413
name: ${{ matrix.name }}
@@ -536,23 +544,23 @@ jobs:
536544
run: >-
537545
cmake ${{ matrix.cmake_args }}
538546
-D USE_QT5=${{ matrix.qt_version == 5 }}
539-
-D CMAKE_BUILD_TYPE=Release
547+
-D CMAKE_BUILD_TYPE=${{ matrix.build_type }}
540548
-G Ninja
541549
-S . -B build
542550
env:
543551
RUSTC_WRAPPER: ${{ matrix.rustc_wrapper }}
544552
CC: ${{ matrix.cc }}
545553
CXX: ${{ matrix.cxx }}
546554
- name: "Build"
547-
run: cmake --build build --config Release --parallel ${{ matrix.cores }}
555+
run: cmake --build build --config ${{ matrix.build_type }} --parallel ${{ matrix.cores }}
548556
env:
549557
RUSTC_WRAPPER: ${{ matrix.rustc_wrapper }}
550558

551559
- name: "Print compiler cache statistics"
552560
run: sccache --show-stats
553561

554562
- name: "Test"
555-
run: ctest ${{ matrix.ctest_args }} -C Release -T test --output-on-failure --parallel ${{ matrix.cores }}
563+
run: ctest ${{ matrix.ctest_args }} -C ${{ matrix.build_type }} -T test --output-on-failure --parallel ${{ matrix.cores }}
556564
working-directory: ./build
557565
env:
558566
# Use the version of clang-format from pip

book/src/getting-started/5-cmake-integration.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,31 @@ You should now see the two Labels that display the state of our `MyObject`, as w
207207

208208
### Windows with MSVC
209209

210-
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`.
210+
MSVC provides multiple versions of its runtime library.
211+
Unfortunately the Debug and Release versions are not binary compatible.
212+
The recommendation by Microsoft is to not mix different runtimes.
211213

212-
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.
214+
See also: <https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-170>
215+
216+
Currently, Rust by default [links to the Multi-Threaded **Release** DLL runtime](https://github.com/rust-lang/rust/issues/39016).
217+
This is a mismatch with the default CMake MSVC Debug configurations, which uses the Multi-Threaded **Debug** DLLs.
218+
219+
To resolve this mismatch, we currently recommend to stick with the Multi-Threaded Release DLL runtime (`/MD`) **for the entire program**!
220+
221+
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.
222+
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).
223+
224+
Additionally, the Qt Debug DLLs also use the Debug runtime.
225+
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.
226+
227+
```cmake
228+
# Note: The Qt:: targets are ALIAS targets that do not support setting properties directly.
229+
# We therefore need to resolve the target names to either Qt5 or Qt6 directly.
230+
set_property(
231+
TARGET Qt6::Core Qt6::Gui Qt6::Qml Qt6::Test Qt6::QuickControls2
232+
PROPERTY MAP_IMPORTED_CONFIG_DEBUG "RELEASE")
233+
```
234+
235+
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.
236+
237+
Note: These issues do not apply to Cargo-only builds, as these always use the Release runtime and Release Qt DLLs.

examples/demo_threading/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,20 @@ endif()
3535

3636
if(NOT USE_QT5)
3737
find_package(Qt6 COMPONENTS ${CXXQT_QTCOMPONENTS})
38+
set(Qt "Qt6")
3839
endif()
3940
if(NOT Qt6_FOUND)
4041
find_package(Qt5 5.15 COMPONENTS ${CXXQT_QTCOMPONENTS} REQUIRED)
42+
set(Qt "Qt5")
43+
endif()
44+
45+
if(MSVC)
46+
# Qt also needs to link against the non-debug version of the MSVC Runtime libraries, see the previous comment.
47+
# Note: The Qt:: targets are ALIAS targets that do not support setting properties directly.
48+
# We therefore need to resolve the target names to either Qt5 or Qt6 directly.
49+
set_property(
50+
TARGET ${Qt}::Core ${Qt}::Gui ${Qt}::Qml ${Qt}::QuickControls2
51+
PROPERTY MAP_IMPORTED_CONFIG_DEBUG "RELEASE")
4152
endif()
4253

4354
find_package(CxxQt QUIET)

examples/qml_features/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,20 @@ endif()
3535

3636
if(NOT USE_QT5)
3737
find_package(Qt6 COMPONENTS ${CXXQT_QTCOMPONENTS})
38+
set(Qt "Qt6")
3839
endif()
3940
if(NOT Qt6_FOUND)
4041
find_package(Qt5 5.15 COMPONENTS ${CXXQT_QTCOMPONENTS} REQUIRED)
42+
set(Qt "Qt5")
43+
endif()
44+
45+
if(MSVC)
46+
# Qt also needs to link against the non-debug version of the MSVC Runtime libraries, see the previous comment.
47+
# Note: The Qt:: targets are ALIAS targets that do not support setting properties directly.
48+
# We therefore need to resolve the target names to either Qt5 or Qt6 directly.
49+
set_property(
50+
TARGET ${Qt}::Core ${Qt}::Gui ${Qt}::Qml ${Qt}::Quick ${Qt}::QuickControls2 ${Qt}::QuickTest ${Qt}::Test
51+
PROPERTY MAP_IMPORTED_CONFIG_DEBUG "RELEASE")
4152
endif()
4253

4354
find_package(CxxQt QUIET)

examples/qml_minimal/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,24 @@ endif()
4141
# ANCHOR: book_cmake_setup-4
4242
if(NOT USE_QT5)
4343
find_package(Qt6 COMPONENTS ${CXXQT_QTCOMPONENTS})
44+
set(Qt "Qt6")
4445
endif()
4546
if(NOT Qt6_FOUND)
4647
find_package(Qt5 5.15 COMPONENTS ${CXXQT_QTCOMPONENTS} REQUIRED)
48+
set(Qt "Qt5")
49+
endif()
50+
51+
if(MSVC)
52+
# Qt also needs to link against the non-debug version of the MSVC Runtime libraries.
53+
# Note: The Qt:: targets are ALIAS targets that do not support setting properties directly.
54+
# We therefore need to resolve the target names to either Qt5 or Qt6 directly.
55+
set_property(
56+
TARGET ${Qt}::Core ${Qt}::Gui ${Qt}::Qml ${Qt}::QuickControls2 ${Qt}::QuickTest ${Qt}::Test
57+
PROPERTY MAP_IMPORTED_CONFIG_DEBUG "RELEASE")
4758
endif()
4859
# ANCHOR_END: book_cmake_setup-4
4960

61+
5062
# ANCHOR: book_cmake_find_cxx_qt_start
5163
find_package(CxxQt QUIET)
5264
if(NOT CxxQt_FOUND)

examples/qml_multi_crates/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,20 @@ endif()
3434

3535
if(NOT USE_QT5)
3636
find_package(Qt6 COMPONENTS ${CXXQT_QTCOMPONENTS})
37+
set(Qt "Qt6")
3738
endif()
3839
if(NOT Qt6_FOUND)
3940
find_package(Qt5 5.15 COMPONENTS ${CXXQT_QTCOMPONENTS} REQUIRED)
41+
set(Qt "Qt5")
42+
endif()
43+
44+
if(MSVC)
45+
# Qt also needs to link against the non-debug version of the MSVC Runtime libraries, see the previous comment.
46+
# Note: The Qt:: targets are ALIAS targets that do not support setting properties directly.
47+
# We therefore need to resolve the target names to either Qt5 or Qt6 directly.
48+
set_property(
49+
TARGET ${Qt}::Core ${Qt}::Gui ${Qt}::Qml ${Qt}::QuickControls2 ${Qt}::QuickTest ${Qt}::Test ${Qt}::Network
50+
PROPERTY MAP_IMPORTED_CONFIG_DEBUG "RELEASE")
4051
endif()
4152

4253
find_package(CxxQt QUIET)

tests/basic_cxx_only/CMakeLists.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,21 @@ set(CMAKE_CXX_STANDARD 17)
2424
set(CMAKE_CXX_STANDARD_REQUIRED ON)
2525

2626
if(NOT USE_QT5)
27-
find_package(Qt6 COMPONENTS Core Gui Qml Test)
27+
find_package(Qt6 COMPONENTS Core Gui Qml Test QuickControls2)
28+
set(Qt "Qt6")
2829
endif()
2930
if(NOT Qt6_FOUND)
3031
find_package(Qt5 5.15 COMPONENTS Core Gui Qml Test QuickControls2 REQUIRED)
32+
set(Qt "Qt5")
33+
endif()
34+
35+
if(MSVC)
36+
# Qt also needs to link against the non-debug version of the MSVC Runtime libraries, see the previous comment.
37+
# Note: The Qt:: targets are ALIAS targets that do not support setting properties directly.
38+
# We therefore need to resolve the target names to either Qt5 or Qt6 directly.
39+
set_property(
40+
TARGET ${Qt}::Core ${Qt}::Gui ${Qt}::Qml ${Qt}::Test ${Qt}::QuickControls2
41+
PROPERTY MAP_IMPORTED_CONFIG_DEBUG "RELEASE")
3142
endif()
3243

3344
find_package(CxxQt QUIET)

tests/basic_cxx_qt/CMakeLists.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,21 @@ set(CMAKE_CXX_STANDARD 17)
2424
set(CMAKE_CXX_STANDARD_REQUIRED ON)
2525

2626
if(NOT USE_QT5)
27-
find_package(Qt6 COMPONENTS Core Gui Qml Test)
27+
find_package(Qt6 COMPONENTS Core Gui Qml Test QuickControls2)
28+
set(Qt "Qt6")
2829
endif()
2930
if(NOT Qt6_FOUND)
3031
find_package(Qt5 5.15 COMPONENTS Core Gui Qml Test QuickControls2 REQUIRED)
32+
set(Qt "Qt5")
33+
endif()
34+
35+
if(MSVC)
36+
# Qt also needs to link against the non-debug version of the MSVC Runtime libraries, see the previous comment.
37+
# Note: The Qt:: targets are ALIAS targets that do not support setting properties directly.
38+
# We therefore need to resolve the target names to either Qt5 or Qt6 directly.
39+
set_property(
40+
TARGET ${Qt}::Core ${Qt}::Gui ${Qt}::Qml ${Qt}::Test ${Qt}::QuickControls2
41+
PROPERTY MAP_IMPORTED_CONFIG_DEBUG "RELEASE")
3142
endif()
3243

3344
find_package(CxxQt QUIET)

tests/qt_types_standalone/CMakeLists.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,21 @@ set(CMAKE_CXX_STANDARD 17)
2424
set(CMAKE_CXX_STANDARD_REQUIRED ON)
2525

2626
if(NOT USE_QT5)
27-
find_package(Qt6 COMPONENTS Core Gui Qml Test)
27+
find_package(Qt6 COMPONENTS Core Gui Qml Test QuickControls2)
28+
set(Qt "Qt6")
2829
endif()
2930
if(NOT Qt6_FOUND)
3031
find_package(Qt5 5.15 COMPONENTS Core Gui Qml Test QuickControls2 REQUIRED)
32+
set(Qt "Qt5")
33+
endif()
34+
35+
if(MSVC)
36+
# Qt also needs to link against the non-debug version of the MSVC Runtime libraries, see the previous comment.
37+
# Note: The Qt:: targets are ALIAS targets that do not support setting properties directly.
38+
# We therefore need to resolve the target names to either Qt5 or Qt6 directly.
39+
set_property(
40+
TARGET ${Qt}::Core ${Qt}::Gui ${Qt}::Qml ${Qt}::Test ${Qt}::QuickControls2
41+
PROPERTY MAP_IMPORTED_CONFIG_DEBUG "RELEASE")
3142
endif()
3243

3344
find_package(CxxQt QUIET)

0 commit comments

Comments
 (0)