Swanson Technologies

Autotools and bash-completion scripts

Whenever a command-line utility has a lot of options, it's helpful to have automatic command-line tab-completion support for those in the shell. I recently tried adding this support to the libbitcoin-explorer project, but found that the whole process is rather arcane and undocumented. This guide will hopefully shed some light on things, at least for your typical autotools-based open-source project.

The first task is to actually write the bash completion script. Fortunately, there is already a nice guide for how to do this:

Source location

Once you have your script, the first question is where to put it in the source tree. While there isn't any documentation on this, the best place seems to be somewhere inside the data directory. This is the standard location for ancillary support files like icons, stylesheets, desktop entries, and such, so a command-line completion script fits right in.

Autoconf

Now that the file exists, the next step is to integrate it with the build system so make install actually puts it somewhere on the user's sytem. The problem is, that "somewhere" is different from one distro to the next, and some distros don't even have bash. There is no solution that makes everyone happy, so the best compromise is to let the user control this via the configure script.

Autoconf options come in two standard flavors. "With" options control integration with external software, like optional libraries. If a program can use libfoo to provide enhanced features, the user should be able to configure that with options like --with-libfoo or --without-libfoo. "Enable" options, on the other hand, turn optional parts of the program on or off at compile time. Options like --enable-utilites or --disable-utilites, for example, might determine whether or not some optional utilites are built.

Since bash is an external program, we definitely want a "with" option. I've seen a few packages that provide an --enable-bash-completion option, but this is a mistake! The correct, semi-standard configure option seems to be --with-bash-completion-dir. To add this flag to the configure script, put the following lines in configure.ac:

AC_ARG_WITH([bash-completion-dir],
    AS_HELP_STRING([--with-bash-completion-dir[=PATH]],
        [Install the bash auto-completion script in this directory. @<:@default=yes@:>@]),
    [],
    [with_bash_completion_dir=yes])

Once this block of code runs, a variable called with_bash_completion_dir will exist with one of three possible values:

Now that this option exists, the configure script needs to do something with it. The first step is turn a "yes" answer into a default location:

if test "x$with_bash_completion_dir" = "xyes"; then
    PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
        [BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"],
        [BASH_COMPLETION_DIR="$datadir/bash-completion/completions"])
else
    BASH_COMPLETION_DIR="$with_bash_completion_dir"
fi

This code snippet uses the bash-completion package to determine the default install location if that package happens to be available. Otherwise, the snippet uses $datadir/bash-completion/completions, which is a reasonable default location for many modern distros.

The only thing left for the configure script to do is to pass these options to the makefile:

AC_SUBST([BASH_COMPLETION_DIR])
AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"])

Automake

Compared to the configure script, the makefile is easy:

if ENABLE_BASH_COMPLETION
bashcompletiondir = $(BASH_COMPLETION_DIR)
dist_bashcompletion_DATA = data/your-script
endif

This code snippet goes in your top-level Makefile.am. If you are still using old-fashioned recursive make, you could also put it in your data/Makefile.am sub-file. In either case, be sure to change data/your-script to the correct relative path for your bash completion script.