Thomas
Thomas

Reputation: 4255

Different implementations in different files in autotools

Let's say I have a feature "foo" that I'm implementing for several operating systems. There is a generic way to do it, but if the OS has native support for it then I want to use that.

How do I conditionally compile only the right source files with autotools?

Example: Let's say "foo" is "reboot the machine".

On Linux I would use reboot(), and I would put that in reboot_reboot.c, where I define myreboot() do just call reboot() with the correct parameters.

On all the BSDs I would use bsd_made_up_blah_reboot(), which takes different parameters. I would put this in reboot_bsd_made_up_blah_reboot.c, which does this. (this is just an example, I know BSDs have reboot())

Else, use reboot_generic.c, which just calls system("shutdown -i6 -g0"). (Yes, let's ignore the syntax for different shutdown binaries)

In configure.ac I can check for the presence of "reboot", "bsd_made_up_blah_reboot", and then in Makefile.am do:

if HAVE_REBOOT
 blah_SOURCES += reboot_reboot.c
else
 if HAVE_BSD_MADE_UP_BLAH_REBOOT
  blah_SOURCES += reboot_bsd_made_up_blah_reboot.c
 else
  blah_SOURCES += reboot_generic.c
 endif
endif

I don't like this, since (as I understand it) there is no "else if" in Makefile.am, and as the number of implementations grow it will get uglier and uglier.

What's the right way to do this? Keep in mind that I want to protect against a future system having both reboot() and bsd_made_up_blah_reboot(), so I can't just include both.

Upvotes: 2

Views: 544

Answers (3)

Jack Kelly
Jack Kelly

Reputation: 18657

Could you AC_SUBST the sources you need into your _SOURCES line, based on what configure digs up?

configure.ac:

AS_CASE([$host],
[*-linux], [REBOOT_OBJ=reboot-linux.$OBJEXT],
[*-bsd], [REBOOT_OBJ=reboot-bsd.$OBJEXT],
[REBOOT_OBJ=reboot-generic.$OBJEXT])
AC_SUBST([REBOOT_OBJ])

Makefile.am:

foo_SOURCES = foo.c bar.c baz.c
foo_LDADD = $(REBOOT_OBJ)
foo_DEPENDENCIES = $(REBOOT_OBJ)
EXTRA_foo_SOURCES = reboot-bsd.c reboot-generic.c reboot-linux.c

Remember that the autoconf philosophy is "test for features, not platforms".

Upvotes: 0

janneb
janneb

Reputation: 37188

You can accomplish the same with autoconf tests and define preprocessor macros in config.h, and then in your code

#include "config.h"
#include <otherstuff.h>
void my_reboot()
{
#ifdef HAVE_REBOOT
    reboot(...);
#elif defined(HAVE_BSD_REBOOT)
    reboot(blahblah);
#else
    system("shutdown -r");
#endif
}

For features which are not very complicated (e.g. portability wrappers such as above), this is IMHO cleaner than having separate source files. YMMV.

EDIT: To answer larsmans and the downvoter, what Kernighan & Pike are saying is that conditional compilation is bad primarily because it makes testing all combinations difficult, if not impossible. That is certainly true, no argument there, but how is conditional compilation by including different source files in the build better than conditional compilation via CPP directives? Well, the obvious answer is that it isn't (with the obvious caveat that mixing CPP control flow with normal control flow is very confusing, but then I'm not advocating that in my example above either), and both methods present the same difficulties wrt testing.

And yes, obviously, if one can avoid conditional compilation, good. However, sometimes this just isn't an option, and then one has to suck it up.

Upvotes: 2

user611775
user611775

Reputation: 1353

I don't like this, since (as I understand it) there is no "else if" in Makefile.am, and as the number of implementations grow if will get uglier and uglier.

But if you can guarantee that the cases HAVE_REBOOT, HAVE_BSD_REBOOT (and whatever else might be) are pairwise disjunctive, you can simply write

if HAVE_REBOOT
x_SOURCES += reboot.c
endif
if HAVE_BSD_REBOOT
x_SOURCES += bsd_reboot.c
endif
if HAVE_NO_REBOOT
x_SOURCES += generic_reboot.c
endif

(You would need to define a HAVE_NO_REBOOT in configure.ac.) Not that hard either, assuming, for example

case "$host" in
(*-linux)
    have_reboot=1;;
(*-bsd)
    have_bsd_reboot=1;;
(*)
    have_no_reboot=1;;
esac;
AM_CONDITIONAL([HAVE_REBOOT], [test "$have_reboot" = 1])
AM_CONDITIONAL([HAVE_BSD_REBOOT], [test "$have_bsd_reboot" = 1])
AM_CONDITIONAL([HAVE_NO_REBOOT], [test "$have_no_reboot" = 1])

Upvotes: 3

Related Questions