Skip to content

Commit 35973f9

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 0c326ba commit 35973f9

File tree

8 files changed

+109
-9
lines changed

8 files changed

+109
-9
lines changed

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ jobs:
247247
clang_format_path: /home/runner/.local/bin/clang-format
248248
cargo_dir: ~/.cargo
249249
rustc_wrapper: sccache
250+
build_type: Release
250251
- name: Ubuntu 24.04 (gcc) Qt6
251252
os: ubuntu-24.04
252253
qt_version: 6
@@ -262,6 +263,7 @@ jobs:
262263
clang_format_path: /home/runner/.local/bin/clang-format
263264
cargo_dir: ~/.cargo
264265
rustc_wrapper: sccache
266+
build_type: Release
265267
packages-extra: >-
266268
libgl1-mesa-dev
267269
libvulkan-dev
@@ -288,6 +290,7 @@ jobs:
288290
cxx: clang++
289291
# sccache is disabled due on macOS to https://github.com/mozilla/sccache/issues/2092
290292
cmake_args: -DSCCACHE=OFF
293+
build_type: Release
291294
- name: macOS 14 (clang) Qt6
292295
os: macos-14
293296
qt_version: 6
@@ -309,6 +312,7 @@ jobs:
309312
cxx: clang++
310313
# sccache is disabled on macOS due to https://github.com/mozilla/sccache/issues/2092
311314
cmake_args: -DSCCACHE=OFF
315+
build_type: Release
312316

313317
- name: Windows 2022 (MSVC) Qt5
314318
os: windows-2022
@@ -326,6 +330,7 @@ jobs:
326330
cc: cl
327331
cxx: cl
328332
rustc_wrapper: sccache
333+
build_type: Release
329334
- name: Windows 2022 (MSVC2019) Qt6
330335
os: windows-2022
331336
qt_version: 6
@@ -342,7 +347,9 @@ jobs:
342347
cc: cl
343348
cxx: cl
344349
rustc_wrapper: sccache
345-
- name: Windows 2022 (MSVC2022) Qt6
350+
build_type: Release
351+
# Use a Debug build to ensure we can build and run tests in Debug mode with MSVC
352+
- name: Windows 2022 (MSVC2022) Qt6 Debug
346353
os: windows-2022
347354
qt_version: 6
348355
aqt_version: '6.9.0'
@@ -358,6 +365,7 @@ jobs:
358365
cc: cl
359366
cxx: cl
360367
rustc_wrapper: sccache
368+
build_type: Debug
361369

362370
runs-on: ${{ matrix.os }}
363371
name: ${{ matrix.name }}
@@ -481,19 +489,19 @@ jobs:
481489
run: >-
482490
cmake ${{ matrix.cmake_args }}
483491
-D USE_QT5=${{ matrix.qt_version == 5 }}
484-
-D CMAKE_BUILD_TYPE=Release
492+
-D CMAKE_BUILD_TYPE=${{ matrix.build_type }}
485493
-G Ninja
486494
-S . -B build
487495
env:
488496
RUSTC_WRAPPER: ${{ matrix.rustc_wrapper }}
489497
CC: ${{ matrix.cc }}
490498
CXX: ${{ matrix.cxx }}
491499
- name: "Build"
492-
run: cmake --build build --config Release --parallel ${{ matrix.cores }}
500+
run: cmake --build build --config ${{ matrix.build_type }} --parallel ${{ matrix.cores }}
493501
env:
494502
RUSTC_WRAPPER: ${{ matrix.rustc_wrapper }}
495503
- name: "Test"
496-
run: ctest ${{ matrix.ctest_args }} -C Release -T test --output-on-failure --parallel ${{ matrix.cores }}
504+
run: ctest ${{ matrix.ctest_args }} -C ${{ matrix.build_type }} -T test --output-on-failure --parallel ${{ matrix.cores }}
497505
working-directory: ./build
498506
env:
499507
# 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)

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)