问题
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.
回答1:
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])
回答2:
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.
回答3:
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".
来源:https://stackoverflow.com/questions/5057098/different-implementations-in-different-files-in-autotools