*********************************************************************** * * * GNU autotools tutorial * * ====================== * * * * Copyright (C) 2004-2005 Holger Macht * * * * Permission is granted to copy, distribute and/or modify this * * document under the terms of the GNU Free Documentation License, * * Version 1.2 or any later version published by the Free Software * * Foundation; with no Invariant Sections, no Front-Cover Texts, and * * no Back-Cover Texts. A copy of the license is included in the * * section entitled "GNU Free Documentation License". * * * *********************************************************************** ================= Table of contents ================= 1) Introduction 2) Autoconf - Part 1 3) Autoconf - Part 2 4) Configuration header 5) Autoheader 6) Writing macros - Part 1 7) Writing macros - Part 2 8) GNU make - Part 1 9) GNU make - Part 2 10) Automake - Part 1 11) Automake - Part 2 =============== 1) Introduction =============== The following chapters will explain the basic concepts of the GNU autotools. This includes simple autoconf scripts, configuration headers and a quick guide for writing own testmacros. Afterwards, there will be an explanation about GNU make which is needed to understand the next chapters about automake. This tutorial can only be considered as a draft. So don't expect too much. The chapters are divided into directories. Each chapter has its own directory and explains a small part. For every chapter, create a new directory and follow the tutorial. The complete directories - how they should look like in the end - are provided as bzipped tarball: http://www.homac.de/files/autotools_tut.tar.bz2 . Just doing the steps explained should actually be enough, but if something does not work the provided files can be used to compare. The directory which is suitable for each chapter is indicated with 'dir:' directly below the title. When doing the tutorial one can use this directory names for testing. ==================== 2) Autoconf - Part 1 ==================== dir: autoconf1 Autoconf is the most used tool for checking different systems of their capabilities. So it can be made easy to compile a software package on different architectures. Autoconf 'configures' a sourcecode package according to the results of the system tests. The output file autoconf creates is a shell script named configure which tests for compilers, libraries or header files. At first you have to write configure.ac. You will also often find the name configure.in, but the official autoconf manual sais that this name should not be used anymore. Based upon this configure.ac, autoconf generates the configure script. The basic layout of configure.ac (configure.in) in looks like that: AC_PREREQ(autoconf_version) This line tells autoconf that at least version autoconf_version is needed. If this preconditon is not given, an error message is printed and configure exits. This macro is not essential but should be used everytime. AC_INIT(packagename , version , [email for bug-reports] ) This macro is absolutely required. It performs a lot of initializations and defines the name of the package, its version number and the e-mail where bug-reports (optional) should go to. An optional fourth argument is the tar-name of the package. The [, ] characters are quotes like " in C++. AC_CONFIG_SRCDIR([some file in the source directory]) This macro simply checks for the existance of a file in the source directory. This is only a savety check but should also be used everytime. Now, there would be the several checks about which are axplained later. At last, there must be the following macro call: AC_OUTPUT([filename]) This defines the filename configure should create when all tests passed and no error occured (usual Makefile). This only makes sence in conjunction with automake. So you can simply omit the parameter when only using autoconf. Now let's come to a simple example. Therefore, we will check if a simple c++ program can be compiled on the system we use. It is called hello.cpp: #include using namespace std; int main() { cout << "Hallo, Welt!\n"; return 0; } We will go through configure.ac step by step: AC_PREREQ(2.59) Like mentioned above, this line sais that at least autoconf version 2.59 has to be available. AC_INIT( greetings , 0.1 , hmacht@suse.de ) Our program will be called "greetings", the version number is 0.1 and bug-reports should go to hmacht@suse.de. AC_CONFIG_SRCDIR([hello.cpp]) We check for the existance of hello.cpp and therefore if the source directory exists. Now there will be the program checks. For now, we will only use the already available macros. It is also possible to write own macros. It is obvious that our program will need a c++ compiler, so we check for one with the following macro. AC_PROG_CXX This macro call checks if a compatible c++ compiler is available. This should be enough for the moment. Finally we need the macro AC_OUTPUT The complete configure.ac file looks like that: AC_PREREQ(2.59) AC_INIT( greetings , 0.1 , hmacht@suse.de ) AC_CONFIG_SRCDIR([hello.cpp]) AC_PROG_CXX AC_OUTPUT Now you can run autoconf. This results in a configure script and an additional directory autom4te.cache. Run ./configure and you will see if your system is able to compile a simple c++ program. ==================== 3) Autoconf - Part 2 ==================== dir: autoconf2 Now it is time to have a closer look at the features autoconf provides. For that we will check if a small QT program can be compiled. The first lines of configure.ac remain the same: AC_PREREQ(2.59) AC_INIT( qttestapp , 0.1 , hmacht@suse.de ) AC_CONFIG_SRCDIR([hello.cpp]) The only changed thing is the name if the application (qttestapp). After these in some manner standard lines, the checks follow: AC_PROG_CXX Check for a c++ compiler. AC_CHECK_PROG(QMAKE, qmake, [/usr/lib/qt3/bin/qmake], [qmake not found],[$QTDIR/bin]) This is something new. AC_CHECK_PROG checks if the program 'qmake' (second parameter) exists in the directory given by the environment variable $PATH. The first parameter is the variable which will hold the result of the ckeck. The third parameter contains the string which will become the value of QMAKE if the prog was found. The fourth will become the value of QMAKE if the prog was not found. AC_CHECK_PROG(MOC, moc, [$QTDIR/moc], [moc not found],[$QTDIR/bin]) That is nearly the same except that it is checked for 'moc'. The following lines check for some libraries: AC_PATH_X Test if the development labraries of the X-Server can be found. Now, we define some shell variables especially for the qt-needed directories and libraries. qtdir=${QTDIR} qt_includes=$qtdir/include qt_libraries=$qtdir/lib LDFLAGS="$LDFLAGS -L$qt_libraries" The last line adds the path for the qt-libraries to the variable LDFLAGS which will be appended to the compiler command at compile time. AC_CHECK_LIB(qt,main,,AC_MSG_ERROR(Cannot find required library Qt.)) AC_CHECK_LIB checks for the library qt (-lqt). This is done by linking a testprogram with the library qt to get the function 'main' (second argument). The third argument, which is left empty in this case, is a list of shell command or macros which should be executed if the function/library was found/is usable. If it is omited, the library (-lqt) is prepended to the variable $LIBS. The next argument specifies the actions if the library could not be found. In this case, the macro AC_MSG_ERROR is used to print an error message and to exit. AC_SUBST(qt_includes) AC_SUBST(qt_libraries) This two lines mean that AC_OUTPUT should substitude every occurrence of @qt_includes@ in inputfiles with the values $qt_include and $qt_libraries have at the time AC_OUTPUT is called. AC_OUTPUT After all, AC_OUTPUT is called. So the complete configure.ac looks like that: AC_PREREQ(2.59) AC_INIT( qttestapp , 0.1 , hmacht@suse.de ) AC_CONFIG_SRCDIR([hello.cpp]) # Checks for programs. AC_PROG_CXX AC_CHECK_PROG(QMAKE, qmake, [/usr/lib/qt3/bin/qmake], [qmake not found],[$QTDIR/bin]) AC_CHECK_PROG(MOC, moc, [$QTDIR/moc], [moc not found],[$QTDIR/bin]) # Checks for libraries. AC_PATH_X qtdir=${QTDIR} qt_includes=$qtdir/include qt_libraries=$qtdir/lib LDFLAGS="$LDFLAGS -L$qt_libraries" AC_CHECK_LIB(qt,main,,AC_MSG_ERROR(Cannot find required library Qt.)) # substitutions AC_SUBST(qt_includes) AC_SUBST(qt_libraries) AC_OUTPUT Run autoconf and ./configure and you will have a simple but working test if a qt-application can be compiled on a specific system. The lines beginning with # are comments and are not considered by autoconf. There are many more checks provided by autoconf. Refer to the autoconf manual on http://www.gnu.org/software/autoconf/manual/autoconf-2.57/autoconf.html for a complete list. ======================= 4) Configuration header ======================= dir: config_header It is also possible to use a configuration file for autoconf. That is helpful if you define a lot of preprocessor symbols which would be usally appended to the commandline using the '-D' option. When using a configuration header you do not use this '-D' options at compile time, but you can include all symbols in an extra header. Afterwards you can insert this headerfile into your sourcecode. That makes the reading of the anyway relative long 'compile lines' much more easier. Also, on some systems the command line can become too long for the operating system to handle. You also have the possibility to take actions regarding to the results of configure in your sourcecode. Taking action in reference to system checks, for instance, is a common practise when developing qt/kde application. Some applications offer the possibility to decide whether to compile with or without kde support. So for creating such a configuration header you have to insert AC_CONFIG_HEADERS into your configure.ac. The argument passed to this macro is the filename of the header and should usually be called conf.h. For instance, if you check for a header file (e.g. AC_CHECK_UNISTD) in configure.ac, and the system has this header, configure will #define HAVE_UNISTD_H to 1. For this to work you have to tell ./configure whose symbols it should check. This is done by creating a conf.h.in with for instance the following lines: /* Define as 1 if you have unistd.h. */ #undef HAVE_UNISTD_H configure will now #define HAVE_UNISTD_H to 1 if the header file is present on this system otherwise #undef HAVE_UNISTD_H is used. So the files look like that: configure.ac: AC_PREREQ(2.59) AC_INIT( greetings , 0.1 , hmacht@suse.de ) AC_CONFIG_HEADERS([conf.h]) AC_CONFIG_SRCDIR([hello.cpp]) AC_PROG_CXX AC_CHECK_HEADERS([unistd.h]) AC_OUTPUT Additionally to the macro call AC_CONFIG_HEADERS we check for the header unistd.h. Now run autoconf and you will see that conf.h contains (hopefully) the following lines: /* conf.h. Generated by configure. */ /* Define as 1 if you have unistd.h. */ #define HAVE_UNISTD_H 1 You can now #include on top of the source file hello.cpp and test for preprocessor conditionals: #include #include using namespace std; int main() { #ifdef HAVE_UNISTD_H cout << "Great! We found a unistd.h!\n"; #else cout << "No unistd.h present!\n"; #endif cout << "Hallo, Welt!\n"; return 0; } Compile the program with 'g++ -I. hello.cpp -o greetings'. The '-I.' parameter is necessary because we have included conf.h with the <> characters and so we have to add the current working directory (where conf.h can be found) to our includepath. This should be done everytime so that conf.h can be found from everywhere. Run ./greetings and you will know if your system is aware of an unistd.h. ============= 5) Autoheader ============= dir: autoheader Instead of writing conf.h.in yourself, you can use the tool autoheader which is much more comfortable. For this, after running autoconf you simply can execute autoheader and this results in a suitable conf.h.in. Autoheader takes all occurrances of AC_DEFINE in configure.ac and inserts it into conf.h.in. By default this are e.g. the package name, the bugreport e-mail and the version number. So you can have for example additionally this line: AC_DEFINE(MYVAR,["hello trainees"],[My personal example variable]) Run autoconf, autoheader and ./configure. You will find lines in conf.h.in saying the following: /* My personal example variable */ #undef MYVAR Compile the following program ( g++ -I. hello.cpp -o greetings ): #include #include using namespace std; int main() { cout << "\nDieses Programm heisst "<< PACKAGE_NAME << "\n"; cout << "Die Versionsnummer ist " << PACKAGE_VERSION << "\n"; cout << "MYVAR hat den Wert: " << MYVAR <<"\n\n"; } The output should look like this: Dieses Programm heisst greetings Die Versionsnummer ist 0.1 MYVAR hat den Wert: hello trainees ========================== 6) Writing macros - Part 1 ========================== dir: macrowriting1 In some special cases it is neccessary to write own macros which check for specific conditions. The following chapter will explain how this is done. So there will be a macro which checks for an environment variable. In the simpliest case, configure.ac looks like that: AC_PREREQ(2.59) AC_INIT( greetings , 0.1 , hmacht@suse.de ) HM_CHECK_ENVVAR([MYVAR]) AC_OUTPUT The new macro is called HM_CHECK_ENVVAR and checks if a specific environment variable is set. The given parameter is the name of the variable. The macro for defining own macros is called AC_DEFUN. Its first argument is the name of the new macro and the second is the body (code). You have to create a file acinclude.m4 which will hold this macro definition. The first lines are a comment with the prototype. Then, the definition follows: # HM_CHECK_ENVVAR([ENVVAR]) # -------------------------------------- AC_DEFUN([HM_CHECK_ENVVAR], [ The new macro will be called HM_CHECK_ENVVAR (first argument). The second argument is the body and begins with this line: AC_MSG_CHECKING([$1]) This macro is used to print a message saying for which condition configure checks at the moment. The first given argument is refered to by $1 which is in our case 'MYVAR' from configure.ac. if test x"$$1" = x ; then AC_MSG_ERROR([ $1 must be set]) fi These lines are simple shell scripting. They test if there is an environment variable with the name $1 (first argument) and if it is not empty. If it is empty an error message ($1 must be set) is printed and configure exits. AC_MSG_RESULT([$$1]) The result - and therefore the value of $1 - of the test is printed. ]) Close the macro definition. In completeness, acinclude.m4 looks like that: # HM_CHECK_ENVVAR([ENVVAR]) # -------------------------------------- AC_DEFUN([HM_CHECK_ENVVAR], [ AC_MSG_CHECKING([$1]) if test x"$$1" = x ; then AC_MSG_ERROR([ $1 must be set]) fi AC_MSG_RESULT([$$1]) ]) In order for autoconf/configure to find this fresh new macro you have to call aclocal. This copies all new macros from acinclude.m4 to aclocal.m4. Run autoconf. Now you can test whether the macro works. First, set MYVAR to some value: export MYVAR="Hallo, Welt!" Now run configure and you should get the following output: checking MYVAR... Hallo, Welt configure: creating ./config.status Now unset MYVAR: unset MYVAR The output: checking MYVAR... configure: error: MYVAR must be set It works!!!! ========================== 7) Writing macros - Part 2 ========================== dir: macrowriting2 The old (HM_CHECK_ENVVAR) macro remains unchanged in acinclude.m4. Below, we add the following new macro definition: # HM_CHECK_QTVERSION # -------------------------------------- AC_DEFUN([HM_CHECK_QTVERSION], [ AC_MSG_CHECKING([qt-version]) if test x$QTDIR = x ;then AC_MSG_ERROR([QTDIR is not set]) fi QT_VERSION_LINE="`grep \"#define QT_VERSION_STR\" $QTDIR/include/qglobal.h 2>/dev/null`" QT_VERSION=`echo $QT_VERSION_LINE | cut -d' ' -f3 | sed -e 's/"//g'` if test x$QT_VERSION = x ;then AC_MSG_ERROR([qglobal.h could not be found in $QTDIR/include/. Please set \$QTDIR correctly]) fi AC_MSG_RESULT([$QT_VERSION]) ]) We are checking for the qtversion (HM_CHECK_QTVERSION). If $QTDIR is not set, exit with an error message. Now, the version is grepped out of $QTDIR/include/qglobal.h. Afterwards, if the variable holding the version ($QT_VERSION) is empty, exit also. configure.ac with both new macro calls: AC_PREREQ(2.59) AC_INIT( greetings , 0.1 , hmacht@suse.de ) HM_CHECK_ENVVAR([MYVAR]) HM_CHECK_QTVERSION AC_OUTPUT Yuu will see if there is a version of QT installed, and what version number it has. ==================== 8) GNU make - Part 1 ==================== dir: make1 Now I want to show the basic concept of make. Example program: main.cpp: #include "printing.h" int main() { printStr("Hello, World!"); return 0; } printing.cpp: #include "printing.h" void printStr(char *text) { cout << text << "\n"; } printing.h: #include using namespace std; void printStr(char *); Usally translated with: g++ main.cpp printing.cpp -o greetings When making changes in one of the three files you have to translate all the three files again. That does not matter much with such a small program, but with a big project it takes a long time to compile everything. So you have the possibility to compile the files independently into object files. Afterwards you can link the three files together. So you can execute the following: g++ -c main.cpp g++ -c printing.cpp This results in two object files called main.o and printing.o. You can link these files together to get a binary file. g++ main.o printing.o -o greetings When changing one file, for instance main.cpp, you only have to translate main.cpp into an object file and then link the files together. This saves a lot of compile time in big projects. Also the command line can get quite long when having a lot of source files. So you can configure make to do the job for you. It looks at the last time when a sourcefile was modified and so decides whether it has to be recompiled. So you only have to type make and you will get the executable. At first you have to write a Makefile. It mainly consists of targets. Targets can be executables, object files or even actions (like clean up directory). The following shape will be used everytime: target : prerequisites command Notice that there _MUST_ be a real tab before the command. The target specifies what should be built. The prerequisites are something which are needed to build the target and the result of the command is the target. So our first line of the Makefile looks like that: greetings : main.o printing.o g++ -o greetings main.o printing.o greetings is our first target. The first target is executed by make everytime you only type the single word make at the command prompt. As an alternative you can also type make greetings. main.o and printing.o are needed to built greetings and the command beginning with g++ produces the target. But that is not everything we have to do. We also have to specify how the object files are created. This are the next lines: main.o : main.cpp printing.h g++ -c main.cpp printing.o : printing.cpp printing.h g++ -c printing.cpp There are two new targets. main.o depends on main.cpp and printing.h. main.o is build with the command g++ -c main.cpp which results in the target. printing.o depends on printing.cpp and printing.h. The command is analog to the one above. The last target is an action called clean which cleans up the sourcedirectory removing all object files. clean : rm main.o printing.o The target clean has no prerequisites because it depends on nothing else. So 'make clean' on the command line results in removing all mentioned object files (rm main.o printing.o). The whole Makefile: greetings : main.o printing.o g++ -o greetings main.o printing.o main.o : main.cpp printing.h g++ -c main.cpp printing.o : printing.cpp printing.h g++ -c printing.cpp clean : rm main.o printing.o Now simply type make and greetings will be build. Modifying main.cpp results in only rebuilding main.cpp and linking main.o and printing.o. Changing printing.cpp results in compiling printing.cpp into an object file and afterwards linking. When you edit printing.h both main.cpp and printing.cpp will be recompiled because main.cpp and printing.cpp depend on this header file. ==================== 9) GNU make - Part 2 ==================== dir: make2 It is commaon practise to use variables in Makefiles. So at first we define a variable holding our object files. The big advantage is that when the names of the object files change or there are new ones, you only have to change this one variable. Change your Makefile like that: objects = main.o printing.o greetings : $(objects) g++ -o hi $(objects) main.o : main.cpp printing.h g++ -c main.cpp printing.o : printing.cpp printing.h g++ -c printing.cpp .PHONY : clean clean : rm $(objects) So the expression $(objects) is replaced with all the object files we have. We also added the line '.PHONY : clean' which tells make that clean is no file to create but rather an action. This prevents conflicts when there will ever be a file called clean in the directory. With a Phony target, clean will be executed everytime because it has no prerequisites. So make will not decide that it is up-to-date and therefore not to rebuild it. ===================== 10) Automake - Part 1 ===================== dir: automake1 Automake automates the writing of Makefiles. For using automake you have to write a file called Makefile.am. This file will be used by automake to create Makefile.in. Based on this file ./configure (autoconf) will create the 'real' Makefile. But at first you have to update your configure.ac. In the following examples we return to the first example from autoconf. So until now, configure.ac looks like that: AC_PREREQ(2.59) AC_INIT( greetings , 0.1 , hmacht@suse.de ) AC_CONFIG_SRCDIR([hello.cpp]) AC_PROG_CXX AC_OUTPUT We have to add a line AM_INIT_AUTOMAKE directly below AC_INIT. This initializes the automake process. It is also very helpful to check for an install program which will install the resulting binary, documentation or datafiles into its corresponding directories. So we add the line AC_PROG_INSTALL below AC_PROG_CXX. For this to work, you have to distribute your own install program, otherwise automake will complain about that. You can copy the default install-sh from /usr/share/automake-1.8/ to the project directory. If you have a suitable install programm in your $PATH, automake will use this one. Additionally we add a macro call for checking for a default C compiler. So here is our new configure.ac: AC_PREREQ(2.59) AC_INIT( greetings , 0.1 , hmacht@suse.de ) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([hello.cpp]) AC_PROG_CC AC_PROG_CXX AC_PROG_INSTALL AC_OUTPUT(Makefile) Now that we are using automake we have also to define an output file. This is done by giving an additional argument to AC_OUTPUT (AC_OUTPUT(Makefile)). Now we have a look on Makefile.am. At first we add an option: AUTOMAKE_OPTIONS = foreign This tells automake that not all common files like INSTALL, README, AUTHORS or COPYRIGHT are needed. This is only used here and should be avoided in real projects. Now the filename of the binary/output program is defined: bin_PROGRAMS = greetings If there are more output files, simply add them whitespace seperated. The prefix 'bin_' means that the executables should go to bindir (usual /usr/bin). If it sais sbin_PROGRAMS, then the files would be installed in /usr/sbin. greetings_SOURCES = hello.cpp The sourcefiles are mentioned. The variable is prefixed with the value of bin_PROGRAMS (greetings). So Makefile.am looks like that: AUTOMAKE_OPTIONS = foreign bin_PROGRAMS = greetings greetings_SOURCES = hello.cpp That's it. You have to run aclocal first, otherwise autoconf will recognise AM_INIT_AUTOMAKE and will complain about an unknown macro. Aclocal will add the automake macros to the list of known macros for autoconf. Now run autoconf and afterwards automake -a. The argument '-a' will add all missing files to the directory. Again you can run ./configure which creates a Makefile. Now you can call make to compile the program and make clean to remove the object files. ===================== 11) Automake - Part 2 ===================== dir: automake2 Now let's come to a more complex example. The sources are in an extra directory named 'src'. Additionally, there will be a main.cpp, a printing.cpp and the corresponding header file printing.h. They look like that: src/main.cpp: #include "printing.h" int main() { printStr("Hello, World!"); return 0; } src/printing.cpp: #include "printing.h" void printStr(char *text) { cout << text << "\n"; } src/printing.h: #include using namespace std; void printStr(char *); In this example, main.cpp uses a function (printStr) which is defined in printing.cpp. The prototype is in printing.h. The function simply prints a string to stdout. As you can see, main.cpp depends on printing.h. So when building the binary and there where changes in printing.h then main.cpp and printing.cpp have to be rebuild. configure.ac has to be adjusted: AC_PREREQ(2.59) AC_INIT( greetings , 0.1 , hmacht@suse.de ) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([hello.cpp]) AC_PROG_CC AC_PROG_CXX AC_OUTPUT(Makefile src/Makefile) We added one more file to AC_OUTPUT, that is to say the Makefile in the src-subdirectory. The top-level Makefile.am contains only two lines: AUTOMAKE_OPTIONS = foreign SUBDIRS = src This tells automake to look in all mentioned subdirs (whitespace seperated) for further occurrances of Makefile.am's. The src/Makefile.am looks like that: bin_PROGRAMS = greetings greetings_SOURCES = hello.cpp printing.cpp printing.h The usual command sequenze: aclocal autoconf automake -a Now you can run ./configure and make and the program will be built. You also can try the dependencies. Modify printing.h and both printing.cpp and main.cpp will be rebuild and linked together. Only modifying main.cpp results in only rebuilding main.cpp and then linking together. So you have the same result then writing the Makefile on your own.