假设我有一个功能“foo”,我正在为多个操作系统实现。有一种通用的方法,但如果操作系统有本机支持,那么我想使用它。
如何使用autotools有条件地编译正确的源文件?
实施例: 假设“foo”是“重启机器”。
在Linux上我会使用reboot(),我会把它放在reboot_reboot.c中,在那里我定义myreboot()只需用正确的参数调用reboot()。
在所有BSD上我会使用bsd_made_up_blah_reboot(),它使用不同的参数。我会把它放在reboot_bsd_made_up_blah_reboot.c中,这样做。 (这只是一个例子,我知道BSD有重启())
否则,请使用reboot_generic.c,它只调用system(“shutdown -i6 -g0”)。 (是的,让我们忽略不同的关闭二进制文件的语法)
在configure.ac中,我可以检查是否存在“reboot”,“bsd_made_up_blah_reboot”,然后在Makefile.am中执行:
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
我不喜欢这个,因为(据我所知)Makefile.am中没有“else if”,随着实现数量的增加,它会变得更加丑陋和丑陋。
这样做的正确方法是什么?请记住,我希望防范未来的系统 reboot()和bsd_made_up_blah_reboot(),所以我不能同时包含这两者。
答案 0 :(得分:3)
我不喜欢这个,因为(据我所知)Makefile.am中没有“else if”,并且随着实现数量的增加,如果变得更加丑陋和丑陋。
但如果你可以保证案例HAVE_REBOOT
,HAVE_BSD_REBOOT
(以及其他可能的话)是成对析取的,你可以简单地写
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
(你需要在configure.ac中定义一个HAVE_NO_REBOOT。)也没那么难,假设,例如
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])
答案 1 :(得分:2)
您可以使用autoconf测试完成相同的操作,并在config.h中定义预处理器宏,然后在您的代码中
#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
}
对于不太复杂的功能(例如上面的可移植包装器),这是具有单独源文件的IMHO清洁器。 YMMV。
编辑:为了回答larsmans和downvoter,Kernighan&amp;派克说的是,条件编译很糟糕主要是因为它使测试所有组合变得困难,如果不是不可能的话。这当然是正确的,那里没有争论,但是如果条件编译是通过在构建中包含不同的源文件比通过CPP指令的条件编译更好?嗯,显而易见的答案是它不是(明显的警告,将CPP控制流与正常控制流混合是非常令人困惑的,但是在我上面的例子中我也没有提倡),两种方法都是相同的测试的困难。
是的,显然,如果可以避免条件编译,那就好了。但是,有时候这不是一个选择,然后就必须把它搞砸了。
答案 2 :(得分:0)
根据AC_SUBST
挖掘的内容,您可以_SOURCES
configure
行所需的来源吗?
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
请记住,autoconf
哲学是“测试功能,而不是平台”。