Qt with autotools
From qtnode
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.