Invent wheels with Autotools & C
This how-to guide will teach you how to invent wheels with Autotools & C. Note that it isn’t detailed, just to give you some ideas how the whole system works.
Introduction
Requirements
My Autotools versions are:
- Automake 1.16.1
- Autoconf 2.69
- Libtool 2.4.6
And I’m on OS X. Installation guide will not be included.
Product
We’ll make a simple C lib (C++ compatible) called libts helps you to get time duration between two function calls.
Procedures
Time to actually make something!
A simple lib
Our project starts simply like this, including a header file and a source file:
1
2
3
4
5
.
├── ts.c
└── ts.h
0 directories, 2 files
And codes are shown below:
ts.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef __TS_H__
#define __TS_H__
#include <sys/time.h>
#include "config.h" /* This file will be generated later */
/* For C++ compatiblity */
#ifdef __cplusplus
extern "C" {
#endif
#define FIRST_CALL -1.0
/*
* Returns the time passed in seconds before the latest call.
* If it's the first time called, return FIRST_CALL.
*/
extern double getTimeDuration(void);
/* End of the extern "C" above */
#ifdef __cplusplus
}
#endif
#endif /* __TS_H__ */
ts.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include "ts.h"
double getTimeDuration()
{
static double latest = 0; /* Last call */
double sec; /* Current time in second */
double ret; /* Return value */
#if HAVE_GETTIMEOFDAY /* This macro comes from config.h */
/* In some specific OS, gettimeofday() is available */
/* See https://man7.org/linux/man-pages/man2/gettimeofday.2.html */
struct timeval tv;
gettimeofday(&tv, NULL);
sec = tv.tv_sec;
sec += tv.tv_usec / 1000000.0;
#else /* HAVE_GETTIMEOFDAY */
/* Or we can use time() instead. */
sec = time(NULL);
#endif /* HAVE_GETTIMEOFDAY */
ret = sec - latest; /* Calculate difference */
latest = sec; /* Update latest */
if (ret == sec) /* First call, return special value */
return FIRST_CALL;
else
return ret;
}
Now the source code is done. Let’s setup Autotools!
Autotools
Autotools is a complicated build system. We have to create several files.
Note: The following commands are run at the root of the source code.
configure.ac
configure.ac is a file for Autoconf to generate an configure script. It checks availability (in our example, if gettimeofday() and time() are available) and generates Makefile from Makefile.in, which will be generated later.
Let’s start with an autoscan GNU provided. It scans your code and generates an configure.ac automatically.
1
autoscan
The directory should be like this:
1
2
3
4
5
6
7
.
├── autoscan.log
├── configure.scan
├── ts.c
└── ts.h
0 directories, 4 files
The autoscan.log can be removed safely. What matters is configure.scan. We have to rename it to configure.ac first:
1
2
rm -f autoscan.log
mv configure.scan configure.ac
configure.ac looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([ts.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([sys/time.h])
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CHECK_FUNCS([gettimeofday])
AC_OUTPUT
It’s actually a piece of m4 language, and all those AC_XXX stuffs are macros and will be expanded into bash scripts. You can write bash in the configure.ac directly as well.
As you can see, it’s smart to include AC_CHECK_FUNCS([gettimeofday]). This will checks if gettimeofday is available. Magic! But, we have to modify it anyway.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69]) # 1
AC_INIT([libts], [0.1], [username@example.com]) # 2
AC_CONFIG_SRCDIR([ts.c]) # 3
AC_CONFIG_HEADERS([config.h]) # 4
AM_INIT_AUTOMAKE # Modified 5
# Checks for programs.
AC_PROG_CC # 6
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([sys/time.h]) # 7
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CHECK_FUNCS([gettimeofday]) # 8
LT_INIT # Modified 9
AC_CONFIG_FILES([Makefile]) # Modified 10
AC_OUTPUT # 11
- 1: Checks the minimal version of
autoconf. - 2 & 11: Start and end of every
configure.ac. It also includes some info for your project. - 3: Check if the source code exists.
- 4: Generates the configuration header named
config.h. - 5: Prepare for generating
Makefile. - 6: Determine a C compiler to use.
- 7: Check if header file
sys/time.his available. - 8: Check if function
gettimeofdayis available. - 9: Initialize Libtool. This will be used later.
- 10: Generate
MakefilefromMakefile.in, which will be generated later.
And to generate the configure file:
1
2
3
aclocal
autoconf
autoheader
And your project will be like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.
├── aclocal.m4
├── autom4te.cache
│ ├── output.0
│ ├── output.1
│ ├── output.2
│ ├── output.3
│ ├── requests
│ ├── traces.0
│ ├── traces.1
│ ├── traces.2
│ └── traces.3
├── config.h.in
├── configure
├── configure.ac
├── ts.c
└── ts.h
Makefile.am
Makefile.am is a file for automake to generate the Makefile.in mentioned above. Now create a Makefile.am and write the following stuffs:
AUTOMAKE_OPTIONS = foreign
include_HEADERS=ts.h
lib_LTLIBRARIES = libts.la
libts_la_SOURCES=ts.c
The build target is libts.la, containing the source file ts.c, which uses Libtool to sustain portability. It’s simpler than configure.ac.
Also, note that AUTOMAKE_OPTIONS is set to foreign, so it won’t force us to create those NEWS, AUTHOR, ChangeLog, etc.
To generate Makefile.in, run:
1
2
libtoolize # Generate supporting files for Libtool
automake --add-missing
Tests
Tests are always needed. Let’s do this in Autotools’ way. First create test.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "ts.h"
#include <assert.h>
#include <stdio.h>
/* Program exits with 0 means tests has passed */
int main(int args, char *argv[])
{
double first = getTimeDuration();
assert(first == FIRST_CALL);
double second = getTimeDuration();
assert(second != FIRST_CALL);
puts("OK");
return 0;
}
You can use modern test frameworks too.
Makefile.am:
# Lib
AUTOMAKE_OPTIONS = foreign
include_HEADERS=ts.h
lib_LTLIBRARIES = libts.la
libts_la_SOURCES=ts.c
# Tests
TESTS = checkTS
check_PROGRAMS = checkTS
checkTS_SOURCES = test.c
checkTS_LDFLAGS = libts.la
Also, if you don’t want to type the above aclocal and stuff again:
1
autoreconf -i
configure & build
Simple! Everything is ready now. Do this as usual:
1
2
./configure
make
And to install:
1
sudo make install
To test:
1
make check
To make a distribution package:
1
make dist
Whoa, you did that! To use this lib in your own programs, just #include <ts.h> and link this library (-lts)!
Product
This demo’s distribution can be found here.