How to Set Up a Project With Autotools

2022-02-01

Tags:
Categories:

Let's see how to set up GNU Autotools for building a new project!

Today we're starting a new project written in the C language.

The project will have a dependency to make it more interesting. We really can use any library, and for this example we'll pick the SDL library.

Last but not least, our project needs a name. Since libSDL is usually powering games, we could imagine our project is a game. Why not call it "Aventura"?

Prerequisites

Before starting, if you're not familiar with Autotools, take some time and go through the Autotools tutorial, an excellent slideshow by Alexandre Duret-Lutz.

Then we need to install all the packages we need.

Let's make sure we have Autotools. For Debian, that can be done with:

sudo apt install autoconf automake libtool gettext

Also, since we picked SDL as a project dependency, let's install the SDL development package:

sudo apt install libsdl2-dev

The implementation

  1. Prepare the directory structure. We need a src directory for the source code, where we begin with a single file, main.c.
    	
    .
    └── src
        └── main.c
    	
          
  2. Edit src/main.c and add the following code, which attempts to initialize the video subsystem of SDL and then cleans up and exits. It's like "Hello, World!" for SDL.
    #include "SDL.h"
    #include <stdio.h>
    
    int
    main (void)
    {
      if (SDL_Init (SDL_INIT_VIDEO) < 0)
        {
          fprintf (stderr, "SDL could not be initialized. SDL_Error: %s\n",
    	       SDL_GetError ());
          return 1;
        }
      atexit (SDL_Quit);
      return 0;
    }
    

    How would we compile this without Autotools?

    A first attempt might be:

    gcc -o src/aventura src/main.c
    

    However that would not be enough, because SDL.h is most likely not located directly in a standard include directory (such as /usr/include). It's probably under /usr/include/SDL2.

    You could specify the include path with -I:

    gcc -I/usr/include/SDL2 -o src/aventura src/main.c
    

    It would compile, but after that you'd get linker errors. You would need to explicitly link against SDL2 with -lSDL2:

    gcc -I/usr/include/SDL2 -o src/aventura src/main.c -lSDL2
    

    An easier and more portable way of getting the build flags in place would be to use pkg-config.

    First we would call pkg-config --list-all and look for "sdl" in its output:

    pkg-config --list-all | grep -i sdl
    

    The above would tell us that the name of the package for SDL is sdl2, and we could use that name to get the build flags:

    pkg-config --cflags --libs sdl2
    

    Then we could use the output of pkg-config in the call to gcc:

    gcc -o main $(pkg-config --cflags --libs sdl2) src/main.c
    

    That's better, but at this point it's already too complicated to compile directly (and we just started). Better leave this to the build system.

  3. Let's add one Makefile template (named Makefile.am) in the root directory and another one in src.

    touch {.,src}/Makefile.am
    

    Now the project tree should look like this:

    	
    .
    ├── Makefile.am
    └── src
        ├── main.c
        └── Makefile.am
    	
          
  4. Edit the top level Makefile.am and add the following line, which tells Automake which subdirectories to build (in our case, src).
    	
    SUBDIRS = src
    	
          
  5. Edit src/Makefile.am and add the following two lines, which tell Automake the names of the binaries we want to build (in our case, aventura) and the lists of source files to compile for each of these binaries (main.c for now).
    	
    bin_PROGRAMS = aventura
    aventura_SOURCES = main.c
    	
          
  6. Run autoscan.

    This will produce configure.scan, which is can be used as a starting point for configure.ac:

    	
    #                                               -*- Autoconf -*-
    # Process this file with autoconf to produce a configure script.
    
    AC_PREREQ([2.71])
    AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
    AC_CONFIG_SRCDIR([src/main.c])
    AC_CONFIG_HEADERS([config.h])
    
    # Checks for programs.
    AC_PROG_CC
    
    # Checks for libraries.
    
    # Checks for header files.
    
    # Checks for typedefs, structures, and compiler characteristics.
    
    # Checks for library functions.
    AC_CHECK_FUNCS([atexit])
    
    AC_CONFIG_FILES([Makefile
                     src/Makefile])
    AC_OUTPUT
    	
          
  7. Rename configure.scan to configure.ac.
  8. Edit configure.ac:
    • Fill the values for the project name, the version and the bug report address
    • Call AM_INIT_AUTOMAKE
    • Call PKG_CHECK_MODULES to define the dependency. This macro is an interface between autoconf and pkg-config. We already know the name of the package is sdl2.
    5c5,6
    < AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
    ---
    > AC_INIT([aventura], [0.1], [someone@example.com])
    > AM_INIT_AUTOMAKE([foreign -Wall -Werror])
    12a14,20
    > PKG_CHECK_MODULES(
    >   [SDL2],
    >   [sdl2 > 2.0.3],
    >   [CFLAGS="$CFLAGS $SDL2_CFLAGS"
    >    LIBS="$SDL2_LIBS $LIBS"],
    >   AC_MSG_WARN($SDL2_PKG_ERRORS)
    > )
    
  9. Run autoreconf --install

Now everything is ready. You can run ./configure followed by make.

The executable is ./src/aventura.