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. */#definePACKAGE_BUGREPORT"BUG-REPORT-ADDRESS"/* Define to the full name of this package. */#definePACKAGE_NAME"FULL-PACKAGE-NAME"/* Define to the full name and version of this package. */#definePACKAGE_STRING"FULL-PACKAGE-NAME VERSION"/* Define to the one symbol short name of this package. */#definePACKAGE_TARNAME"full-package-name"/* Define to the version of this package. */#definePACKAGE_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>intmain(intargc, char* argv[]){doublesec;
 structtimevaltv;
 
 gettimeofday(&tv, NULL);
 sec = tv.tv_sec;
 sec += tv.tv_usec / 1000000.0;
 
 printf("%f\n", sec);
 
 return0;
}

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. */#defineHAVE_GETTIMEOFDAY1/* Define to 1 if you have the <inttypes.h> header file. */#defineHAVE_INTTYPES_H1/* Define to 1 if you have the <memory.h> header file. */#defineHAVE_MEMORY_H1/* Define to 1 if you have the <stdint.h> header file. */#defineHAVE_STDINT_H1/* Define to 1 if you have the <stdlib.h> header file. */#defineHAVE_STDLIB_H1/* Define to 1 if you have the <strings.h> header file. */#defineHAVE_STRINGS_H1/* Define to 1 if you have the <string.h> header file. */#defineHAVE_STRING_H1/* Define to 1 if you have the <sys/stat.h> header file. */#defineHAVE_SYS_STAT_H1/* Define to 1 if you have the <sys/time.h> header file. */#defineHAVE_SYS_TIME_H1/* Define to 1 if you have the <sys/types.h> header file. */#defineHAVE_SYS_TYPES_H1/* Define to 1 if you have the <unistd.h> header file. */#defineHAVE_UNISTD_H1/* Define to the address where bug reports for this package should be sent. */#definePACKAGE_BUGREPORT"BUG-REPORT-ADDRESS"/* Define to the full name of this package. */#definePACKAGE_NAME"FULL-PACKAGE-NAME"/* Define to the full name and version of this package. */#definePACKAGE_STRING"FULL-PACKAGE-NAME VERSION"/* Define to the one symbol short name of this package. */#definePACKAGE_TARNAME"full-package-name"/* Define to the version of this package. */#definePACKAGE_VERSION"VERSION"/* Define to 1 if you have the ANSI C header files. */#defineSTDC_HEADERS1/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */#defineTIME_WITH_SYS_TIME1

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"#ifdefHAVE_SYS_TIME_H#include <sys/time.h>#else#include <time.h>#endifdoubleget_sec_since_epoch(){doublesec;
 
 #ifdefHAVE_GETTIMEOFDAYstructtimevaltv;
 
 gettimeofday(&tv, NULL);
 sec = tv.tv_sec;
 sec += tv.tv_usec / 1000000.0;
 #elsesec = time(NULL);
 #endifreturnsec;
}intmain(intargc, char* argv[]){printf("%f\n", get_sec_since_epoch());
 
 return0;
}

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
page revision: 5, last edited: 29 Apr 2008 01:45
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License
Click here to edit contents of this page.
Click here to toggle editing of individual sections of the page (if possible). Watch headings for an "edit" link when available.
Append content without editing the whole page source.
Check out how this page has evolved in the past.
If you want to discuss contents of this page - this is the easiest way to do it.
View and manage file attachments for this page.
A few useful tools to manage this Site.
Change the name (also URL address, possibly the category) of the page.
View wiki source for this page without editing.
View/set parent page (used for creating breadcrumbs and structured layout).
Notify administrators if there is objectionable content in this page.
Something does not work as expected? Find out what you can do.
General Wikidot.com documentation and help section.
Wikidot.com Terms of Service - what you can, what you should not etc.
Wikidot.com Privacy Policy.

AltStyle によって変換されたページ (->オリジナル) /