1. Overview

In this tutorial, we’ll learn the differences between Makefile.am and Makefile.in, and their roles in the configuration process. Many programmers are aware of the importance of M**akefiles and know how to use them to group their C programs together and build an executable.

However, M**akefile has its own problem – it cannot handle building dependencies across machines, as machines tend to have different environment settings. We need some high-level tools to configure our software and this tool uses some other files such as Makefile.in and Makefile.am, which can be confusing.

2. Automake and the Software Configuration Process

When we build a program that’s meant to run on different systems, we may need different file dependencies to compile and generate the executable. As long as the project files don’t call the hardware-level methods, we should be able to make a copy of the source code onto another machine.

Immediately after that, we need to configure, compile and install the program using the Autotools from the GNU Build System. Apparently, there exist many other build systems, but only the GNU version uses the Makefile.am and Makefile.in files.

It would be impossible to discuss the entire GNU Build System in this article, so we’ll only focus on the Automake, including how it works and what files it needs. In fact, the building process itself is very simple if we type the required commands on the command prompt.

3. Why Do We Need Automake?

When we want to make an executable, we often rely on Makefiles to control our dependencies. The installer calls the Makefiles to build the executable for us.

However, if we only use the installer alone, we may find that we have to hand-write the Makefile. This may cause dependency problems in large-scale projects with hundreds of intertwined dependencies. Developers may forget to change, or modify the incorrect dependency requirements, and the installer may generate glitched executables, or even refuse to compile.

Therefore, we need another tool, Automake, to dynamically handle the Makefile and to make them portable between machines.

4. How Does Automake Work?

There are many other tutorials focusing on the Automake as a whole, and we don’t need to focus on all of them. The only thing we need to know today is that to create all the Makefile.in files for a package, Autoconf automatically scans configure.ac to locate each appropriate Makefile.am. This program assumes that only one configure.ac is in each directory. If multiple configure.ac files exist, then we need to run A**utomake in each directory holding one configure.ac file.

It’s also possible to give Automake arguments with .am appended to the arguments. GNU guidelines recommend this only for rebuilding outdated Makefile.in. In this case, we need to make sure to run Automake at the topmost directory, because it still needs to scan configure.ac to find the required Makefile.am.

5. How to Create Files Required by Automake

Suppose we’d like to create a project but we don’t actually know how much configuration we need for our machines. We’ll demonstrate and understand this with a simple Hello World program from the GNU website. All we need is a simple package that only comes with five files. Let’s create them right away.

First, let’s create main.c, the source file of our program, which is called “hello“. Here we’d like to put this file into the src folder, instead of the root directory. Doing so can help us manage the files and expand the directory:

$ cat main.c
#include <config.h>  
#include <stdio.h>

int main(void)
{
  puts ("Hello World!");
  puts ("This is " PACKAGE_STRING ".");
  return 0;
}

Second, let’s create the README file, which contains a very brief documentation:

$ cat README
This is a demonstration package for GNU Automake.
Type 'info Automake' to read the Automake manual.

Next, we have the Makefile.am and src/Makefile.am files with Automake instructions for two of the major directories:

$ cat src/Makefile.am
bin_PROGRAMS = hello
hello_SOURCES = main.c

$ cat Makefile.am
SUBDIRS = src
dist_doc_DATA = README

Finally, we have the configure.am file, which contains information for Autoconf to generate the configure script:

$ cat configure.am
AC_INIT([Project_Name], [1.0], [[email protected]])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([
 Makefile
 src/Makefile
])
AC_OUTPUT

All we need are these five files for this small project. Larger projects may require more files, such as more Makefile.am files for other directories.

6. Configuration Process

Once we create these files, we can locate the project root directory in the terminal and generate new installation files:

$ autoreconf --install
configure.ac:3: installing './compile'
configure.ac:2: installing './install-sh'
configure.ac:2: installing './missing'
src/Makefile.am: installing './depcomp'

After this command finishes on its own, we can find a whole bunch of files appearing in our directory, including the Makefile.in files. Let’s forget about other files and only focus on the Makefile.am and Makefile.in files in our directory, to avoid a possible digression.

We can notice that there are actually two Makefile.in files in both the project root folder and the /src folder. These files were generated according to the Makefile.am files in their directories, respectively. If we compare the corresponding Makefile.am and Makefile.in we notice that they look very different:

$ cat Makefile.am
SUBDIRS = src
dist_doc_DATA = README

$ cat Makefile.in
# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
...
VPATH = @srcdir@
am__is_gnu_make = { \
  if test -z '$(MAKELEVEL)'; then \
    false; \
  elif test -n '$(MAKE_HOST)'; then \
    true; \
  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
    true; \
  else \
    false; \
  fi; \
}
...
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.

We see that Makefile.am only tells what subsidiary directories we have, with the README file included as documentation.

On the other hand, Makefile.in looks extremely complicated, with loads of statements, comments, and codes similar to the style of Bash scripts.

In fact, Makefile.am is a script to tell the configuration tool the dependencies of the folder. Automake, or specifically autoconf, scans the configure.ac file and the Makefile.am files to understand the dependencies of the project. Then, it uses that information to generate the Makefile.in files, which contain the parameters required by the actual configuration process.

Let’s not abandon our progress right here. We still need to actually configure the environment for our program based on each individual machine. It’s time to run ./configure to start this process. The output may vary between machines:

$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... no
checking for mawk... mawk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
...
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating config.h
config.status: executing depfiles commands

At this step, we can see the Makefile and config.h header files already appear in our directories.

The last thing we need to do is simply to call the make command and run the program:

$ make
...
$ ./src/hello
Hello World!
This is Project_Name 1.0.

Now, we’ve successfully configured our program according to our machine environment. We only added four extra files without digging through the dependency mayhem.

7. Conclusion

In this article, we briefly discussed the differences between Makefile.am and Makefile.in by seeing their roles in the configuration process.

We learned that Makefile.am is a script designed to feed Autoconf to provide dependency information as a higher-level yet more readable abstraction. Then, Autoconf uses this information to generate Makefile.in files, which contain scripts and information for the actual configuration process.

We have understood the differences between Makefile.am and Makefile.in files, and their roles in the entire configuration process. This combination helps us save precious time as we don’t need to dig through the pile of configuration mayhem anymore.