我有一个模块将针对几个不同的操作系统和配置。有时,一些 C 代码可以使这个模块的任务更容易一些,所以我有一些 C 函数我想绑定代码。我不必绑定 C 函数——例如,我不能保证最终用户甚至有一个 C 编译器,而且优雅地故障转移到完成相同事情的纯 Perl 方式通常不是问题-- 但如果我能从 Perl 脚本中调用 C 函数就更好了。
还在我这儿?这是另一个棘手的部分。几乎所有的 C 代码都是系统特定的——为 Windows 编写的函数无法在 Linux 上编译,反之亦然,而在 Solaris 上执行类似操作的函数看起来完全不同。
#include <some/Windows/headerfile.h>
int foo_for_Windows_c(int a,double b)
{
do_windows_stuff();
return 42;
}
#include <path/to/linux/headerfile.h>
int foo_for_linux_c(int a,double b)
{
do_linux_stuff(7);
return 42;
}
此外,即使对于针对同一系统的本机代码,也可能只有其中一些可以在任何特定配置上编译。
#include <some/headerfile/that/might/not/even/exist.h>
int bar_for_solaris_c(int a,double b)
{
call_solaris_library_that_might_be_installed_here(11);
return 19;
}
但理想情况下,我们仍然可以使用可以使用该配置进行编译的 C 函数。所以我的问题是:
如何有条件地编译 C 函数(仅编译适合 的当前值的代码
$^O
)?如何单独编译 C 函数(有些函数可能无法编译,但我们仍想使用可以编译的函数)?
我可以在构建时(最终用户安装模块时)还是在运行时(
Inline::C
例如,使用 )执行此操作?哪种方式更好?我如何知道哪些函数已成功编译并且可以从 Perl 中使用?
所有想法表示赞赏!
更新:感谢所有回复的人。所以这就是我所做的:
Inline::C
我考虑了一种使用inside of
语句的运行时绑定方案eval
,但最终选择了子类Module::Build
化和自定义ACTION_build
方法:
my $builderclass = Module::Build->subclass(
class => 'My::Custom::Builder',
code => <<'__CUSTOM_BUILD_CODE__,',
sub ACTION_build {
use File::Copy;
my $self = shift;
### STEP 1: Compile all .xs files, remove the ones that fail ###
if (! -f "./lib/xs/step1") {
unlink <lib/xs/*>;
foreach my $contrib_file (glob("contrib/*.xs")) {
File::Copy::copy($contrib_file, "lib/xs/");
}
open my $failed_units_fh, '>', 'lib/xs/step1';
local $@ = undef;
do {
my $r = eval { $self->ACTION_code() };
if ($@ =~ /error building (\S+\.o) from/i
|| $@ =~ /error building dll file from '(\S+\.c)'/i) {
my $bad_file = $1;
$bad_file =~ s!\\!/!g;
my $bad_xs = $bad_file;
$bad_xs =~ s/.[oc]$/.xs/;
print STDERR "ERROR COMPILING UNIT $bad_xs ... removing\n\n";
unlink $bad_xs;
print $failed_units_fh "$bad_xs\n";
} elsif ($@) {
print STDERR "Compile error not handled in $^O: $@\n";
}
} while $@;
print "Removed all uncompilable units from lib/xs/\n";
close $failed_units_fh;
}
### STEP 2: Combine valid .xs files into a single .xs file ###
if (! -f "./lib/xs/step2") {
open my $valid_units_fh, '>', "lib/xs/step2";
my (@INCLUDE,%INCLUDE,$MODULE,@PREMOD,@POSTMOD);
foreach my $xs (glob("lib/xs/*.xs")) {
open my $xs_fh, '<', $xs;
while (<$xs_fh>) {
if (m/#include/) {
next if $INCLUDE{$_}++;
push @INCLUDE, $_;
} elsif (/^MODULE/) {
$MODULE = $_;
push @POSTMOD, <$xs_fh>;
} else {
push @PREMOD, $_;
}
}
close $xs_fh;
print $valid_units_fh "$xs\n";
}
close $valid_units_fh;
unlink <lib/xs/*>, <blib/arch/auto/xs/*/*>;
unlink 'lib/My/Module.xs';
open my $xs_fh, '>', 'lib/My/Module.xs' or croak $!;
print $xs_fh @INCLUDE, @PREMOD, $MODULE, @POSTMOD;
close $xs_fh;
print "Assembled remaining XS files into lib/My/Module.xs\n";
}
### STEP 3: Clean all .xs stuff and compile My/Module.xs ###
unlink <lib/xs/*>;
$self->ACTION_code();
return $self->SUPER::ACTION_build(@_);
}
}
检查$@
可能非常脆弱。它适用于我尝试过的系统(全部使用 gcc),但它可能无法正常工作,因为它到处都是写的。