Search code examples
c++qtmakefilecross-compilingmxe

Cross-compilation Ubuntu-Win7 using a custom Makefile instead of qmake


I've a project which uses Qt4 with no additional libraries and QtSql (for working with a SQLLite database) and I want to cross-compile it for a x86_64 Windows 7 machine. That's my summarized makefile (it uses only .hpp files, apart from main.cpp, since I make use of lots of templates):

CXX=g++-4.8
CXXFLAGS=-std=c++11 -pedantic -Wall -pedantic-errors -Wextra
IPATH=-I/usr/include/qt4/ -I/usr/include/qt4/QtGui -I/usr/include/qt4/QtSql
LDFLAGS=-lQtGui -lQtSql -lQtCore

OBJS=main.o
HEADERS=mymoc1.hpp mymoc2.hpp other-headers

all: myexec

myexec: $(OBJS)
    $(CXX) $(CXXFLAGS) $(OBJS) -o $@ $(LDFLAGS)

main.o: main.cpp $(HEADERS)
    $(CXX) -c $(CXXFLAGS) $< -o $@ $(IPATH)

mymoc1.hpp: the-header-needing-moc.hpp
    moc $< -o $@

mymoc2.hpp: other-header-needing-moc.hpp
    moc $< -o $@

I am going to follow the next tutorial: http://mxe.cc/#tutorial Supposing I've complete succesfully the first fourth steps, my doubt comes from choosing between 5c and 5d steps. What shall I use and how in my case? What does it happen with dependences like QtSql? And with moc?

Additionally, should I define LD, AR or PKG_CONFIG variables as the tutorial says? I didn't specify the linker or the assembler in my original makefile. And in case I should, why?

EDIT I've read here that mingw has troubles working with templates, and I make a deep use of them in my project. Since MXE uses mingwinternally, should I consider other alternatives (like build in Windows directly) instead of using MXE?


