如何有条件地将C代码片段编译到我的Perl模块?

mob*_*mob 5 perl swig xs

我有一个模块,将针对几种不同的操作系统和配置.有时,一些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;
}
Run Code Online (Sandbox Code Playgroud)
#include <path/to/linux/headerfile.h>
int foo_for_linux_c(int a,double b)
{
  do_linux_stuff(7);
  return 42;
}
Run Code Online (Sandbox Code Playgroud)

此外,即使对于同一系统的本机代码,也可能只能在任何特定配置上编译其中一些.

#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;
}
Run Code Online (Sandbox Code Playgroud)

但理想情况下,我们仍然可以使用可以使用该配置进行编译的C函数.所以我的问题是:

  • 如何有条件地编译C函数(只编译适合当前值的代码$^O)?

  • 如何单独编译C函数(某些函数可能无法编译,但我们仍然想使用那些函数)?

  • 我可以在构建时(最终用户安装模块时)或在运行时(Inline::C例如)进行此操作吗?哪种方式更好?

  • 我如何判断哪些函数已成功编译并可从Perl中使用?

所有的想法赞赏!


更新:感谢所有回复的人.所以这就是我做的:

我考虑了一个与语句Inline::C内部 运行时绑定的方案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(@_);
  }
}
Run Code Online (Sandbox Code Playgroud)

检查$@可能非常脆弱.它适用于我尝试的系统(全部使用gcc),但它可能无法正常工作.

dax*_*xim 5

理想情况下,使用Module::Build.在配置时(perl Build.PL),检测平台和头部位置(但也让用户指定的命令行选项来覆盖检测),设置相关extra_compiler_flags,并extra_linker_flags在构造函数中,然后将相关的文件,例如从复制contriblib(他们会自动拿起来ExtUtils::CBuilder).现在,分发是针对平台定制的 - 接下来的步骤(./Build ; …)将正常工作.