Sunday, June 2, 2019

GNU Build System – Conditional Features


Brief

In training (Qs-GnuBuildEnvironment) we discussed the rationale and means of using the Gnu Build System in application development and deployment. This training extends on that training to include instructions on how to utilize this build environment in the development of more sophisticated applications where autoconf will examine the system and tailor the application dependent on the existence of module dependencies.

Introduction

The GNU Build System (also known as autotools) is a suite of tools authored by Gnu used to make portable software products. The build system includes two significant programs: automake and autoconf. The automake utility is used in the generation of makefiles from a Makefile.am template. The autoconf utility provides a set of utilities that help to generate the configure script. Together, these utilities provide the means of interrogating the target system and generating a target-specific makefile to be used to compile the application.

Before beginning, it is assumed that your system has the following packages installed:
  1. autoconf
  2. automake
  3. gcc
  4. g++
  5. pkg-config
  6. libgtk2.0-dev

Discussion


We begin with a similar example to that outlined in training Qs-GnuBuildEnvironment. The example is similar but simplified to use a single source file. Without discussion we will identify the contents of the input files and abbreviated procedure for setting up the build environment.

Base Example

configure.ac

AC_PREREQ(2.59)
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AM_INIT_AUTOMAKE(someApp,2.1)
AC_CONFIG_HEADER([config.h])
AC_PROG_CC
AC_OUTPUT(Makefile)

Makefile.am

bin_PROGRAMS=someApp
someApp_SOURCES=myMain.c

myMain.c

#include <stdio.h>
#include <stdlib.h>

int main()
{
printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
return EXIT_SUCCESS;
}

Brief Build System Setup

Given the configure.ac, Makefile.am, and myMain.c files stepping through the following procedure will result in building the someApp application.
$ aclocal
$ autoconf
$ autoheader
$ touch NEWS README AUTHORS ChangeLog
$ automake -a
$ ./configure
$ make


Example Extension

The autoconf utility allows examining the target system for features. A common action for applications is to allow autoconf to define C preprocessor symbols based on the result of such feature tests.

configure.ac

AC_PREREQ(2.59)
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AM_INIT_AUTOMAKE(someApp,2.1)
AC_CONFIG_HEADER([config.h])
AC_PROG_CC
AC_CHECK_HEADER([popt.h],
[AC_DEFINE([HAVE_POPT_H], [1],
[Define to 1 if you have <popt.h>.])],
[AC_MSG_ERROR([Sorry, can't do anything for you])])
AC_OUTPUT(Makefile)

After making the above changes to the configure.ac file you must repeat the procedure for re-establishing the build environment.
$ aclocal
$ autoheader
$ automake –add-missing –copy
$ autoconf

Running ./configure will now execute the AC_CHECK_HEADER feature test, giving the following output:
checking for popt.h... no
configure: error: Sorry, can't do anything for you
or
checking for popt.h... yes
If the feature test for popt.h fails configure outputs an error and aborts the script. If the feature test for popt.h succeeds AC_DEFINE will define the HAVE_POPT_H precompiler symbol as 1, defined in config.h. This test effectively defines a critical dependency on popt.h, which isn't exactly what we intended. What we intended is the definition of a precompiler symbol based on the autoconf examination of the system. If we update the configure.ac file as follows we will get the desired result.

configure.ac

AC_PREREQ(2.59)
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AM_INIT_AUTOMAKE(someApp,2.1)
AC_CONFIG_HEADER([config.h])
AC_PROG_CC
AC_CHECK_HEADER([popt.h],
[AC_DEFINE([HAVE_POPT_H], [1],
[Define to 1 if you have <popt.h>.])])
AC_OUTPUT(Makefile)

Next, by placing precompiler commands we can test for the definition of the HAVE_POPT_H symbol. This allows us to place conditional code in our application. In the absence of popt libraries we could substitute our own command line parsing routine.

myMain.c

#include <stdio.h>
#include <stdlib.h>

int main()
{
printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
#ifdef HAVE_POPT_H
printf("(%s:%d) whee...we have popt.h\n",__FILE__,__LINE__);
#else
printf("(%s:%d) rats...we don't have popt.h\n",__FILE__,__LINE__);
#endif
printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
return EXIT_SUCCESS;
}


Conclusion

In closing, authoring a Gnu Build Environment compliant product offers flexibility to the user by incorporating a standardized means for examining the target system and ensuring the required product dependencies are met and offers a well defined configuration and build environment when distributing your product by source. Incorporation of autoconf feature examination allows the conditional definition of precompiler symbols. These symbols can be used to place conditional code in our application, giving us alternatives to continue in the absence of optional features.

References

  1. Introduction to the GNU Build System; FSK Consulting








No comments:

Post a Comment