Solution

  • I'm going to post my own answer here, but full of details, because I was very lost and perhaps other users are in similar conditions.

    I am going to follow the next tutorial: http://mxe.cc/#tutorial Supposing I've complete successfully the first fourth steps, my doubt comes from choosing between 5c and 5d steps. What shall I use and how in my case?

    Yes, you (me indeed, I'm answering my own question), can use Makefile to cross-compile a statically-linked Qt project. The problem is, as Qt is statically linked, any other Qt dependencies must be statically linked as well, and this create a potential long string of library dependencies to be added in your Makefile (and you also need to know the correct options to be passed to the linker). The problem is some of these option or libraries are beside windows-specific, so, if you have never compiled a program for Windows before, it's hard you to know what are they and what they are used for.

    For this reason the best thing you can do is to write your own .pro file to create a working Makefile with qmake. At this point, if you do still want to make your custom Makefile, you can execute the makefile generated by qmake and see the executed commands, to copy back and experiment in your custom Makefile.

    Anyway, I'll post the farest makefile I reach (later in this post I'll show how my working *.pro file looks like). It compiled successfully, but it crash when linking, because there was a lot of unresolved dependences I hadn't be capable to fix as said above (my mxe installation is in `/usr/local/mxe):

    ifndef CROSS
    CROSS=x86_64
    endif
    
    CROSS_ID=$(CROSS)-w64-mingw32.static
    MXE_BASE=/usr/local/mxe/usr
    MXE_USR=$(MXE_BASE)/$(CROSS_ID)
    
    MXE_INCL=$(MXE_USR)/include
    MXE_QTINCL=$(MXE_USR)/qt/include
    MXE_LIB=$(MXE_USR)/lib
    MXE_QTLIB=$(MXE_USR)/qt/lib
    
    LDIFLAGS=-I$(MXE_INCL) -I$(MXE_QTINCL)/QtCore -I$(MXE_QTINCL)/QtGui -I$(MXE_QTINCL)/QtSql
    LDLFLAGS=-L$(MXE_LIB) -L$(MXE_QTLIB)
    
    LDLIBS=-Wl,-Bstatic -lwinmm -loleut32 -lQtGui -lQtSql -lQtCore
    
    CXX=$(MXE_PATH)/bin/$(CROSS_ID)-g++
    CXXFLAGS=-std=c++11 -pedantic -pedantic-errors -Wall
    
    OBJS=main.o
    MOC_HEADERS=mymoc1.hpp mymoc2.hpp
    HEADERS=$(MOC_HEADERS) myheaders
    
    APP=myapp.exe
    
    all: $(APP)
    
    $(APP): $(OBJS)
         $(CXX) $(CXXFLAGS) $(OBJS) -o $@ $(LDLFLAGS) $(LDLIBS)
    
    main.o: main.cpp $(HEADERS)
         $(CXX) $(CXXFLAGS) $< -o $@ $(LDIFLAGS)
    
    mymoc1: header1.hpp
         moc $< -o $@
    
    mymoc2: header2.hpp
         moc $< -o $@
    
    # Thee ways of call it:
    #   make  # CROSS=x86_64 by default as shown in the first line.
    #   make CROSS=x86_64 # Make it explicit.
    #   make CROSS=i686 # Choose 32 bit.
    

    What does it happen with dependencies like QtSql?

    As you see, you treat your dependencies as other normal libraries: specifying it with -l options, but now with Wl,-Bstatic (to say the linker the linking must be static), and specifying with -L options the exact place of the cross-compiled libraries as shown in the code above. Additionally, in that makefile I added -lwinmm and -loleut32 because they were ones of the falling dependencies.

    Anyway, this makefile don't fully compile because of the linker, but to work, you shall only to add the other indirectly required libraries and options (seeying the qmake generated makefile). Anyway, the main configuration issues are all of them shown in the makefile above.

    And with moc?

    The tool moc is a preprocessor, and thus, machine independent (AFAIK). So, you can pass moc to your files using the local installation of moc. Anyway, MXE, of course, also installs a platform-specific moc:

    MXE_MOC=$(MXE_USR)/qt/bin/moc
    
    mymoc1: header1.hpp
        $(MXE_MOC) $< -o $@
    

    But I don't think there's important differences between the moc of MXE and yours, except perhaps the MXE version is more modern (I don't know).

    Additionally, should I define LD, AR or PKG_CONFIG variables as the tutorial says?

    It isn't necessary. If you don't explicitly use them, you don't need to define them.

    I've read that mingw has troubles working with templates, and I make a deep use of them in my project.

    False. The current MXE version installs the mingw32 forge of gcc 5.1.0, which works like a charm.

    And what about the .pro file?

    MXE = /usr/local/mxe/usr/$$CROSS
    MXE_INCL = $$MXE/include
    MXE_LIB = $$MXE/lib
    MXE_QT = $$MXE/qt
    
    MXE_QTINCL = $$MXE_QT/include
    MXE_QTLIB = $$MXE_QT/lib
    
    TARGET = myapp # .exe no required.
    
    OBJS = main.o
    MOC_HEADERS = mymoc1.hpp mymoc2.hpp
    HEADERS = $$MOC_HEADERS other-headers
    SOURCES = main.cpp
    
    QMAKE_CXX = $${CROSS}-g++
    QMAKE_CXXFLAGS = -static -std=c++11 -pedantic -pedantinc-errors -Wall
    
    QMAKE_LFLAGS += -Xlinker -Bstatic
    INCLUDE_PATH += $$MXE_QTINCL $$MXE_QTINCL/QtGui $$MXE_QTINCL/QtSql
    
    TEMPLATE = app
    CONFIG += qt release
    QT += core gui sql
    
    LIBS += -L$$MXE_QTLIB -L$$MXE_LIB
    
    # Call it (for x86_64):
    #    /usr/local/mxe/usr/x86_64-w64-mingw32.static/qt/bin/qmake\
    #      -makefile -o cross_makefile -nomoc CROSS=x86_64 myapp.pro
    #    make -f cross_makefile
    

    As you see, I'm saying qmake to don't generate moc files (option -nomoc), because for some strange reason, qmake is not capable to find my Q_OBJECTs among so many templates. So, I had to generate them manually before. What I really did was to modify my original makefile (the one I use to compile my project for Linux) with a target call cross which generates the moc files and then call automatically qmake with the proper options.