File Coverage

blib/lib/FFI/Platypus/Bundle.pm
Criterion Covered Total %
statement 80 85 94.1
branch 21 28 75.0
condition 2 3 66.6
subroutine 9 9 100.0
pod n/a
total 112 125 89.6


line stmt bran cond sub pod time code
1             package FFI::Platypus::Bundle;
2              
3 19     19   674 use strict;
  19         39  
  19         555  
4 19     19   99 use warnings;
  19         42  
  19         411  
5 19     19   301 use 5.008004;
  19         100  
6 19     19   92 use Carp ();
  19         76  
  19         19355  
7              
8             # ABSTRACT: Bundle foreign code with your Perl module
9             our $VERSION = '2.06_01'; # TRIAL VERSION
10              
11              
12             package FFI::Platypus;
13              
14             sub _bundle
15             {
16 27     27   65 my @arg_ptrs;
17              
18 27 100 66     241 if(defined $_[-1] && ref($_[-1]) eq 'ARRAY')
19             {
20 1         3 @arg_ptrs = @{ pop @_ };
  1         4  
21             }
22              
23 27         80 push @arg_ptrs, undef;
24              
25 27         79 my($self, $package) = @_;
26 27 100       132 $package = caller unless defined $package;
27              
28 27         150 require List::Util;
29              
30 27         62 my($pm) = do {
31 27         88 my $pm = "$package.pm";
32 27         196 $pm =~ s{::}{/}g;
33             # if the module is already loaded, we can use %INC
34             # otherwise we can go through @INC and find the first .pm
35             # this doesn't handle all edge cases, but probably enough
36 27 100   28   211 List::Util::first(sub { (defined $_) && (-f $_) }, ($INC{$pm}, map { "$_/$pm" } @INC));
  28         737  
  298         899  
37             };
38              
39 27 50       180 Carp::croak "unable to find module $package" unless $pm;
40              
41 27         130 my @parts = split /::/, $package;
42 27         78 my $incroot = $pm;
43             {
44 27         49 my $c = @parts;
  27         59  
45 27         613 $incroot =~ s![\\/][^\\/]+$!! while $c--;
46             }
47              
48 27     52   131 my $txtfn = List::Util::first(sub { -f $_ }, do {
  52         963  
49 27         129 my $dir = join '/', @parts;
50 27         79 my $file = $parts[-1] . ".txt";
51             (
52 27         161 "$incroot/auto/$dir/$file",
53             "$incroot/../arch/auto/$dir/$file",
54             );
55             });
56              
57 27         135 my $lib;
58              
59 27 100       139 if($txtfn)
    50          
60             {
61 24         47 $lib = do {
62 24         45 my $fh;
63 24 50       1006 open($fh, '<', $txtfn) or die "unable to read $txtfn $!";
64 24         478 my $line = <$fh>;
65 24         268 close $fh;
66 24 50       354 $line =~ /^FFI::Build\@(.*)$/
67             ? "$incroot/$1"
68             : Carp::croak "bad format $txtfn";
69             };
70 24 50       476 Carp::croak "bundle code is missing: $lib" unless -f $lib;
71             }
72             elsif(-d "$incroot/../ffi")
73             {
74 3         938 require FFI::Build::MM;
75 3         21 require Capture::Tiny;
76 3         17 require Cwd;
77 3         15 require File::Spec;
78 3         38 my $save = Cwd::getcwd();
79 3         70 chdir "$incroot/..";
80             my($output, $error) = Capture::Tiny::capture_merged(sub {
81 3     3   4208 $lib = eval {
82 3         13 my $dist_name = $package;
83 3         28 $dist_name =~ s/::/-/g;
84 3         35 my $fbmm = FFI::Build::MM->new( save => 0 );
85 3         18 $fbmm->mm_args( DISTNAME => $dist_name );
86 3         15 my $build = $fbmm->load_build('ffi', undef, 'ffi/_build');
87 3         17 $build->build;
88             };
89 3         60 $@;
90 3         131 });
91 3 50       3912 if($error)
92             {
93 0         0 chdir $save;
94 0         0 print STDERR $output;
95 0         0 die $error;
96             }
97             else
98             {
99 3         203 $lib = File::Spec->rel2abs($lib);
100 3         71 chdir $save;
101             }
102             }
103             else
104             {
105 0         0 Carp::croak "unable to find bundle code for $package";
106             }
107              
108 27 50       3750 my $handle = FFI::Platypus::DL::dlopen($lib, FFI::Platypus::DL::RTLD_PLATYPUS_DEFAULT())
109 0         0 or Carp::croak "error loading bundle code: $lib @{[ FFI::Platypus::DL::dlerror() ]}";
110              
111 27         316 $self->{handles}->{$lib} = $handle;
112              
113 27         213 $self->lib($lib);
114              
115 27 100       58 if(my $init = eval { $self->function( 'ffi_pl_bundle_init' => [ 'string', 'sint32', 'opaque[]' ] => 'void' ) })
  27         181  
116             {
117 1         67 $init->call($package, scalar(@arg_ptrs)-1, \@arg_ptrs);
118             }
119              
120 27 100       1181 if(my $init = eval { $self->function( 'ffi_pl_bundle_constant' => [ 'string', 'opaque' ] => 'void' ) })
  27         143  
121             {
122 1         27 require FFI::Platypus::Constant;
123 1         37 my $api = FFI::Platypus::Constant->new($package);
124 1         33 $init->call($package, $api->ptr);
125             }
126              
127 27 100       892 if(my $address = $self->find_symbol( 'ffi_pl_bundle_fini' ))
128             {
129 1         19 push @{ $self->{fini} }, sub {
130 1     1   7 my $self = shift;
131 1         23 $self->function( $address => [ 'string' ] => 'void' )
132             ->call( $package );
133 1         17 };
134             }
135              
136 27         153 $self;
137             }
138              
139             1;
140              
141             __END__