Home | Research | HowTos Martin Raden, nee Mann

Martin Raden, nee Mann Bioinformatics Group
Institute for Computer Science
Albert-Ludwigs-University of Freiburg
Georges-Koehler-Allee 106
D-79110, Freiburg, Germany

Phone: +49 (0)761 203-8254
Fax: +49 (0)761 203-7462
Room: 106-02-012

email as png email as png Martin Raden @ ORCIDhttps://orcid.org/0000-0002-7926-5911


HowTo Autotools - Distributing source code with autoconf and automake

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



content

additional links

general settings (top)

To build and distribute your source code with autotools you've got to write some simple additional files:

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(..).

the normal procedure if all Makefile.am files are prepared (top)

example program (top)

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)

automake for libaries (top)

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)

example library structure (top)

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:

files in root directory (top)

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!

final library structure ready to build via autotools (top)

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)

Add Debug support (top)

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)

Prohibit the automatic setup of debug compiler options via 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

External library dependencies (top)

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.

Adding install path support to the 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])

Check for header files of the library (top)

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).

Linking check for libraries (top)

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 support (top)

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)

The steps to do (top)

Example with doxygen support (top)

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)

non-standard test support using 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.

What to do? (top)

Example (top)

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
-------------------------------------------------------------------------------
$
Created by Martin Raden, nee Mann, last updated in May, 2017.