Chapter 3: Let's make "Hello, world!" portable

In the previous chapter, we added Autoconf to our program. But it's not quite portable yet. So we'll try to make it portable in this chapter.

“config.h” has portability constants

To make the program portable, we'll need to modify our sourcefile, “hello.c”. Our source file will need #ifdef, #ifndef, etc. to be portable.

But we can't use #ifdef and #ifndef unless we have some constants to check. Well, it turns out the constants we need are in “config.h”. So we'll simply #include "config.h".

But we don't have “config.h” yet. “config.h” is created by Autoconf, and we need to tell Autoconf to generate one. This is actually quite simple. Just run “autoheader”, followed by “./configure”:

$ ls
Makefile     autom4te.cache/  config.log      configure*    hello*
Makefile.in  autoscan.log     config.status*  configure.ac  hello.c
$ autoheader
autoheader2.50: `config.h.in' is created
$ ls
Makefile         autoscan.log  config.status*  hello*
Makefile.in      config.h.in   configure*      hello.c
autom4te.cache/  config.log    configure.ac
$ ./configure
checking for gcc... gcc
checking for C compiler default output... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables... 
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
$ ls
Makefile         autoscan.log  config.log      configure.ac
Makefile.in      config.h      config.status*  hello*
autom4te.cache/  config.h.in   configure*      hello.c
$

(Note to Autoconf 2.13 users: If “config.h” isn't generated by “./configure”, add the line “AC_CONFIG_HEADER(config.h)” to “configure.in” somewhere after the “AC_INIT” line, then run “autoconf” and repeat the above again.)

Notice how “autoheader” generates “config.h.in”, then “config.h.in” was used by “configure” to generate “config.h”? In general, that's how Autoconf works: You'll use some program to generate “<something>.in”, and “<something>.in” will be used by Autoconf to generate “<something>”.

Now, let's see what kind of constants we got. Open “config.h”:

/* config.h.  Generated by configure.  */
/* config.h.in.  Generated from configure.ac by autoheader.  */
 
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "BUG-REPORT-ADDRESS"
 
/* Define to the full name of this package. */
#define PACKAGE_NAME "FULL-PACKAGE-NAME"
 
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "FULL-PACKAGE-NAME VERSION"
 
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "full-package-name"
 
/* Define to the version of this package. */
#define PACKAGE_VERSION "VERSION"

Well, there are some constants, but nothing useful for portability. I guess our program is too simple to have any portability issues. It looks like we'll need to make our program more complex, so we'll actually get something in “config.h”. So let's do that.

Let's add complexity to “hello.c”

So let's make our program a little more complex so we can run some portability tests with Autoconf. We'll modify our program to print the number of seconds since the Epoch (January 1, 1970 00:00:00 GMT) using the gettimeofday function call:

/* hello.c: A program to show the time since the Epoch */
 
#include <stdio.h>
#include <sys/time.h>
 
int main(int argc, char* argv[])
{
   double sec;
   struct timeval tv;
 
   gettimeofday(&tv, NULL);
   sec = tv.tv_sec;
   sec += tv.tv_usec / 1000000.0;
 
   printf("%f\n", sec);
 
   return 0;
}

And shall we Autoconf it?:

$ autoscan
Use of uninitialized value in concatenation (.) or string at /usr/bin/autoscan line 195.
configure.ac: warning: missing AC_CHECK_FUNCS([gettimeofday]) wanted by: hello.c:12
configure.ac: warning: missing AC_CHECK_HEADERS([sys/time.h]) wanted by: hello.c:4
configure.ac: warning: missing AC_HEADER_TIME wanted by: hello.c:10
$ mv configure.scan configure.ac
$ autoconf
$ autoheader
autoheader2.50: `config.h.in' is updated
$ ./configure
checking for gcc... gcc
checking for C compiler default output... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables... 
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking how to run the C preprocessor... gcc -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking sys/time.h usability... yes
checking sys/time.h presence... yes
checking for sys/time.h... yes
checking whether time.h and sys/time.h may both be included... yes
checking for gettimeofday... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
$ ls
Makefile         autoscan.log  config.h.in~    configure*    hello.c
Makefile.in      config.h      config.log      configure.ac
autom4te.cache/  config.h.in   config.status*  hello*
$

