It’s actually pretty easy to cross-compile qBittorrent on Linux for Windows targets using MXE, a cross environment based on the MinGW-w64 toolchain.
The MinGW-w64 project is an improvement on the original MinGW (Minimalist GNU for Windows) project to support 64-bits targets. Currently MXE supports two MinGW-w64 targets: i686-w64-mingw32 for 32-bits executables and x86_64-w64-mingw32 for 64-bits executables.
This is a guide to cross-compile qBittorrent on Linux for Windows 64-bits only. As usual we’ll use Debian for this.
Configuring MXE
MXE is a cross environment that helps you cross-compile several Open Source libraries like Qt, OpenSSL, zlib, etc. This will make your life way easier if you want to cross-compile any of those projects. Since qBittorrent uses Qt, boost, OpenSSL and zlib, we are going to tell MXE to cross-compile all those projects for us.
First things first, we need to download MXE and install the dependencies. To get the most recent version we use git for this:
naikel@debian ~/git $ git clone https://github.com/mxe/mxe.git Cloning into 'mxe'... remote: Counting objects: 30857, done. remote: Compressing objects: 100% (8/8), done. remote: Total 30857 (delta 2), reused 0 (delta 0), pack-reused 30849 Receiving objects: 100% (30857/30857), 18.28 MiB | 346.00 KiB/s, done. Resolving deltas: 100% (18339/18339), done. Checking connectivity... done.
MXE needs several dependencies and depending on what you want to cross-compile you might need some of them, but not all. MXE will tell you what you haven’t installed yet in order to cross-compile a project. Anyway for a list of all dependencies you can go here.
We should first compile the cross-compiler itself, but it’s going to be installed anyway if I just tell MXE to cross-compile Qt, because it needs it.
We need to specify the target we want in an environment variable called MXE_TARGET. This target is x86_64-w64-mingw32.static.
So let’s try to cross-compile Qt 5 (we just need qtbase) to see what happens:
naikel@debian ~/git $ cd mxe naikel@debian ~/git/mxe $ make MXE_TARGETS=x86_64-w64-mingw32.static qtbase [using autodetected 4 job(s)] [check requirements] Missing requirement: autopoint Missing requirement: bison Missing requirement: flex Missing requirement: gperf Missing requirement: intltoolize Missing requirement: libtool Missing requirement: ruby Missing requirement: scons Missing requirement: gdk-pixbuf-csource
Looks like there’s some dependencies I haven’t installed yet. Since this is Debian I just need to issue:
naikel@debian ~/git/mxe $ sudo apt-get install ruby autopoint bison flex gperf \ > scons libtool-bin intltool libgtk2.0-dev
And now everything is ready to cross-compile Qt 5 (qtbase only):
naikel@debian ~/git/mxe $ make MXE_TARGETS=x86_64-w64-mingw32.static qtbase [using autodetected 4 job(s)] [build] mxe-conf x86_64-w64-mingw32.static [done] mxe-conf x86_64-w64-mingw32.static 4 KiB 0m4.042s [build] pkgconf x86_64-w64-mingw32.static [done] pkgconf x86_64-w64-mingw32.static 416 KiB 0m4.000s [build] binutils x86_64-w64-mingw32.static [done] binutils x86_64-w64-mingw32.static 431888 KiB 1m3.827s [download] mingw-w64 [no-build] mingw-w64 x86_64-w64-mingw32.static [download] gcc [build] gcc x86_64-w64-mingw32.static [done] gcc x86_64-w64-mingw32.static 2418476 KiB 7m44.256s (...)
That takes a while as you might guess. Just leave it. But take a look again at the last 3 lines: the gcc compiler is being built as well and actually all the MinGW-w64 x86_64-w64-mingw32.static toolchain is going to be downloaded, compiled, and installed. Good!
Also OpenSSL and zlib where downloaded, cross-compiled, and installed it as well. So all is left is boost and libtorrent. Let’s go with boost:
naikel@debian ~/git/mxe $ make MXE_TARGETS=x86_64-w64-mingw32.static boost [using autodetected 4 job(s)] [download] boost [build] boost x86_64-w64-mingw32.static [done] boost x86_64-w64-mingw32.static 594100 KiB 5m19.305s
I will configure libtorrent manually in the next step. That’s because I always want to try qBittorrent with several libtorrent versions, and if I use MXE to build libtorrent I will be stuck with 1.0.6. But if you don’t care about it just type:
naikel@debian ~/git/mxe $ make MXE_TARGETS=x86_64-w64-mingw32.static libtorrent-rasterbar
And skip the next section.
Finally add the MXE path to your path:
naikel@debian ~/git/mxe $ export PATH=$PATH:$HOME/git/mxe/usr/bin
Cross-Compile libtorrent-rasterbar
To cross-compile libtorrent you need to make a couple of minor changes. Basically the problem is that Windows filesystem is case-insensitive, but Linux is case-sensitive. So things like:
#include <Windows.h>
will fail miserably in Linux. You will need to edit all the include files that have capital letters and maybe change things like this:
#ifndef WIN32
to this
#ifndef _WIN32
Since I was using libtorrent 1.0.6 I also had to change a bug in the code in file.cpp
if (!GetFileAttributesEx(f.c_str(), GetFileExInfoStandard, &data))
to
if (!GetFileAttributesEx_(f.c_str(), GetFileExInfoStandard, &data))
And we’re ready. We just need to specify the target in the configure command and the location of the cross-compiled boost libraries:
naikel@debian ~/git/libtorrent $ ./configure --prefix=$HOME/git/mxe/usr/x86_64-w64-mingw32.static \ > --disable-debug --host=x86_64-w64-mingw32.static --with-boost=$HOME/git/mxe/usr/x86_64-w64-mingw32.static/
And after the configuration just compile it and install it under the MXE libraries directory:
naikel@debian ~/git/libtorrent $ make install
Cross-Compile qBittorrent
I had some problems with the configure program while trying to cross-compile qBittorrent. This is what happens:
naikel@debian ~/git/qBittorrent $ ./configure --host=x86_64-w64-mingw32.static \ > --with-boost=$HOME/git/mxe/usr/x86_64-w64-mingw32.static/include/boost \ > --with-boost-libdir=$HOME/git/mxe/usr/x86_64-w64-mingw32.static/lib --with-qt5 checking for x86_64-w64-mingw32.static-gcc... x86_64-w64-mingw32.static-gcc checking whether the C compiler works... yes (...) checking for (...)qmake... configure: error: cannot check for file existence when cross compiling
And the configure program couldn’t complete because of that. The file is there I’m not sure why it doesn’t want to check if it’s there when cross compiling. So I couldn’t find another way but to change the configure file:
@@ -4511,8 +4511,8 @@ $as_echo_n "checking for $QT_QMAKE/qmake... " >&6; } if eval \${$as_ac_File+:} false; then : $as_echo_n "(cached) " >&6 else - test "$cross_compiling" = yes && - as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 +# test "$cross_compiling" = yes && +# as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "$QT_QMAKE/qmake"; then eval "$as_ac_File=yes" else
And also change the case of the #include entries that had capitalized letters on it.
After that you need to remove the BOOST_NO_CXXX11_RVALUE_REFERENCES definition in src/src.pro to avoid this:
/(...)/boost/optional/optional.hpp:1010:5: error: 'reference_type_of_temporary_wrapper' does not name a type reference_type_of_temporary_wrapper operator *() && { return boost::move(this->get()) ; } ^
And finally configure the libraries folders and names in the pri files. Here’s the diff for src/src.pro:
diff --git a/src/src.pro b/src/src.pro index 7a40bc6..92bf460 100644 --- a/src/src.pro +++ b/src/src.pro @@ -4,7 +4,7 @@ CONFIG += qt thread silent # C++11 support CONFIG += c++11 -DEFINES += BOOST_NO_CXX11_RVALUE_REFERENCES +# DEFINES += BOOST_NO_CXX11_RVALUE_REFERENCES # Windows specific configuration win32: include(../winconf.pri)
The diff for the winconf-mingw.pri file:
diff --git a/winconf-mingw.pri b/winconf-mingw.pri index 0283d23..465e4cc 100644 --- a/winconf-mingw.pri +++ b/winconf-mingw.pri @@ -22,17 +22,17 @@ RC_FILE = qbittorrent_mingw.rc # Adapt the lib names/versions accordingly CONFIG(debug, debug|release) { - LIBS += libtorrent \ - libboost_system-mgw45-mt-d-1_47 \ - libboost_filesystem-mgw45-mt-d-1_47 \ - libboost_thread-mgw45-mt-d-1_47 + LIBS += libtorrent-rasterbar \ + libboost_system-mt \ + libboost_filesystem-mt \ + libboost_thread_win32-mt } else { - LIBS += libtorrent \ - libboost_system-mgw45-mt-1_47 \ - libboost_filesystem-mgw45-mt-1_47 \ - libboost_thread-mgw45-mt-1_47 + LIBS += libtorrent-rasterbar \ + libboost_system-mt \ + libboost_filesystem-mt \ + libboost_thread_win32-mt } LIBS += libadvapi32 libshell32 libuser32 -LIBS += libcrypto.dll libssl.dll libwsock32 libws2_32 libz libiconv.dll +LIBS += libcrypto libssl libwsock32 libws2_32 libz libiconv LIBS += libpowrprof
And the diff for the winconf.pri file:
diff --git a/winconf.pri b/winconf.pri index 73e7921..8448851 100644 --- a/winconf.pri +++ b/winconf.pri @@ -10,13 +10,14 @@ INCLUDEPATH += $$quote(C:/qBittorrent/Zlib/include) INCLUDEPATH += $$quote(C:/qBittorrent/openssl/include) # Point this to the boost lib folder -LIBS += $$quote(-LC:/qBittorrent/boost_1_51_0/stage/lib) +#LIBS += $$quote(-LC:/qBittorrent/boost_1_51_0/stage/lib) # Point this to the libtorrent lib folder -LIBS += $$quote(-LC:/qBittorrent/RC_0_16/bin/) +#LIBS += $$quote(-LC:/qBittorrent/RC_0_16/bin/) # Point this to the zlib lib folder -LIBS += $$quote(-LC:/qBittorrent/Zlib/lib) +#LIBS += $$quote(-LC:/qBittorrent/Zlib/lib) # Point this to the openssl lib folder -LIBS += $$quote(-LC:/qBittorrent/openssl/lib) +#LIBS += $$quote(-LC:/qBittorrent/openssl/lib) +LIBS += $$quote(-L~/git/mxe/usr/x86_64-w64-mingw32.static/lib)
And that’s it. We can configure qBittorrent and then run make and a 64-bits executable for Windows will be created in src/release/qbittorrent.exe.
configure: Running qmake to generate the makefile…
Project MESSAGE: Building translations
Project MESSAGE: Processing lang/qbittorrent_ar
sh: 1: lrelease: not found
Project ERROR: Building translations failed, cannot continue
configure: error: Failed running /home/richard/mxe/usr/x86_64-w64-mingw32.static/qt5/bin/qmake to generate the makefile
Since I have Qt installed natively in my Linux, I have /usr/bin/lrelease.
If you don’t I guess you have to rebuild the toolchain with:
make MXE_TARGETS=x86_64-w64-mingw32.static qttools