0. TODO
- usage of am_edit
- moc and .moc.cpp files
- special macros for dcop_idl and the like
- kde and qt versioning, for #ifdefing things out (like KListView)
- non-Linux systems
- packaging, adding dependency on kdelibs etc.
- KDevelop projects
- CVS integration
1. Introduction
As a result of some requests on kde-devel I try to give an insight on how to setup the build system for a KDE application. The coverage will then extend to
KDevelop specific details, dependency issues and packaging.
The structure of this document starts with a simple hand-compiled app and does lead into a fully KDE compliant build system, suitable for inclusion in KDE's CVS and playing
nice with other software.
The following aspects will play a major role:
- we start with a nonsense application and no build system at all
- the sources will be fixed to allow a proper build system
- preparing and introducing the autoconf system
- using KDE specific macros
- compatibility notes for external sources
Please bear in mind that the requests targeted at easy-to-understand steps, so many parts of this document won't be of much use to those who already know what the topic is all about.
2. Our application
The following 3 files represent the main source file (main.cpp, defining the entry point for the program), the widget source and header file (foowidget.cpp/.h) and a very simplicistic
shell-script substitute (ab)used as a make file. It is the most simple KDE GUI possible.
Now what is wrong with the above? An end-user may not have a /usr/local/qt, or her KDE is in /opt/kde2. The version number is written into the source directly which makes it harder
to change all the occurences. The kdecore library may be named libkdecore3 for some (weird) reason, so the app could not be compiled. Finally, the compiler could be egcs instead of
g++, for example.
How to deal with the problems? Let's at first create a file config.h which includes the version number in one single line, let's say #define VERSION "0.1", and substitue the "0.1" in
main.cpp with VERSION. When the compiler preprocesses the source file, it turns the defined value into a string again, if #include "config.h" is present in main.cpp.
3. Automatic magic
This is the right moment to start with Autoconf, which only needs a configure.in and a Makefile.in for the beginning. After launching autoconf in the project directory, it builds the file
configure and (in most cases) creates other files, namely config.h and Makefile (from Makefile.in). But what does configure configure? Look at the example:
This is the minimum configuration for autoconf: It is initialized with a random existing file, checks for a C++ compiler, and outputs a makefile (taking Makefile.in as the template).
Beside that, it creates config.cache (so the following configurations are faster), the log file config.log which can be used in trouble cases, and config.status which is the preferred way
of reconfiguring a package or an application.
So far so good, this does already solve the compiler problem - just replace g++ with @CXX@ in Makefile.in and - voila - the produced Makefile contains c++
as your favorite C++ compiler.
To get config.h configured automatically, just append the line AM_INIT_AUTOMAKE(foo, 0.1) after AC_INIT. This is automake - a macro system which goes hand in hand with
autoconf. However, to let this work you need to let Autoconf know that there are some Automake macros. This is very easy - just run aclocal before you run autoconf. This tool
searches all known macro names from configure.in and places the full macros into aclocal.m4, which gets merged into configure.
The last point is to call automake --add-missing so some required files like install-sh are installed into your project directory. Automake will complain it doesn't find a
Makefile.am, but this will be fixed later.
After this short Autotools introduction, you may want to use this system for your KDE application. KDE and Qt configuration macros are available in a file named acinclude.m4.in, placed
in either of the kde modules' admin directories. If you rename that to acinclude.m4, you can use them directory. Just append the following macros: KDE_SET_PREFIX,
KDE_USE_QT(2), AM_KDE_WITH_NLS, AC_PATH_KDE.
Now the next configure run will take much longer - but it will do all necessary checks for you: whether X11 is present, whether the Qt version is the right one, where Qt's meta object compiler
is located, if gettext is available etc.
The next section will now show you how to use all the rather difficult setup.
4. Makefile goes easy
There's a difference between a simple make file, as shown above, and an easy one. Remember the warning automake gave us? It wanted a Makefile.am, so we'll give it the Makefile.am -
and at the same time we can safely forget our Makefile.in because automake builds it for us.
For the time being you should add AC_OBJEXT and AC_EXEEXT to configure.in, later we will make this obsolete.
Easy, isn't it? The best of all is that now you can easily replace the hardcoded values with the one from the KDE macros. Set INCLUDES to $(all_includes), foo_LDFLAGS to
$(all_libraries), and foo_LDADD to $(LIB_KDECORE) $(LIB_QT).
Just one note about the compilation: You'll see many define values there. Add AM_CONFIG_HEADER(config.h) to configure.in and the lines will be shortened.
The header file still contains #define VERSION "0.1", and some other values, and you're free to add more ones here.
You might think it's a drawback that your 2 source files have resulted in a total of about 30 files now, but as soon as the project grows you'll benefit heavily from it. A whole distribution
doesn't need much more administrative files - maybe a RPM spec file, a debian directory and the usual Readme, ChangeLog and so on. Adding new directories will be very easy now.
5. About
Copyright (C) 2001 Josef Spillner, dr_maux@users.sourceforge.net
This document may be improved and distributed as permitted by the GNU Free Documentation License.
|
#include <kapp.h>
#include <kcmdlineargs.h>
#include "foowidget.h"
int main(int argc, char **argv)
{
KCmdLineArgs::init(argc, argv, "Foo", "Bar", "0.1");
KApplication a(argc, argv);
FooWidget foo;
a.setMainWidget(&foo);
foo.show();
a.exec();
}
|
Listing 1: Source file source/src.step1/main.cpp
|
|
AC_INIT(Makefile.in)
AM_INIT_AUTOMAKE(foo, 0.1)
AC_PROG_CXX
KDE_SET_PREFIX
KDE_USE_QT(2)
AM_KDE_WITH_NLS
AC_PATH_KDE
AC_OUTPUT(Makefile)
|
Listing 4: Source file source/src.step2/configure.in
|
|
bin_PROGRAMS = foo
foo_SOURCES = main.cpp foowidget.cpp
noinst_HEADERS = foowidget.h
INCLUDES = $(all_includes)
foo_LDFLAGS = $(all_libraries)
foo_LDADD = $(LIB_KDECORE) $(LIB_QT)
clobber: clean
rm -f configure config.status install-sh missing mkinstalldirs
rm -f Makefile Makefile.in aclocal.m4 config.log config.cache
rm -rf .deps .libs COPYING INSTALL stamp-h stamp-h.in
rm -f config.sub config.guess config.h config.h.in libtool ltconfig ltmain.sh
|
Listing 5: Source file source/src.step3/Makefile.am
|
|
|