Build processes can be frustratingly brittle. If you find yourself making predictable, repetitive changes to your build procedure to suit a given environment, Autoconf may be for you.
January 01, 2006
URL:http://www.drdobbs.com/gnu-autoconf/184402060
Ethan McCallum is a freelance technology consultant who spends a lot of time with Linux, C++, and Java. He can be reached at ethanqm@ penguinmail.com.
Although we design software to be flexible and adaptable at runtime, our build processes are often still rigidonce a project moves beyond the most trivial of examples or stub tests, changes to the build environment cause a snowball effect throughout the directory tree. Addressing headaches like this, GNU Autoconf (http://www.gnu.org/software/autoconf/) provides a uniform interface to creating your makefiles at build time. If you find yourself making predictable, repetitive changes to your build procedure to suit a given environmenteven through a homegrown sed/Perl toolAutoconf should help.
All but the most simple of software is driven by some sort of file or command-line parameters to control its behaviornetwork listener ports, process owners, number of threads to spawn, and the like. Deferring such configuration from compile time to runtime provides tremendous flexibility to the program because the product of a single build is adaptable to myriad environments.
Autoconf encourages this adaptability within the build process by similarly deferring decisions until build timethe location of supporting tools, flags for OS-specific compilers, installation directories, file ownership, and so on. Results of queries against the build host are merged with templates to create makefiles (actually, Autoconf can create any kind of file based on a template, but is typically used to create makefiles), and command-line tasks can be wrapped inside a tool such that you need not even remember to run them individually.
Having a clean, standard process to manage this is ideal for both developers and end users. (Raise your hand if you remember hand-editing makefiles and, in some cases, header files, to get a piece of software to build on your system.) An added benefit of letting Autoconf handle this sort of work is that it minimizes the chances of error introduced by human element; manual edits or lists of build procedures, for example.
If you frequently download and build open-source tools from source, chances are you are already familiar with Autoconf. Autoconf generates the configure of the configure/make/make-install routine. The configure process queries the host and uses its findings to bootstrap the makefiles, customizing them for the target build environment.
What may be surprising is that configure is a shell scriptand not a fancy shell script at that, just the most basic of Bourne shell syntax. Autoconf creates the configure script by expanding a series of m4 macros, as specified by users.
Autoconf parses a filetypically called "configure.ac"and expands any m4 macros it encounters. The macros are fragments of shell logic, which is why the resultant configure is a shell script. Similar to a preprocessor, anything Autoconf does not recognize as a macro it adds to configure untouched, which means a configure.ac could contain a mix of macros and raw shell code. Purists might eschew this notion, but the realists see this as a means to bootstrap Autoconf integration or perform a proof-of-concept. Over time, such quick-hit shell actions may be wrapped in proper m4 macros, so the lofty goal of a "pure" configure.ac can still be achieved. To introduce raw shell code into configure.ac is typically a precursor to writing your own site- or project-specific Autoconf macros.
The shell syntax to be used here is plain Bourne shell (/bin/sh)not C Shell, Korn Shell, or Bash, but very plain, portable Bourne shell. I emphasize the word "portable" because Bourne shells are not the same across all operating systems.
While the Autoconf manual says configure.ac must contain at minimum two specific Autoconf macros, you need three macro calls for Autoconf to be of any real use:
AC_INIT([myproject], \ [3.5.1], \ [bugs-myproject@mydomain])
AC_CONFIG_FILES([Makefile \ base/Makefile \ network/Makefile])
AC_OUTPUTThe square brackets are included in the text. While some technical manuals have adopted the square brackets to indicate an optional parameter in examples, Autoconf (really, m4) sees them as quoting charactersthey act as boundaries such that values are not misinterpreted. I recommend that any parameter, other than a single word, be quoted. Autoconf is also sensitive about spacesthere should be no spaces between a square brace and the text it encloses, nor should there be space between a closing square brace and a following comma. In cases where you would like to move a value to the next line, use tabs instead of spaces.
AC_PROG_CXX([ gcc3 , gcc , cc ])
AC_CHECK_TYPES( [uid_t , struct stat SSL], [true], [AC_MSG_NOTICE([missing a key \ data type, this build may \ fail])] )
AC_ARG_WITH( [ssl], [AC_HELP_STRING([with-ssl], \ [provide the location of your \ SSL toolkit])], [ ## ... code to execute if \ ## this flag is passed to ## configure ], [ ## ... code to execute if \ ## this flag is NOT passed \ ## to configure ] )
ksh ./configureJust as Autoconf by default looks for its input in configure.ac, it seeks project-specific macros in a file named "aclocal.m4." (Autoconf also supports sitewide macros in a file called "acsite.m4," thus allowing you to create a hierarchy within your organization, much like a code library.) In addition to shell fragments, your macro may also call existing macros. Common built-in macros of this sort include:
AC_DEFUN( [MYPROJECT_MACRONAME], [ ## ... macro actions ... ] )
Listing 2 implements a custom Autoconf macro based on the built-in AC_ARG_ENABLE macro, which permits conditional inclusion of compile-time features based on whether the --with-{feature} flag is passed to configure. The feature in question is a compiler flag that enables runtime profiling.
The macro in Listing 2 uses the AC_MSG_INFO macro to communicate with the end user, rather than pure shell echo statements. This provides consistent output for the configure script. The results of the test are available in makefiles as the substitution variable @CXXFLAGS_PROFILING@, which will be either -pg or blank, based on whether the flag --with-profiling is passed to configure.
Listings 3 and 4 are a sample configure.ac and Makfefile.in, respectively, which include our custom macros from Listings 1 and 2. I intentionally used a basic makefile for brevity. For example, instead of creating makefile variables for the compiler flags, I include the Autoconf output variables inline with the compiler commands.
Once you've written configure.ac, creating configure is a snap: Simply run autoconf in the directory where you have placed configure.ac (most likely at the root of your project's source tree). Be sure to rerun the command each time you update configure.ac, or your changes will not be incorporated into the configure script.
There are myriad ways to integrate Autoconf into an existing build process. In all cases, it would behoove you to thoroughly review and, if necessary, clean up your existing build process before making any changes. This should reveal where you make your hand-edits, and which makefile definitions should be put under Autoconf control. In turn, this will make the initial Autoconf introductions brief, providing a relatively low downtime for your build process. If you already have a semiautomated process for performing your makefile substitutions, you could wrap that with Autoconf and phase it out over time.
Next, rename all files that will be updated by Autoconf such that they are recognized as templates: Add a .in suffix to their names. (Be sure to do this within your source control system as well.) Educate local development staff such that everyone understands that only changes to the template files will survive a source-code check-in. Include these filenames in the AC_CONFIG_FILES section of configure.ac. This way, from the start, each call to configure will create the target files.
Mark up the new templates using Autoconf output variable syntax @variable_name@. Be sure to review both the Autoconf manual and your aclocal.m4 to confirm the names of the output variables and the macros in which they are defined. At this point, if your build process is too delicate to call proper macros from configure.ac, your first version of this file can simply contain a series of AC_SUBST calls that use your default values. Listing 5 includes an example of such a configure.ac.
In Listing 5, the call to AC_CONFIG_FILES uses tabs and newlines to separate its arguments for clarity. Also, the values passed to AC_SUBST are double quoted. Technically, only the CFLAGS substitution requires quotes, as its value contains a space; but to quote all values follows good shell form.
With the ability to perform variable substitutions, you can view your makefiles as a (limited) type of source code: "That macro is subject to change and is found in seven makefiles; let's put it under Autoconf."
Using Autoconf in new projects (that is, making it part of the build process from the beginning) follows a path similar to integrating it into existing projects. Most likely the project will begin as stub code, so you can run autoscan on that to extract special headers or definitions you will use. From there, it becomes an iterative process: As the source tree grows, new directories are added, thus new files must be added to AC_CONFIG_FILES. If a new tool is required for a particular module, a new macro call is added to configure.ac (perhaps a custom macro is created in aclocal.m4) and the new output variables are added to that module's makefile. As part of release planning, further updates are made to configure.ac and the main makefile. In essence, Autoconf management can take on a life of its own, a project within your project. As time progresses, though, the Autoconf maintenance should subside even as the actual development moves along at full steam.
Some practices apply to both new and existing projects:
Authors of open-source tools are acutely aware of varying build environments; it is no coincidence, then, that Autoconf drives the build process for so many open-source tools today. This needn't limit Autoconf to builds of open-source tools, however. Anyone who has ever designed a build environment for a multideveloper project can see the advantages of adaptable build-time configuration.
All of this is good, but skeptics might raise an eyebrow: "Is all of this overkill for my project?" Perhaps, but not likely. If your project consists of a single makefile with definitions that never change, makes use of no external products/libraries, and you've never changed environments or compilers, then Autoconf probably is overkill. (Then again, who has a project like that and what are the chances it will remain as such?)
Even if Autoconf sounds like a great idea for your project, there are other considerations. From a technical standpoint, your first task is to determine whether Autoconf runs in your environment.
Also consider the political ramifications, as determined by your team or company's stance toward free software. Some sites are wary of tools for which there are no explicit contracts, nor technical support contacts. To that, you may be able to quell management's fears by explaining Autoconf's ubiquity: It is part of the build process of a great number and wide variety of open-source packages, including the long-since-stable crowd of applications that are used, free of support contracts, every day in production environments.
Even then, you may want to have the local legal staff review the license such that they are aware of any sticking points. My understanding of the Autoconf license is that the configure script generated by the tool is not covered by the GNU Public License (GPL), nor is the code built by the process bootstrapped by configure; but it would still be to your advantage to have the license reviewed by a competent attorney.
Once you've confirmed Autoconf is right for you, the next hurdle is to implement it. There's no time like the present, and this article has provided some tips on integrating Autoconf into an existing project. As with the introduction of any widespread change, you must decide between "baby steps" and "leaps and bounds"; in other words, it comes down to how much disruption you can afford and how much confidence you have in the changes you make. There is no one true answer here. For the faint of heart, Autoconf's design supports an incremental introduction, in which most of the changes may take place behind the scenes.
Like most development tools, Autoconf requires an initial investment of time and effort, but as it takes care of some of the drudgework, the payoff is that you get to spend more time doing what you want to do in the first placewrite code.
CUJ
dnl dnl MYPROJECT_CHECK_SSL dnl dnl Allow the caller to specify the base tree for dnl ssl, and set compiler/linker flags accordingly dnl dnl Sets output variables: @LDFLAG_SSL@ and @CXXFLAG_SSL@ dnl AC_DEFUN( [MYPROJECT_CHECK_SSL], [ AC_ARG_WITH( [ssl], [AC_HELP_STRING([--with-ssl], [provide the location of your SSL toolkit])], [ ## if provided, set -L and -I accordingly MYPROJECT_DIST_SSL=${withval} MYPROJECT_LDFLAG_SSL="-L${MYPROJECT_DIST_SSL}/lib" MYPROJECT_CXXFLAG_SSL="-I${MYPROJECT_DIST_SSL}/include" AC_MSG_NOTICE([We'll look for ssl under ${MYPROJECT_DIST_SSL}]) AC_SUBST(LDFLAG_SSL, [${MYPROJECT_LDFLAG_SSL}]) AC_SUBST(CXXFLAG_SSL, [${MYPROJECT_CXXFLAG_SSL}]) ], [ ## if it's in a system path, there's no need for the "-L" or "-I" flags ## this means, if the tool isn't available in default header/library ## search paths, the build will fail. AC_MSG_NOTICE([We'll look for ssl in the default preprocessor/linker paths]) AC_SUBST(LDFLAG_SSL, [""]) AC_SUBST(CXXFLAG_SSL, [""]) ] ) ] )
dnl dnl MYPROJECT_ENABLE_PROFILING dnl dnl sets output variables to be used as dnl compiler flags related to profiling, e.g. dnl with GNU gprof. dnl dnl Sets outcatput variable: @CXXFLAG_PROFILING@ dnl AC_DEFUN( [MYPROJECT_ENABLE_PROFILING], [ AC_ARG_ENABLE( [profiling], AC_HELP_STRING([--enable-profiling], [build executables for profiling]), [ AC_MSG_NOTICE([Enabling runtime profiling flags]) AC_SUBST(CXXFLAG_PROFILING, ["-pg"]) ], [ ## no need to send a message if we're not profiling AC_SUBST(CXXFLAG_PROFILING, [""]) ] ) ] )
## ## Autoconf input for project MyProject ## AC_INIT([myproject],[3.5.1],[bugs-myproject@mydomain]) dnl - compiler check AC_PROG_CXX( g++3 c++3 g++ c++ ) dnl - basic header files AC_HEADER_STDC AC_CHECK_HEADERS(unistd.h) dnl - OS-related types AC_TYPE_UID_T AC_TYPE_MODE_T dnl - C-based functions we will need AC_CHECK_FUNCS([strdup]) dnl - where is the SSL toolkit? MYPROJECT_CHECK_SSL dnl - should we enable runtime profiling? MYPROJECT_ENABLE_PROFILING dnl - the roundup AC_CONFIG_FILES([ Makefile base/Makefile net/Makefile net/defaults.h ]) dnl - write out the files based on the templates + configure's queries AC_OUTPUT
## ## toplevel Makefile for MyProject ## ## WARNING: ## @configure_input@ ## all: myproject myproject: @CXX@ -o myproject @CXXFLAG_PROFILING@ @CXXFLAG_SSL@ @LDFLAG_SSL@ -lssl ## EOF
AC_INIT([myproject],[5.1],[bugs-myproject@mydomain]) AC_SUBST(CC,["/usr/local/bin/gcc3"]) AC_SUBST(CFLAGS,["-O3 -pg"]) AC_SUBST(DEFAULT_OWNER,["nobody"]) AC_SUBST(DEFAULT_PORT,["2345"]) AC_CONFIG_FILES([ Makefile base/Makefile net/Makefile net/defaults.h ])
Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.