Bioinformatics Group
https://orcid.org/0000-0002-7926-5911
My (really) short and non-exhaustive introduction on 'How to publish source code using
the GNU autotools' (
autoconf,
automake,
... ).
This tutorial is mainly on distributing C++ source code. In case you want to distribute
C source just replace within the following examples the CXXFLAGS
setups with
CFLAGS
and set the build language mode from
AC_LANG([C++])
to AC_LANG([C])
.
This is version 2.0 of this page.
Many thanks for comments and feedback on older versions to
AC_PROG_C*
diff
to the autotools system
To build and distribute your source code with autotools you've got to write some simple additional files:
configure.ac
contains the pre-settings for your
configure
script (root folder)
which performs all the useful checks for your dependencies and is responsible
for the generation of the final Makefiles
of your project.
Makefile.am
that contains all basic information
needed to compiler and link your source code. It is the base for the final
Makefile
generated by the configure
script.
Usually one in the root folder and one for each source subdirectory.NEWS, README, AUTHORS,
Changelog
to distribute all additional information on your package.'--foreign'
to the
automake
call (see below)
A template configure.ac
file for a simple C++ program
without dependencies may look like that:
# this is example-file: configure.ac # initial information about the project AC_INIT([myProg],[2.0],[me@myProg.org]) # check if the source folder is available AC_CONFIG_SRCDIR([src/MYSOURCEFILE.cpp]) # check for C++ preprocessor and compiler AC_PROG_CXXCPP AC_PROG_CXX # automake initialization (mandatory) including a check for automake API version >= 1.9 AM_INIT_AUTOMAKE([1.9]) # use the C++ compiler for the following checks AC_LANG([C++]) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([string]) AC_CHECK_HEADERS([iostream]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T # distribute additional compiler and linker flags among Makefiles # --> set and change these variables instead of CXXFLAGS or LDFLAGS (for user only) AC_SUBST([AM_CXXFLAGS]) AC_SUBST([AM_LDFLAGS]) # files to generate via autotools (prepare .am or .in source files) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([src/Makefile]) # finally this generates the Makefiles etc. for the build AC_OUTPUT
All MY*
attributes have to be defined by the
YOU and are of global importance for the autotools/configure process.
Don't forget to change the project setup in the
AC_INIT(..)
call nor to update the source
file to check for within AC_CONFIG_SRCDIR(..)
.
Makefile.am
files are prepared
(top)'aclocal'
to generate the
m4
-files'autoconf'
to generate the
configure
script on the base of the
configure.ac
file'automake -a -c'
to generate all
still missing files with generic content (INSTALL
and COPYING
) and
to generate the Makefile.in
files that
are used by the configure
script to
generate the final Makefiles
'configure'
script in order to create the final
Makefiles
(you can set the install directory via the
--prefix=INSTALLPATH
parameter of the
script)'make'
to build your programs
and 'make install'
to install the program
in the usual or via the --prefix
parameter
of the configure
-script defined install path'make dist'
you can generate a
packed source code distribution, which you can provide for download
(e.g. with 'make dist-gzip'
in .tar.gz
format)
Download: an example project
myprog-2.0.tar.gz (containing
the source for one program placed in a
src
directory, plus the files
neccessary for the autotools)
For sharing your programming library the autotools are a powerful concept. Here I will give you an example for a simple C++ library..
Download: The example project mylib-2.0.tar.gz (containing the structured sources for the library and one depending program, plus the files neccessary for the autotools)
Assume we want to build a library 'mylib
'
containing a single class myClass
with header and source file. As every good library our
mylib
will provide its own namespace
and thus will be placed in an according subfolder.
Further we want to build and distribute a simple program helloWorld
based on this library.
Therefore, our initial set of source files comprises:
You would like to link all the object files of mylib
into a static library 'mylib.a' that will be installed together with
the neccessary header files into
a lib
and an include
subfolder (see 'make install'
above).
Also, you want to build automatically a packed version of your files
to distribute them (see 'make dist'
above).
Therefore, you should start to write a configure.ac
file:
We will use the template file from above, slightly extended.
# this is example-file: configure.ac # initial information about the project AC_INIT([mylib],[2.0],[me@myLib.org]) # check if the source folder is correct AC_CONFIG_SRCDIR([src/bin/helloWorld.cpp]) # Checks for programs # check for C++ preprocessor and compiler and the library compiler AC_PROG_CXXCPP AC_PROG_CXX AC_PROG_RANLIB # automake initialisation and check for minimal automake API version 1.9 AM_INIT_AUTOMAKE([1.9]) # use the C++ compiler for the following checks AC_LANG([C++]) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([string]) AC_CHECK_HEADERS([iostream]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T # distribute additional compiler and linker flags # --> set these variables instead of CXXFLAGS or LDFLAGS AC_SUBST([AM_CXXFLAGS]) AC_SUBST([AM_LDFLAGS]) # files to generate via autotools (.am or .in source files) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([src/mylib/Makefile]) AC_CONFIG_FILES([src/bin/Makefile]) # generate the final Makefile etc. AC_OUTPUT
Here the file is extended with AC_PROG_RANLIB
that checks if the ranlib tools are available to build the library
later. Also the relevant Makefiles
are
given to the AC_CONFIG_FILES
function.
For each of this files a Makefile.am
has to
be written.
Note: Beneath the Makefile
in the root
directory we plan to setup a separate Makefiles
for the
library (src/mylib/
directory) and the program
(src/bin/
directory).
In the following I give the simple Makefile.am
of the root directory.
# this is example-file: Makefile.am # the subdirectories of the project to go into SUBDIRS = \ src/mylib \ src/bin
Here only the important subdirectories have to be listed where other
Makefiles
files have to be called in order
to build the package.
Note: The order of the subfolders is of importance
for the compilation process. First we have to build the library to be ready
for the linking of the program within the src/bin/
directory.
Makefile.am
within the source folders
(top)# this is example-file: src/mylib/Makefile.am # additional include paths necessary to compile the C++ library AM_CXXFLAGS = -I$(top_srcdir)/src @AM_CXXFLAGS@ ############################################################################### # THE LIBRARIES TO BUILD ############################################################################### # the library names to build (note we are building static libs only) lib_LIBRARIES = libmylib.a # where to install the headers on the system libmylib_adir = $(includedir)/mylib # the list of header files that belong to the library (to be installed later) libmylib_a_HEADERS = \ myClass.hpp # the sources to add to the library and to add to the source distribution libmylib_a_SOURCES = \ $(libmylib_a_HEADERS) \ myClass.cpp ###############################################################################
Here we list the headers and source files of the library to build and distribute.
Note: The include path is given relative
to the root/source directory given by the global variable
top_srcdir
plus the local source subdirectory
name. The @AM_CXXFLAGS@
ensures the paste of
additional compiler flags set within the configure script!
(see debug support for an example)
Note: Non-alphanumeric characters of the
library name are presented with underscore '_' in the corresponding
variable names! (e.g. libmylib.a --> libmylib_a_SOURCES
)
Note: The headers variable is added to the source file
list of the library to ensure that they will be part of the source distribution
of the package.
# this is example-file: src/bin/Makefile.am # additional include paths necessary to compile the C++ programs AM_CXXFLAGS = -I$(top_srcdir)/src @AM_CXXFLAGS@ ############################################################################### # THE PROGRAMS TO BUILD ############################################################################### # the program to build (the names of the final binaries) bin_PROGRAMS = helloWorld # list of sources for the 'helloWorld' binary helloWorld_SOURCES = \ helloWorld.cpp # the additional libraries needed to link helloWorld helloWorld_LDADD = $(top_builddir)/src/mylib/libmylib.a $(AM_LDFLAGS) ###############################################################################
Here the programs source files are listed and the additional compiler
and linker flags are set.
Note: We use the _LDADD
variable to ensure the use of the package's mylib
library
when linking the binary. Note further, we give the full path relative to the
build directory to enable an automatic relinking in case the library is changed.
Note: All AM_*FLAGS
are ignored in preference to
a per-executable (or per-library) _*FLAGS
variable if it is defined.
(see automake manual)
Therefore, we have to ensure that additional linker flags set by the configure
script (see debug support for an example)
are used as well by adding the variable $(AM_LDFLAGS)
to the helloWorld_LDADD
explicitly!
Download: The example project mylib-2.0.tar.gz (containing the structured sources for the library and one program, plus the files neccessary for the autotools)
To support via autotools that the build process incorporates debug
information you can introduce the following lines into your
configure.ac
script.
########################################################################## # debug compilation support ########################################################################## AC_MSG_CHECKING([whether to build with debug information]) AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [enable debug data generation (def=no)])], [debugit="$enableval"], [debugit=no]) AC_MSG_RESULT([$debugit]) if test x"$debugit" = x"yes"; then AC_DEFINE([DEBUG],[],[Debug Mode]) AM_CXXFLAGS="$AM_CXXFLAGS -g -Wall -Werror -Wno-uninitialized -O0" else AC_DEFINE([NDEBUG],[],[No-debug Mode]) AM_CXXFLAGS="$AM_CXXFLAGS -O3" fi ##########################################################################
To get the changed compiler flags distributed among your final
Makefiles
you have to add the following
line somewhere afterwards:
AC_SUBST([AM_CXXFLAGS])
Note: You have to take care that the
AM_CXXFLAGS
variable won't be used for your
build targets if a target specific *_CXXFLAGS
is specified! You would have to add the variable
$(AM_CXXFLAGS)
to this flag manually!
(see example project comments)
AC_PROG_C*
(top)
When checking for the C or C++ compiler via AC_PROG_CC
or
AC_PROG_CXX
it automatically includes a test of the according
environment variable CFLAGS
or CXXFLAGS
that are user specific and derived from the environment.
Unfortunately, it is not only checking if the user has set the variable to provide
additional compiler information. In case the variable is specified, nothing happens to it.
But if not present, the check sets the variable automatically to '-g -O2'
(see automake manual).
Thus, the compiler will generate debug information and the optimization level is fixed to
'-O2'
. Since the CXXFLAGS
variable is
placed in the final compiler call behind our configure based
AM_CXXFLAGS
variable (e.g. for debug support),
any optimization level setup will be overwritten with '-O2'
.
This is the case because the compiler uses only the last optimization level set
(see gcc manual).
If we are not interested in this automatic compiler flag setup, we have to undo the changes manually.
At least I have not found another workaround for that...
# store current user given compiler flags to avoid default setup via AC_PROG_CXX OLD_CXXFLAGS=$CXXFLAGS # check for C++ preprocessor and compiler and the library compiler # (might change the compiler flags) AC_PROG_CXXCPP AC_PROG_CXX # reset compiler flags to initial flags CXXFLAGS=$OLD_CXXFLAGS
Often the build of a library or tool depends on external libraries. The
configure
script can be used to allow for the setup of their
(non-standard) install paths and to check if they are available.
configure
script
(top)########################################################################## # adding the XXX library (e.g. with static name 'libXXX.a') ########################################################################## # adding the lib to the files to link LIBS="-lXXX $LIBS" # introduce the optional configure parameter for a non-standard install prefix of XXX AC_ARG_WITH([XXX], [AS_HELP_STRING([--with-XXX=prefix], [try this for a non-standard install prefix of the XXX library])], [XXXPATHSET=1], [XXXPATHSET=0]) # if optional parameter used, extend path flags for compliler and linker if test $XXXPATHSET = 1 ; then # extend the compiler and linker flags according to the path set AM_CXXFLAGS="$AM_CXXFLAGS -I$with_XXX/include" AM_LDFLAGS="$AM_LDFLAGS -L$with_XXX/lib" fi ##########################################################################
Note: Linkers are sensitive to library ordering
so the order in which LIBS is generated is important!
Note: If the library is not mandatory to build your
source it might be better to set the LIBS
variable
within the tests (see linking tests for an example).
Don't forget to distributed the variables among your final
Makefiles
:
# distribute the changed variables among the Makefiles AC_SUBST([LIBS]) AC_SUBST([AM_CXXFLAGS]) AC_SUBST([AM_LDFLAGS])
When linking against a library, the simplest check you can do is for the availablity
of its header files.
Note: The check is done via a compilation attempt. Unfortunately,
the compiler call internally run does not use our AM_CXXFLAGS
.
Therefore, a possibly set non-standard install prefix won't be
used and the check will fail for sure. We can bypass the problem by temporarily merging
AM_CXXFLAGS
into the user defined CXXFLAGS
.
########################################################################## # check for XXX headers ########################################################################## # store current CXXFLAGS and merge with AM_CXXFLAGS for compilation check OLD_CXXFLAGS=$CXXFLAGS; CXXFLAGS="$AM_CXXFLAGS $CXXFLAGS" # check for XXX library headers AC_MSG_CHECKING([for the XXX library headers]) # try to compile a file that includes a header of the library XXX AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include]])], [AC_MSG_RESULT([found]) FOUND_XXX=1;], [AC_MSG_RESULT([not found]) FOUND_XXX=0;]) # reset original CXXFLAGS CXXFLAGS=$OLD_CXXFLAGS # handle results if test $FOUND_XXX != 1; then AC_MSG_NOTICE([]) AC_MSG_NOTICE([The XXX library was not found!]) if test $XXXPATHSET = 1 ; then AC_MSG_NOTICE([ The install prefix '$with_XXX' was set for the XXX library.]) AC_MSG_NOTICE([ --> Maybe wrong ???]) else AC_MSG_NOTICE([ No non-standard install prefix was set.]) AC_MSG_NOTICE([ --> You might want to use '--with-XXX=PREFIX' ?!?]) fi AC_MSG_NOTICE([]) AC_MSG_ERROR([XXX library is an essential dependency : cannot build and stop here !]) fi ##########################################################################
Note: Somewhere before the code given above you have to specify the compiler to use!
That is you have to insert some AC_LANG([...])
statement
(e.g. AC_LANG([C++])
as given in the example).
As for done for the headers, we will try to link a small program that calls a
function from the library in order to ensure we can use the library for our build.
Note: As done for the header check: we have to ensure
the usage of our configure
based AM_*FLAGS
.
Since the program has to be compiled first, take care of the AM_CXXFLAGS
as well!
# store current *FLAGS and merge with AM_*FLAGS for compilation and linker check OLD_CXXFLAGS=$CXXFLAGS; OLD_LDFLAGS=$LDFLAGS; CXXFLAGS="$AM_CXXFLAGS $CXXFLAGS" LDFLAGS="$AM_LDFLAGS $LDFLAGS" # ensure the library to check for is covered by the LIBS variable OLD_LIBS=$LIBS LIBS="$LIBS -lXXX" # try to link the function 'XXX_functionName' out of library XXX AC_MSG_CHECKING([whether the XXX library can be linked]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[#include]], [[XXX_functionName();]])], [AC_MSG_RESULT([yes]) FOUND_XXX=1;], [AC_MSG_RESULT([no]) LIBS=$OLD_LIBS; dnl reset to old value since XXX was not found FOUND_XXX=0;]) # reset original *FLAGS CXXFLAGS=$OLD_CXXFLAGS LDFLAGS=$OLD_LDFLAGS
or combine hierarchically with header check from above :
########################################################################## # check for XXX library ########################################################################## # store current *FLAGS and merge with AM_*FLAGS for compilation and linker check OLD_CXXFLAGS=$CXXFLAGS; OLD_LDFLAGS=$LDFLAGS; CXXFLAGS="$AM_CXXFLAGS $CXXFLAGS" LDFLAGS="$AM_LDFLAGS $LDFLAGS" # ensure the library to check for is covered by the LIBS variable OLD_LIBS=$LIBS LIBS="$LIBS -lXXX" # check for XXX library headers AC_MSG_CHECKING([for the XXX library headers]) # try to compile a file that includes a header of the library XXX AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include]])], [AC_MSG_RESULT([found]) # try to link the function 'XXX_functionName' out of library XXX AC_MSG_CHECKING([whether the XXX library can be linked]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[XXX_functionName();]])], [AC_MSG_RESULT([yes]) FOUND_XXX=1;], [AC_MSG_RESULT([no]) LIBS=$OLD_LIBS; dnl reset to old value since XXX was not found FOUND_XXX=0;])], [AC_MSG_RESULT([not found]) FOUND_XXX=0;]) # reset original *FLAGS CXXFLAGS=$OLD_CXXFLAGS LDFLAGS=$OLD_LDFLAGS # handle check results if test $FOUND_XXX != 1; then AC_MSG_NOTICE([]) AC_MSG_NOTICE([The XXX library was not found!]) if test $XXXPATHSET = 1 ; then AC_MSG_NOTICE([ The install prefix '$with_XXX' was set for the XXX library.]) AC_MSG_NOTICE([ --> Maybe wrong ???]) else AC_MSG_NOTICE([ No non-standard install prefix was set.]) AC_MSG_NOTICE([ --> You might want to use '--with-XXX=PREFIX' ?!?]) fi AC_MSG_NOTICE([]) AC_MSG_ERROR([ XXX library is an essential dependency : cannot build and stop here !]) fi ##########################################################################
Doxygen is a documentation system
for C++, C, Java, Objective-C, Python, IDL
(Corba and Microsoft flavors) and to some extent PHP, C#, and D. It
provides a generic machinery to generate a well formatted project
documentation directly from source code and external data. The
resulting documentation is available in serveral formats e.g. pdf,
html, chm, ...
To add support for the
doxygen system to the autotools
machinery, I am using a slightly changed version of the
Doxample
package of Oren Ben-Kiki. (It seems a more
recent version is available! But not integrated into this tutorial yet...)
It provides the complete additional stuff, that adds several
parameters to the configure
script to guide the possible documentation modes supported by doxygen.
The original scripts by Oren Ben-Kiki are provided and tested with
aclocal/automake-1.8
and
auto(header|conf)-2.59
. I used my slightly
adapted version successfully with
doxygen-1.5.5
,
aclocal/automake-1.10.2
, and
auto(header|conf)-2.63
.
You can get my updated version here:
Download: The
Doxample-v2.1.tar.gz (containing
the doxygen support files of Oren Ben-Kiki - slightly adapted)
(updated 090606)
'include $(top_srcdir)/aminclude.am'
to the Makefile.am
in the root folder'DX_INIT_DOXYGEN($PACKAGE_NAME, MYDOXYGENCONFIG)'
to your configure.ac
($PACKAGE_NAME
is automatically set by
AC_INIT representing theprojects name and
MYDOXYGENCONFIG
is the Doxample specific
doxygen config file of your project [e.g. doxygen.cfg out of the
Doxample archive in your root].)
configure
script by the functions
DX_HTML_FEATURE, DX_CHM_FEATURE, DX_CHI_FEATURE,
DX_MAN_FEATURE, DX_RTF_FEATURE, DX_XML_FEATURE, DX_PDF_FEATURE,
DX_PS_FEATURE
adding a line
DX_XXX_FEATURE(ON)
or
DX_XXX_FEATURE(OFF)
to your
configure.ac
before the
DX_INIT_DOXYGEN(..)
line.
'./configure --help'
you
should get some new 'Optional Features' concerning doxygen. You
can use them to guide the default doxygen output modes later.
(e.g. '--disable-doxygen-doc'
or
'--enable-doxygen-pdf'
)
'make doxygen-doc'
you finally
generate the doxygen documentation guided by the doxygen config
file. All documentations will be placed to a new subfolder named
doxygen-doc.
I will show how to add doxygen support on the
final library autotools example that was
introduced before.
Download: The example project
mydoxylib-2.1.tar.gz
for generating documentation with doxygen.
(updated 090606)
(containing
the structured example library from above including the incorporated Doxample files)
An example for source code documentation:
// this is example-file: src/mylib/myClass.hpp #ifndef MYLIB__MYCLASS_HPP_ #define MYLIB__MYCLASS_HPP_ #include#include /*! * Namespace that covers all classes of my example library. */ namespace mylib { /*! * This is a simple 'hello world' class part of the autotools examples. * * @author Martin Raden - http://www.bioinf.uni-freiburg.de/~mmann/ * */ class MyClass { protected: //! the home URL of this class std::string homeURL; public: //! construction MyClass(); //! destruction virtual ~MyClass(); /*! * Says hello to the world and prints the message to the given stream. * * @param out the stream to write the message to */ void sayHello(std::ostream & out) const; }; } // namespace mylib #endif /*MYLIB__MYCLASS_HPP_*/
Enable the definitions of the doxygen automake macros by adding
the following to the Makefile.am
in the root folder.
aminclude.am
is part of the
Doxample-v2.1.tar.gz provided.
# DOXYGEN SUPPORT include $(top_srcdir)/aminclude.am # ensure the distribution of the doxygen configuration file EXTRA_DIST = doxygen.cfg
Finally, the configure.ac
is extended by:
###################################################################### # DOXYGEN SUPPORT ###################################################################### DX_HTML_FEATURE(ON) DX_CHM_FEATURE(OFF) DX_CHI_FEATURE(OFF) DX_MAN_FEATURE(OFF) DX_RTF_FEATURE(OFF) DX_XML_FEATURE(OFF) DX_PDF_FEATURE(OFF) DX_PS_FEATURE(OFF) DX_INIT_DOXYGEN([$PACKAGE_NAME],[doxygen.cfg]) ######################################################################
Download: The example project
mydoxylib-2.1.tar.gz
for generating documentation with doxygen.
(updated 090606)
(containing
the structured sources for the library and one program, plus the files
neccessary for the autotools and the Doxample files)
diff
command (top)
Note:
The following test framework is bypassing the native autotool's test support.
It is a custom framework built to meet my personal requirements and does not cover all features
possible. Nevertheless, it might be sufficient for others as well or easy to extend.
Since it is not depending on autotool's test cases, it is compatible with older automake
versions (tested with 1.9).
Again, the code fragments given assume C++ compilation and linking!
In the following, I will describe a diff
-based test
system for a library or program to provide a possibility to check if the local
installation works fine or not.
The Unix diff
command allows the comparison of two files for differences. Therefore
it is a good choice for a generic test system.
tests
)Makefile.am
for your
tests
folder (see below)Makefile.am
file in the root directoryconfigure.ac
file make test
in the root folder to
start the automatic test suite in a first run. testXXX.lastout
to
testXXX.verified
make test
next time
(e.g. on the user's system), it will check for differences between
the current and your verified test output
A simple test testmylib_MyClass.cpp
for the MyClass
interface from the
mydoxlib example might look like this:
#include#include int main(int argc, char** argv) { std::cout << "\n==========================================" "\n testing mylib::MyClass" "\n==========================================" << std::endl; std::cout << " => constructing m = new mylib::MyClass()" << std::endl; mylib::MyClass *m = new mylib::MyClass(); std::cout << " => m->sayHello(std::cout) : " << std::endl; m->sayHello(std::cout); std::cout << " => destruction : delete m" << std::endl; delete m; std::cout << "\n==========================================\n" << std::endl; return 0; }
The Makefile.am
of the
tests
folder.
Note: The only 4 variables you have to touch
are given at the beginning : TESTSOURCES,
TESTCXXFLAGS, TESTLDFLAGS, TESTLIBS
# this is example-file: tests/Makefile.am ################################################################# # TEST CASES TO RUN : ################################################################# # set the list of all test routines to compile TESTSOURCES = \ testmylib_MyClass.cpp # set test specific compiler and linker options TESTCXXFLAGS = -I$(top_srcdir)/src TESTLDFLAGS = TESTLIBS = $(top_builddir)/src/mylib/libmylib.a ################################################################# # AUXILIARY VARIABLES FOR TEST BUILD AND EXECUTION ################################################################# # this ensure that test cases and verified outputs will be distributed EXTRA_DIST = $(TESTSOURCES) $(TESTSOURCES:.cpp=.verified) # final compiler and linker flag adds AM_CXXFLAGS = $(TESTCXXFLAGS) @AM_CXXFLAGS@ AM_LDFLAGS = $(TESTLDFLAGS) @AM_LDFLAGS@ LIBS = $(TESTLIBS) @LIBS@ TESTCXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) TESTCXXLINK = $(CXX) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ -o $@ DIFFS=$(TESTSOURCES:.cpp=.diff) ################################################################# # MAKE TARGETS FOR TEST BUILD AND EXECUTION ################################################################# tests: $(DIFFS) .cpp.diff: @name=$*; name=$${name/test/}; \ $(PRINTF) " Building test %-38s" $$name; @$(TESTCXXCOMPILE) -c -o $*.o $< @$(TESTCXXLINK) $*.o $(LIBS) -o $*.test @$(PRINTF) " --> execution :"; @./$*.test > $*.lastout @name=$*; name=$${name/test/}; \ if [ -e $*.verified ] ; then \ if $(DIFF) $*.verified $*.lastout >$@ ; then \ $(PRINTF) " OK\n"; \ else \ $(PRINTF) " FAILED\n";\ $(PRINTF) "------------------------------------------------------\n" ;\ $(PRINTF) " Diff for $$name (verified '<', current '>')\n" ;\ $(PRINTF) "------------------------------------------------------\n" ;\ $(CAT) $@ ;\ $(PRINTF) "------------------------------------------------------\n" ;\ $(PRINTF) " [ If new output is correct: mv $*.lastout $*.verified ]\n" ;\ $(PRINTF) "------------------------------------------------------\n" ;\ $(PRINTF) "\n" ;\ fi ;\ $(RM) $@ ;\ else \ $(PRINTF) " DONE\n"; \ $(PRINTF) "------------------------------------------------------\n" ;\ $(PRINTF) " Output of $$name :\n" ;\ $(PRINTF) "------------------------------------------------------\n" ;\ $(CAT) $*.lastout ;\ $(PRINTF) "------------------------------------------------------\n" ;\ $(PRINTF) " WARNING: verified output of $$name is missing. Please generate\n" ;\ $(PRINTF) " a file $*.verified that contains the verfied output of $$name.\n"; \ $(PRINTF) " [ If output is correct: mv $*.lastout $*.verified ]\n" ;\ $(PRINTF) "------------------------------------------------------\n" ;\ $(PRINTF) "\n" ;\ fi clean-local: $(RM) -rf $(TESTSOURCES:.cpp=.test) $(TESTSOURCES:.cpp=.o) \ $(TESTSOURCES:.cpp=.lastout) $(TESTSOURCES:.cpp=.diff) #################################################################
The root folder's Makefile.am
extension.
# directory that contains all tests TSTDIR = tests tests: all @$(PRINTF) "-------------------------------------------------------------------------------\n" @$(PRINTF) " Running the tests :\n" @$(PRINTF) "-------------------------------------------------------------------------------\n" @make test -s -C $(TSTDIR) @$(PRINTF) "-------------------------------------------------------------------------------\n" test: tests # ... ensure TSTDIR is added to SUBDIRS SUBDIRS = ... $(TSTDIR)
Since the Makefile.am
extension from above are
based on some additional program variables (DIFF, CAT, PRINTF
),
we have to extend the configure.ac
to check for
the programs the variable setups.
########################################################################## # Checks for programs needed for tests ########################################################################## # Check for 'diff' and get full path. AC_ARG_VAR([DIFF],[the 'diff' program to use for test output comparison]) AC_PATH_PROG([DIFF],[diff],[]) if test "x$DIFF" = "x"; then AC_MSG_NOTICE([==> diff command not found!]) AC_MSG_NOTICE([==> Set DIFF variable if present in non-standard path!]) AC_MSG_ERROR([diff is mandatory to run the tests : will stop here!]) fi # Check for 'cat' and get full path. AC_ARG_VAR([CAT],[the 'cat' program used for printing test output files]) AC_PATH_PROG([CAT],[cat],[]) if test "x$CAT" = "x"; then AC_MSG_NOTICE([==> cat command not found!]) AC_MSG_NOTICE([==> Set CAT variable if present in non-standard path!]) AC_MSG_ERROR([cat is mandatory to run the tests : will stop here!]) fi # Check for 'printf' and get full path. AC_ARG_VAR([PRINTF],[the 'printf' program used to print test information]) AC_PATH_PROG([PRINTF],[printf],[]) if test "x$PRINTF" = "x"; then AC_MSG_NOTICE([==> printf command not found!]) AC_MSG_NOTICE([==> Set PRINTF variable if present in non-standard path!]) AC_MSG_ERROR([printf is mandatory to run the tests : will stop here!]) fi ########################################################################## # [...] # ensure the build of the tests' Makefile AC_CONFIG_FILES([tests/Makefile]) ##########################################################################
Note: The variable definition via
AC_ARG_VAR
enables the setup of the variables
by the user (see 'configure --help'
output)
and includes the distribution of the variables among the
Makefiles
via an implicit
AC_SUBST
call.
The resulting test calls for the mydoxylib example could look like:
$ make tests [...] ------------------------------------------------------------------------------- Running the tests : ------------------------------------------------------------------------------- Building test mylib_MyClass --> execution : DONE ------------------------------------------------------ Output of mylib_MyClass : ------------------------------------------------------ ========================================== testing mylib::MyClass ========================================== => constructing m = new mylib::MyClass() => m->sayHello(std::cout) : Hello world, I am the example Program! I am part of the autotools examples by Martin Raden. http://www.bioinf.uni-freiburg.de/~mmann/ => destruction : delete m ========================================== ------------------------------------------------------ WARNING: verified output of mylib_MyClass is missing. Please generate a file testmylib_MyClass.verified that contains the verfied output of mylib_MyClass. [ If output is correct: mv testmylib_MyClass.lastout testmylib_MyClass.verified ] ------------------------------------------------------ ------------------------------------------------------------------------------- $ head -n 13 tests/testmylib_MyClass.lastout > tests/testmylib_MyClass.verified $ make test ------------------------------------------------------------------------------- Running the tests : ------------------------------------------------------------------------------- Building test mylib_MyClass --> execution : FAILED ------------------------------------------------------ Diff for mylib_MyClass (verified '<', current '>') ------------------------------------------------------ 13a14,17 > => destruction : delete m > > ========================================== > ------------------------------------------------------ [ If new output is correct: mv testmylib_MyClass.lastout testmylib_MyClass.verified ] ------------------------------------------------------ ------------------------------------------------------------------------------- $ mv tests/testmylib_MyClass.lastout tests/testmylib_MyClass.verified $ make tests [...] ------------------------------------------------------------------------------- Running the tests : ------------------------------------------------------------------------------- Building test mylib_MyClass --> execution : OK ------------------------------------------------------------------------------- $