Notice a few things here:

  • After running “autoscan”, remember to rename “configure.scan” to “configure.ac”.
  • “autoscan” generates warning messages about “configure.ac”. This is because “configure.ac” is checked by “autoscan” to see if it needs updating. In our case, we simply replace “configure.ac” with “configure.scan”. We may not want to do this in the future, if we have a customized version of “configure.ac”. If that were the case, we would edit “configure.ac” manually instead.

So what's in “config.h”?

/* config.h.  Generated by configure.  */
/* config.h.in.  Generated from configure.ac by autoheader.  */
 
/* Define to 1 if you have the `gettimeofday' function. */
#define HAVE_GETTIMEOFDAY 1
 
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
 
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
 
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
 
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
 
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
 
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
 
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
 
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
 
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
 
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
 
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "BUG-REPORT-ADDRESS"
 
/* Define to the full name of this package. */
#define PACKAGE_NAME "FULL-PACKAGE-NAME"
 
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "FULL-PACKAGE-NAME VERSION"
 
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "full-package-name"
 
/* Define to the version of this package. */
#define PACKAGE_VERSION "VERSION"
 
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
 
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1

Alright! So using this header file, we can check for several things. For simplicity, though, we'll check only two things — Whether gettimeofday() exists (using “HAVE_GETTIMEOFDAY”), and whether we have “sys/time.h” (using “HAVE_SYS_TIME_H”).

(Note to Autoconf 2.13 users: After you rename “configure.scan” to “configure.in”, remember to add the line “AC_CONFIG_HEADER(config.h)” to “configure.in”!)

So let's modify our source to use this information!

Let's make our sourcecode portable!

So we're now ready to modify our sourcecode to use the information provided by “config.h”:

/* hello.c: A program to show the time since the Epoch */
 
#include <stdio.h>
#include "config.h"
 
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
 
double get_sec_since_epoch()
{
   double sec;
 
   #ifdef HAVE_GETTIMEOFDAY
      struct timeval tv;
 
      gettimeofday(&tv, NULL);
      sec = tv.tv_sec;
      sec += tv.tv_usec / 1000000.0;
   #else
      sec = time(NULL);
   #endif
 
   return sec;
}
 
int main(int argc, char* argv[])
{
   printf("%f\n", get_sec_since_epoch());
 
   return 0;
}

I decided to make a separate function to handle the time function call. This way, I can group the functions that have portability problems into one section of my code, and adjust it again in the future if necessary.

The time function call tries to use the gettimeofday() system call, but it will fall back on the time() system call if gettimeofday() isn't available. gettimeofday() is more useful because it also provides microsecond information, but we'll use time() if gettimeofday() isn't available.

Oh, and don't forget to #include "config.h".

Now, we need to Autoconf it once more, just to verify we didn't make any changes that causes more portability problems:

$ autoscan
$

We can stop at this point since Autoscan didn't give us any errors. Had it told us we need to update “configure.ac”, we would have needed to rename “configure.scan”, rerun “autoconf”, etc.

Now, configure and compile:

$ ./configure
checking for gcc... gcc
checking for C compiler default output... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables... 
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking how to run the C preprocessor... gcc -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking sys/time.h usability... yes
checking sys/time.h presence... yes
checking for sys/time.h... yes
checking whether time.h and sys/time.h may both be included... yes
checking for gettimeofday... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: config.h is unchanged
$ make clean all
rm -f hello
cc     hello.c   -o hello
$ ./hello 
1053952012.248884
$

Good job! Now your program is not only using Autoconf, but it's also portable!

Let's review

Okay, so let's review. If you were making a new program from scratch, this is what you'd do:

  1. Write your program, keeping portability in mind. Create “Makefile.in”.
  2. Run “autoscan”.
  3. Rename “configure.scan” to “configure.ac”
  4. Run “autoheader”.
  5. Run “autoconf”.
  6. “./configure”.
  7. Check “config.h”. If necessary, modify source and repeat from step #2.
  8. And lastly, compile!

Good? Good!

What's next?

Now, we're ready to use Automake to create a more robust “Makefile”.

  • Next: Chapter 4 — Let's add Automake to our program
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License