Как я условно компилирую фрагменты кода C в свой модуль Perl?

У меня есть модуль, который будет предназначаться для нескольких различных операционных систем и конфигураций. Иногда, некоторый код C может сделать задачу этого модуля немного легче, таким образом, у меня есть некоторые функции C, что я хотел бы связать код. Я не должен связывать функции C - я не могу гарантировать, что у конечного пользователя даже есть компилятор C, например, и это обычно - не проблема к обработке отказа корректно к чистому Perl способ выполнить то же самое - но было бы хорошо, если я мог бы вызвать функции C из сценария Perl.

Все еще со мной? Вот другая хитрая часть. Примерно весь код C является конкретной системой - функция, записанная для Windows, не скомпилирует на Linux и наоборот, и функция, которая делает подобную вещь на Солярисе, будет выглядеть полностью отличающейся.

#include 
int foo_for_Windows_c(int a,double b)
{
  do_windows_stuff();
  return 42;
}

#include 
int foo_for_linux_c(int a,double b)
{
  do_linux_stuff(7);
  return 42;
}

Кроме того, даже для собственного кода, который предназначается для той же системы, возможно, что только некоторые из них могут быть скомпилированы на какой-то конкретной конфигурации.

#include 
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 в 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 ;
     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 , ;
     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 ;
   $self->ACTION_code();
   return $self->SUPER::ACTION_build(@_);
  }
}

Проверка на $@ вероятно, довольно хрупко. Это работает над системами, которые я попробовал (все использование gcc), но это, вероятно, не будет работать, как это записано везде.

5
задан mob 10 April 2010 в 04:52
поделиться

3 ответа

В идеале используйте Module :: Build . Во время настройки ( perl Build.PL ) определите платформу и расположение заголовка (но также позвольте пользователю указать параметры командной строки для отмены обнаружения), установите соответствующие extra_compiler_flags и extra_linker_flags в конструкторе, а затем скопируйте соответствующие файлы, например, из contrib в lib (где они будут автоматически загружены ExtUtils :: CBuilder ). Теперь дистрибутив настроен для платформы - следующие шаги ( ./ Build;… ) будут работать в обычном режиме.

5
ответ дан 14 December 2019 в 04:34
поделиться

В одном из моих модулей у меня есть следующий кусок кода:

my $C_support = Module::Build::ConfigData->feature("C_support")

my $builder = Module::Build->new(
    ...
    config_data => {
        C_support => $C_support
    }
);
$builder->xs_files({}) if not $C_support;

Затем в коде я обнаруживаю его, загружая Module_name::ConfigData и вызывая метод config.

if (Module_name::ConfigData->config("C_support")) {
    XSLoader::load(__PACKAGE__, $VERSION);
}
if (not defined &somefunction) {
    #define it
}

Для подробностей посмотрите мои Build.PL и Module.pm

2
ответ дан 14 December 2019 в 04:34
поделиться

Я использовал такие техники:

sub slow_function {
    # slow fallback perl code if possible
}
BEGIN {
    eval {
        require Inline;

        if ($condition) {
            Inline->import(C => q {
                int slow_function (...) {
                    // c function to conditionally compile
                }
            })
        } else {
            Inline->import(C => q {
                int slow_function (...) {
                    // c function with something different
                }
            })
        }   
        1;
    } or print STDERR "Inline::C error: $@ perl fallback used instead\n";
}
1
ответ дан 14 December 2019 в 04:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: