I've been tasked with giving a legacy codebase some love. The actual code is written in C, but is not very relevant aside from the #includes each file makes. No, the bulk of this task involves GNU Autotools.

Project hierarchy

├── buildall
├── ChangeLog
├── configure.ac
├── include
│   ├── Makefile.am
│   └── skey.h
├── lib
│   ├── Makefile.am
│   ├── md4.c
│   ├── md4.h
│   ├── put.c
│   ├── skeylogin.c
│   └── skeysubr.c
├── Makefile.am
├── man
│   ├── key.1
│   ├── keyinit.1
│   ├── keysh.1
│   └── skey.1
├── NEWS
├── skey
│   ├── Makefile.am
│   └── skey.c
├── skeyinit
│   ├── Makefile.am
│   └── skeyinit.c
└── skeysh
    ├── Makefile.am
    └── skeysh.c


AC_INIT([skey], [2.0], [[email protected]])


# Checks for programs.

# Configure the compiler.
CFLAGS+=" -std=gnu99"

# Checks for libraries.
AC_CHECK_LIB([crypt], [crypt])

# Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h netdb.h netinet/in.h shadow.h stdlib.h string.h strings.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/systeminfo.h sys/time.h syslog.h termio.h termios.h unistd.h utmp.h utmpx.h])

# Checks for typedefs, structures, and compiler characteristics.
AC_CHECK_MEMBERS([struct stat.st_blksize])

# Checks for library functions.
AC_CHECK_FUNCS([alarm bzero endpwent ftruncate getcwd gethostbyaddr gethostbyname gethostname getpass getspnam gettimeofday getusershell inet_ntoa isascii memset mkdir putenv realpath rmdir setenv socket strcasecmp strchr strdup strerror strncasecmp strpbrk strrchr strstr sysinfo tzset])

AC_CONFIG_FILES([Makefile lib/Makefile include/Makefile skey/Makefile skeyinit/Makefile skeysh/Makefile])

Root Makefile.am

SUBDIRS=lib skey skeyinit skeysh include

man_MANS = man/skey.1

    cd lib && $(MAKE) all

skey: lib
    cd skey && $(MAKE) all

skeyinit: lib
    cd skeyinit && $(MAKE) all

skeysh: lib
    cd skeysh && $(MAKE) all

all: lib skey skeyinit skeysh

install: all
    cd lib && $(MAKE) install && cd ..
    cd skey && $(MAKE) install && cd ..
    cd include && $(MAKE) install && cd ..
    cd skeyinit && $(MAKE) install && cd ..
    cd skeysh && $(MAKE) install && cd ..
    $(INSTALL_DATA) $(man_MANS) $(mandir)
    $(INSTALL_DATA) README $(docdir)


lib_LIBRARIES = libskey.a
INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir) -I.
libskey_a_SOURCES = skeylogin.c skeysubr.c md4.c put.c

%.o: %.c
    $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@

$(lib_LIBRARIES): $(libskey_a_SOURCES)
    ar -rs $(lib_LIBRARIES) skeylogin.o skeysubr.o md4.o put.o

all: $(lib_LIBRARIES)

install: all
    $(INSTALL) $(lib_LIBRARIES) $(libdir)


bin_PROGRAMS = key
key_SOURCES = skey.c
man_MANS = ../man/key.1
key_LDADD = ../lib/libskey.a

INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir) -I.

%.o: %.c
    $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@

$(bin_PROGRAMS): $(key_SOURCES) $(key_LDADD)
    $(CC) -o $@ $(CFLAGS) $(INCLUDES) $(key_SOURCES) $(key_LDADD)
    chmod +x $(bin_PROGRAMS)

all: $(bin_PROGRAMS)

install: all
    $(INSTALL) $(bin_PROGRAMS) $(bindir)
    $(INSTALL_DATA) $(man_MANS) $(mandir)


include_HEADERS = skey.h ../config.h

    $(INSTALL) $(include_HEADERS) $(includedir)

I'm not gonna show the others because this post is long enough as it is. They're mostly variations of skey/Makefile.am.

My goals

The goal of what I have here is to compile a bunch of subprojects, and then install them (via make install). There are other targets I'm planning, but their details are not in the scope of this question.

I'm writing most of this code on my personal machine, which runs Kubuntu 15.10.

My questions

  • Some constructs in the various 'Makefile.am's may be redundant. How can I eliminate them?
  • I need to support nine different OSes, all Unix flavors (not necessarily Linux). Am I doing anything that hampers portability?
  • Are there Autotools best practices that I could be employing?
  • Can the command I use to compile and link the code be improved? What about the parts that link to the contents of lib (which is a static library)?
  • What standard/conventional make targets am I missing (besides clean, dist, and check, which are planned)?

It's entirely possible that I'm unknowingly doing extraneous checks in my configure.ac.


1 Answer 1


I see some things that may help you improve your program.

Don't use unneeded macros

The AC_C_CONST is only used to compensate for either a C compiler that doesn't fully support the const keyword or when a C++ compiler is used to compile C code. Since CFLAGS has -std=gnu99, it's clear that this project is assuming either gcc or clang, both of which have long supported const, rendering the use of AC_C_CONST unnecessary.

Avoid gnu-isms in makefiles

If your goal is to have truly portable makefiles, you can't assume that make is necessarily gnu make. For that reason, dependency rules of this form:

%.o: %.c

Should be written using the older portable syntax instead:


However, see the next suggestion.

Don't write more than you need

The implicit rule for .o files mentioned above is exactly the same as is already built-in, so it's not needed at all and should be omitted. In a similar vein, all of the rules in lib/Makefile.am and all of the other makefiles should simply be deleted.

Don't use chmod when not needed

In skey/Makefile.am the rule for $(bin_PROGRAMS) explicitly invokes chmod to set the executable bit. This is not needed or wanted because the bin_ macro automatically performs this function.


The INCLUDES form is an old name. Modern makefiles should instead use AM_CPPFLAGS. The syntax is otherwise identical.

Use AM_PROG_AR for the archiver

Don't use ar directly in lib\Makefile.am. Instead, you should use $(AR) $(ARFLAGS). For this program, you should set ARFLAGS and add AM_PROG_AR to configure.ac. Then you can (and should) omit the rule to build the library, as mentioned above. The final version of lib/Makefile.am should look like this:

lib_LIBRARIES = libskey.a
INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir) -I.
libskey_a_SOURCES = skeylogin.c skeysubr.c md4.c put.c

Purge the code of deprecated utilities

The use of bzero is deprecated. It should be replaced, wherever it occurs in the code, with memset instead.

Use autoreconf to check your build

You can get autoreconf to tell you a lot of what I just did by running autoreconf -Wall which will emit warnings and errors for all kinds of portability problems and configuration errors.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.