Qt with autotools

From qtnode

Jump to: navigation, search

Contents

Using Qt 4.1.0 with Autoconf/Automake

While Trolltech wants you to use qmake, this is sometimes not possible, especially if your project has complicated build rules. The main build system on Linux and other free/open source Operating Systems is GNU's autotools. In this article, we do not explain how to use these tools, but the reader is advised that GNU has extensive documentation on its web site.

This article is divided into three sections: autoconf, automake and qmake. The autoconf section tells you what to put in your configure.in to build Qt projects. The automake section tells you how to write your Makefile.am for Qt projects. The qmake section tells you how you can use qmake to figure out how to fix your configure.in and Makefile.am files.

We only consider the simple case of creating a Qt application. Creating dynamically linked libraries and so on is left to the reader. The code in this article has been tested under Qt 4.1.0, X11 and MinGW versions.

Autoconf

The main difficulty here is finding the Qt directories and ensuring that we're able to moc a header and compile and link everything. However, there are also platform-dependent libraries which Qt will only reveal by running qmake. On Linux, one must also link against the X11 libraries, while on MinGW, one must add certain Windows dll to the link command.

The configure.in is quite long, but you shouldn't have to change much of it. There is definitely the possibility of putting much of it in acinclude.m4.

AC_INIT(My project,0.1)
AC_CONFIG_HEADERS(my-project-config.h)
AM_INIT_AUTOMAKE
AC_PROG_CC
AC_PROG_CXX

AC_MSG_CHECKING(QT4.1 directory)
QTDIR=xxx
AC_ARG_WITH([qt-dir],
             AC_HELP_STRING([--with-qt-dir=/path/to/Qt-4.1.0],
                            [to specify the path to the Qt-4.1.0 directory.]),
             [QTPATHS="$withval"],
             [QTPATHS="/usr/local/Trolltech/Qt-4.1.0 /c/Qt/4.1.0"])

for x in $QTPATHS; do
    if test -d $x ; then
       QTDIR="$x"
    fi
done
if test $QTDIR = xxx ; then
   AC_MSG_ERROR(Could not locate QT 4.1)
