Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: March 18, 2024
As we know, Linux is available in a variety of distributions. All of them are built on top of the Linux kernel. Still, each distribution can vary in terms of modules and packages. In addition, users can change their system to such an extent that it becomes very different from the default installation. So, it’s important that a program installer should address these factors.
In this tutorial, we’ll use the autotools package to create an installer. In other words, we’ll create a build system that uses the make and make install commands. There are several steps we’ll be going through:
In the end, we’ll have a package ready to be installed on other Linux systems.
To begin with, we’ll write a small C program. This program calculates the Fibonacci sequence. We enter an index and it returns the related Fibonacci number. The program consists of a source code file and a header file.
Initially, we’ll create a new directory with the name fibonacci and make it the current working directory:
$ mkdir fibonacci
$ cd fibonacci
This is the root directory of the package that we’re creating. Under this directory, we’ll create the src sub-directory to save our code.
Now, let’s examine the fibonacci.c file that contains the program’s code:
$ cat src/fibonacci.c
#include <stdio.h>
#include <stdlib.h>
#include "fibonacci.h"
int main( int argc, char* argv[] ) {
printf( "F%d: %d \n", atoi( argv[1] ),f( atoi(argv[1] )) );
}
int f( int n ) {
if (n <= 1 ) return n;
return f( n - 1 ) + f( n - 2 );
}
Indeed, we can see two functions:
Next, let’s examine the fibonacci.h header file:
$ cat fibonacci.h
int f( int n );
As we can see, the header file contains the declaration of the f() function.
To create the build system, we’ll use the autoconf and automake packages.
In Ubuntu, we can install both packages with apt-get:
$ sudo apt-get install autoconf automake
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
...
In addition, we should install a C compiler, in case our system doesn’t already have one:
$ sudo apt install gcc
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
...
Now, we’re ready to move on and begin creating the installer.
The main aim of the autoconf tool is to create a configure script. This is the script that creates a tailored Makefile for the end user’s system. Moreover, the autoconf tool requires a configure.ac file at the top-level directory of the program. The main purpose of this file is to describe the components of our program.
On the other hand, the main aim of the automake tool is to create the Makefile.in files. The configure script uses these files to create Makefile. Furthermore, automake also uses the Makefile.am files as input.
In brief, we’ll first create the Makefile.ac files. Then we’ll use the autoscan tool to generate a configure.ac template that we’ll edit to meet our needs.
Firstly, we should create the main Makefile.am file in our top-level directory:
$ cat Makefile.am
SUBDIRS = src
As we can see, the SUBDIRS variable points to the src directory. Thus, automake will search src for other Makefile.am files.
So, let’s create Makefile.am in src:
$ cat ./src/Makefile.am
bin_PROGRAMS = fibonacci
fibonacci_SOURCES = fibonacci.c fibonacci.h
The bin_PROGRAMS variable indicates two things:
Furthermore, the fibonacci_SOURCES variable holds a list of the source files of the fibonacci program separated with space.
Although we can create a configure.ac file from scratch, we’ll use the autoscan command to create a template instead. Then, we can edit the template to meet our needs.
First, we call the autoscan command in the top-level directory of our package:
$ autoscan
$ ls
autoscan.log configure.scan src
Indeed, we can see two new files:
Next, let’s see the initial contents of configure.scan:
$ cat configure.scan
# -*- 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/fibonacci.h] )
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_CONFIG_FILES( [Makefile src/Makefile] )
AC_OUTPUT
As we can see, the file contains a set of macros that comply with the M4 macros language:
In addition, we can see that the tool found the two Makefile.in files that we created previously and added them to the AC_CONFIG_FILE macro.
Next, we edit the configuration.scan file by including the actual package name, version, and similar data. A key point here is to add the AM_INIT_AUTOMAKE macro. The AM_INIT_AUTOMAKE macro enables the creation of Makefile.in by the automake tool.
As a result, the configure.scan file is complete:
$ cat configure.scan
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ( [2.71] )
AC_INIT( [fibonacci], [1.0], [[email protected]] )
AM_INIT_AUTOMAKE( [-Wall -Werror foreign] )
AC_CONFIG_SRCDIR( [src/fibonacci.h] )
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_CONFIG_FILES( [Makefile src/Makefile] )
AC_OUTPUT
Finally, we rename configure.scan to configure.ac.
At this point, we’re ready to run the autotool commands for creating the build system.
First, we call the aclocal command that creates the aclocal.m4 file:
$ aclocal
$ ls
Makefile.am aclocal.m4 autom4te.cache autoscan.log configure.ac src
Indeed, we can see that the command created an aclocal.m4 file. This file contains all macros required by autoconf.
Next, we run the autoheader command:
$ autoheader
$ ls
Makefile.am aclocal.m4 autom4te.cache autoscan.log config.h.in configure.ac src
Here, we can observe that the command produced the config.h.in file. This is a file that holds the C macros that the configure script uses.
After this, we run automake:
$ automake --add-missing
configure.ac:11: installing './compile'
configure.ac:6: installing './install-sh'
configure.ac:6: installing './missing'
src/Makefile.am: installing './depcomp'
In this example we used the –add-missing option that creates any missing files.
Finally, let’s run the autoconf command:
$ autoconf
$ ls
Makefile.am Makefile.in aclocal.m4 autom4te.cache autoscan.log compile config.h.in configure configure.ac depcomp install-sh missing src
Indeed, we can see a configure script in the output. At this point, the build system is ready. This means that we can create an archive of the directory, and provide it to end users.
In the previous section, we used several commands to create the build system. However, we can just run the autoreconf command instead of them:
$ autoreconf --install
configure.ac:11: installing './compile'
configure.ac:6: installing './install-sh'
configure.ac:6: installing './missing'
src/Makefile.am: installing './depcomp'
The autoreconf command performs all the required steps to create the build system.
Let’s verify that the package is ready to install via the usual configure–make–install procedure:
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether the build environment is sane... yes
...
$ make
make all-recursive
make[1]: Entering directory '/home/ubuntu/fibonacci'
...
$ sudo make install
Making install in src
...
$ fibonacci 8
F8: 21
Indeed, we installed fibonacci on our system. The program was copied to the default bin directory.
In this article, we saw a complete example of how to create a build system with GNU autotools. Through this example, we learned the basic steps of creating an installer with GNU autotools.