qttypes: Support C++ toolchains on Windows other than MSVC
Closed this issue · 2 comments
Summary
Build.rs script of qttypes assumes that on Windows the toolchain is always MSVC, which leads to linkage errors with e.g. MinGW toolchain.
Description
As discussed in #150, there are lines in qttypes/build.rs which only work for MSVC setup:
qmetaobject-rs/qttypes/build.rs
Lines 146 to 151 in d3df3a9
It produces this output:
cargo:rustc-link-lib=msvcrtd
cargo:rustc-link-search=C:/Qt/5.15.2/mingw81_32/lib
cargo:rustc-link-lib=Qt5Cored
cargo:rustc-link-lib=Qt5Guid
cargo:rustc-link-lib=Qt5Widgetsd
cargo:rustc-link-lib=Qt5Quickd
cargo:rustc-link-lib=Qt5Qmld
which instructs cargo to pass to a linked a set of libs specifically built for debugging with MSVC toolchain. Libraries with a d
suffix are only shipped with Qt MSVC builds.
Note that since there is an if debug
variable check involved in condition, this bug does not happen on --release
mode.
Also, I don't think msvcrt
/msvcrtd
is really needed outside of MSVC ecosystem? Sounds like a compiler's runtime library, similar to compiler-rt
in LLVM land.
Steps to reproduce
- Use Windows
- Do NOT install Visual Studio / MSVC toolchain (or at least hide it from env for now)
- Install rustup, add GNU target, say
x86_64-pc-windows-gnu
ori686-pc-windows-gnu
. To avoid passing around--target
arguments, default target can be saved in Cargo configuration. - Install Qt with online installer. Make sure to add Qt libs and compiler toolchain built with and for MinGW, matching bitness or Cargo target.
- Cargo-build qmetaobject-rs itself or as a part of another project in debug mode (i.e. NOT
--release
).
Expected result
Everything should be OK.
Actual result
= note: C:/Qt/Tools/mingw810_32/bin/../lib/gcc/i686-w64-mingw32/8.1.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -lmsvcrtd
C:/Qt/Tools/mingw810_32/bin/../lib/gcc/i686-w64-mingw32/8.1.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -lQt5Cored
C:/Qt/Tools/mingw810_32/bin/../lib/gcc/i686-w64-mingw32/8.1.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -lQt5Guid
C:/Qt/Tools/mingw810_32/bin/../lib/gcc/i686-w64-mingw32/8.1.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -lQt5Widgetsd
C:/Qt/Tools/mingw810_32/bin/../lib/gcc/i686-w64-mingw32/8.1.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -lQt5Quickd
C:/Qt/Tools/mingw810_32/bin/../lib/gcc/i686-w64-mingw32/8.1.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -lQt5Qmld
collect2.exe: error: ld returned 1 exit status
Now what?
Add to qttypes build script a check for current toolchain being used, and only add compiler runtime and d
suffix if target is related to MSCV.
Version
My best guess is qmetaobject-rs starting from v0.2. It doesn't happen on published v0.1.4, i.e. before factoring out qttypes into its own crate.
https://doc.rust-lang.org/reference/conditional-compilation.html#target_env
Maybe we want cfg!(target_env = "msvc")
No good. It can be set to "gnu", even though everything else is MSVC, including cargo:rustc-link-search=C:/Qt/5.15.2/msvc2019/lib
. Looks like its value is taken from toolchain, not host nor the target.
This is my setup right now:
- PATH points to
C:/Qt/5.15.2/msvc2019/{lib,bin}
- Windows 10 SDK and C++ build tools installed (rust-lang/rustup#2809)
- rustup:
- Default host:
x86_64-pc-windows-msvc
- toolchain:
stable-x86_64-pc-windows-gnu
(default) - target:
i686-pc-windows-msvc
- Default host:
- Settings up dev env by executing these lines in cmd.exe consecutively:
- "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars32.bat"
- "C:\Qt\5.15.2\msvc2019\bin\qtenv2.bat"
- "C:\Program Files\PowerShell\7\pwsh.exe"
- (needless to say, I'm launching new Windows Terminal with PowerShell profile just to type
cmd
, paste those lines and dive into another PowerShell session)
As you can see, I've been through some MS s**t this weekend. But at least certainly learned something new...
Still, qttypes build script produces this output:
TARGET = Some("i686-pc-windows-msvc")
HOST = Some("x86_64-pc-windows-gnu")
DEBUG = Some("true")
running: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Tools\\MSVC\\14.29.30037\\bin\\HostX64\\x86\\cl.exe" "-nologo" "-MD" "-Z7" "-Brepro" "-I" "C:\\Users\\ratijas\\projects\\qmetaobject-rs\\qttypes" "-I" "C:/Qt/5.15.2/msvc2019/include" "-W4" "-FoC:\\Users\\ratijas\\projects\\qmetaobject-rs\\target\\i686-pc-windows-msvc\\debug\\build\\qttypes-6b4ec5005a66d071\\out\\rust_cpp\\cpp_closures.o" "-c" "C:\\Users\\ratijas\\projects\\qmetaobject-rs\\target\\i686-pc-windows-msvc\\debug\\build\\qttypes-6b4ec5005a66d071\\out\\rust_cpp\\cpp_closures.cpp"
cpp_closures.cpp
running: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Tools\\MSVC\\14.29.30037\\bin\\HostX64\\x86\\lib.exe" "-out:C:\\Users\\ratijas\\projects\\qmetaobject-rs\\target\\i686-pc-windows-msvc\\debug\\build\\qttypes-6b4ec5005a66d071\\out\\librust_cpp_generated.a" "-nologo" "C:\\Users\\ratijas\\projects\\qmetaobject-rs\\target\\i686-pc-windows-msvc\\debug\\build\\qttypes-6b4ec5005a66d071\\out\\rust_cpp\\cpp_closures.o"
exit code: 0
cargo:rustc-link-lib=static=rust_cpp_generated
cargo:rustc-link-search=native=C:\Users\ratijas\projects\qmetaobject-rs\target\i686-pc-windows-msvc\debug\build\qttypes-6b4ec5005a66d071\out
cargo:VERSION=5.15.2
cargo:LIBRARY_PATH=C:/Qt/5.15.2/msvc2019/lib
cargo:INCLUDE_PATH=C:/Qt/5.15.2/msvc2019/include
cargo:FOUND=1
cargo:rustc-link-search=C:/Qt/5.15.2/msvc2019/lib
cargo:rustc-link-lib=Qt5Core
cargo:rustc-link-lib=Qt5Gui
cargo:rustc-link-lib=Qt5Widgets
cargo:rustc-link-lib=Qt5Quick
cargo:rustc-link-lib=Qt5Qml
cargo:rustc-link-lib=Qt5WebEngine
cargo:rerun-if-changed=src/lib.rs
Not bad, and even compiles and runs. But is definitely not what we expected. I print-debugged the cfg!(target_env = "...")
value, and turned out it is really set to "gnu". Uhh...
Oh, wait, i know why! It's because build scripts are special. They are being built as a binaries for the target platform of a host. Hence the target env matches my toolchain. Easy.
What we need instead are Environment variables Cargo sets for build scripts, namely CARGO_CFG_TARGET_ENV
.
And... boom! Tetris for Jeff. We got our d
's back — but only when they're needed:
TARGET = Some("i686-pc-windows-msvc")
HOST = Some("x86_64-pc-windows-gnu")
DEBUG = Some("true")
cargo:VERSION=5.15.2
cargo:LIBRARY_PATH=C:/Qt/5.15.2/msvc2019/lib
cargo:INCLUDE_PATH=C:/Qt/5.15.2/msvc2019/include
cargo:FOUND=1
cargo:rustc-link-lib=msvcrtd
cargo:rustc-link-search=C:/Qt/5.15.2/msvc2019/lib
cargo:rustc-link-lib=Qt5Cored
cargo:rustc-link-lib=Qt5Guid
...