fi
AC_MSG_RESULT($QTDIR)
host=`uname -a` # AC_CANONICAL_HOST is broken at the time of this writing.
case $host in
  MINGW32*)
    AC_MSG_NOTICE(MinGW detected.)
    QTLIBS="-L$QTDIR/lib -lopengl32 -lglu32 -lgdi32 -luser32 -lmingw32 -lqtmain -lQtOpenGL4 -lQtGui4 -lQtCore4 -mthreads -Wl,-enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc -Wl,-s -Wl,-s -Wl,-subsystem,windows"
    QTINC="-I$QTDIR/include -I$QTDIR/include/QtCore -I$QTDIR/include/QtGui -I$QTDIR/include/QtOpenGL -DUNICODE -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_THREAD_SUPPORT -DQT_NEEDS_QMAIN -frtti -fexceptions"
    QTBIN="$QTDIR/bin"
    ;;
  *)
    AC_MSG_NOTICE(I'm assuming this is Linux)
    AC_PATH_XTRA
    QTLIBS="-Wl,-rpath,$QTDIR/lib -L$QTDIR/lib -lQtGui -lQtOpenGL -lQtCore $X_LIBS -lX11 -lXext -lXmu -lXt -lXi $X_EXTRA_LIBS -lGLU -lGL -lpthread"
    QTINC="-I$QTDIR/include -I$QTDIR/include/QtGui -I$QTDIR/include/QtCore -I$QTDIR/include/QtOpenGL $X_CFLAGS -DQT_OPENGL_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED"
    QTBIN="$QTDIR/bin"
    ;;
esac

LIBS="$LIBS $QTLIBS"
INCLUDES="$INCLUDES $QTINC"
PATH="$PATH:$QTBIN"
CXXFLAGS="$CXXFLAGS $QTINC"

# Now we check whether we can actually build a Qt app.
cat > myqt.h << EOF
#include <QObject>
class Test : public QObject
{
Q_OBJECT
public:
  Test() {}
  ~Test() {}
public slots:
  void receive() {}
signals:
  void send();
};
EOF

cat > myqt.cpp << EOF
#include "myqt.h"
#include <QApplication>
int main( int argc, char **argv )
{
  QApplication app( argc, argv );
  Test t;
  QObject::connect( &t, SIGNAL(send()), &t, SLOT(receive()) );
}
EOF

AC_MSG_CHECKING(does moc work?)
bnv_try_1="moc myqt.h -o moc_myqt.cpp"
AC_TRY_EVAL(bnv_try_1)
if test x"$ac_status" != x0; then
   AC_MSG_ERROR(moc doesn't work)
fi
AC_MSG_RESULT(yes)
AC_MSG_CHECKING(can I compile moc_myqt.cpp?)
bnv_try_2="$CXX -c $CXXFLAGS -o moc_myqt.o moc_myqt.cpp"
AC_TRY_EVAL(bnv_try_2)
if test x"$ac_status" != x0; then
   AC_MSG_ERROR(couldn't compile moc_myqt.cpp)
fi
AC_MSG_RESULT(yes)
AC_MSG_CHECKING(can I compile myqt.cpp?)
bnv_try_3="$CXX $QTINC -c $CXXFLAGS -o myqt.o myqt.cpp"
AC_TRY_EVAL(bnv_try_3)
if test x"$ac_status" != x0; then
   AC_MSG_ERROR(couldn't compile myqt.cpp)
fi
AC_MSG_RESULT(yes)
AC_MSG_CHECKING(can I link against QT?)
nv_try_4="$CXX $LIBS -o myqt myqt.o moc_myqt.o"
AC_TRY_EVAL(bnv_try_4)
if test x"$ac_status" != x0; then
   AC_MSG_ERROR(couldn't link)
fi
AC_MSG_RESULT(yes)

AC_MSG_CHECKING(for mkoctfile)
AC_TRY_EVAL(mkoctfile)
if test x"$ac_status" != x0; then
   AC_MSG_ERROR(mkoctfile is not in the path)
fi
AC_MSG_RESULT(yes)
rm -f moc_myqt.cpp myqt.h myqt.cpp myqt.o myqt moc_myqt.o

AC_CONFIG_FILES(Makefile)
AC_OUTPUT

Change the project name and version, as well as its config.h name. Please read the GNU autoconf manual.

Automake

At first, there appears to be many ways of writing the Makefile.am, but it turns out that automake doesn't like it when some of its variables use GNU Make function calls. After twiddling around for a while, I came up with this:


# The final program is called "myapp"
bin_PROGRAMS = myapp

# You have two .cpp files you wrote, myapp.cpp and another.cpp
# Remember to include the name of the resource file with the .cpp extension.
myapp_SOURCES = \
	myapp.cpp \
	another.cpp \
        resources.cpp

# You have one .h file, it's called myapp.h. Therefore, here I list
# its mocced name, moc_myapp.cpp.
nodist_myapp_SOURCES = \
	moc_myapp.cpp

# This is to ensure the myapp.h file is distributed in your myapp-0.1.tar.gz
# I also have a resources.qrc I need to include. The png is used in myapp.
EXTRA_DIST = \
	$(nodist_myapp_SOURCES:moc_%.cpp=%.h) \
	resources.qrc myapp.png

# This rule lets GNU make create any moc_*.cpp from the equivalent *.h
moc_%.cpp: %.h
	moc $< -o $@

# Adjust this line according to your resources.qrc
resources.cpp : resources.qrc octave-gui.png
	rcc resources.qrc -o resources.cpp

# This line ensures that generated moc_*.cpp and resources.cpp files are
# deleted when we make clean.
CLEANFILES = $(filter moc_%.cpp,$(myapp_SOURCES)) resources.cpp

qmake

As you know, if you use certain feature, for instance OpenGL, you need to do

    QT += opengl

in your myapp.pro file. It turns out that as you add packages, qmake will add stuff to the INCLUDES, CXXFLAGS and LIBS variables. If you're using a package that requires such feature additions, then make an empty project with that feature in a separate directory, and use qmake to generate the makefiles. Then open the makefiles in a text editor (in MinGW, you need to look at Makefile.Release) and figure out what has been added to the compile and link lines. If you need to, you can even run make on your empty project and see exactly what it uses to compile it. You can then add these options to your configure.in.

Personal tools