Cross-Compile qBittorrent for Windows on Linux using MXE

Cross-Compile

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.

2 thoughts to “Cross-Compile qBittorrent for Windows on Linux using MXE”

  1. 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

    1. 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

Leave a Reply