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   724 use strict;
  19         45  
  19         585  
4 19     19   98 use warnings;
  19         41  
  19         427  
5 19     19   318 use 5.008004;
  19         97  
6 19     19   90 use Carp ();
  19         75  
  19         20000  
7              
8             # ABSTRACT: Bundle foreign code with your Perl module
9             our $VERSION = '2.08'; # VERSION
10              
11              
12             package FFI::Platypus;
13              
14             sub _bundle
15             {
16 27     27   74 my @arg_ptrs;
17              
18 27 100 66     247 if(defined $_[-1] && ref($_[-1]) eq 'ARRAY')
19             {
20 1         4 @arg_ptrs = @{ pop @_ };
  1         3  
21             }
22              
23 27         78 push @arg_ptrs, undef;
24              
25 27         80 my($self, $package) = @_;
26 27 100       145 $package = caller unless defined $package;
27              
28 27         175 require List::Util;
29              
30 27         56 my($pm) = do {
31 27         112 my $pm = "$package.pm";
32 27         167 $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   203 List::Util::first(sub { (defined $_) && (-f $_) }, ($INC{$pm}, map { "$_/$pm" } @INC));
  28         801  
  298         937  
37             };
38              
39 27 50       197 Carp::croak "unable to find module $package" unless $pm;
40              
41 27         142 my @parts = split /::/, $package;
42 27         68 my $incroot = $pm;
43             {
44 27         48 my $c = @parts;
  27         59  
45 27         558 $incroot =~ s![\\/][^\\/]+$!! while $c--;
46             }
47              
48 27     52   141 my $txtfn = List::Util::first(sub { -f $_ }, do {
  52         1045  
49 27         104 my $dir = join '/', @parts;
50 27         87 my $file = $parts[-1] . ".txt";
51             (
52 27         167 "$incroot/auto/$dir/$file",
53             "$incroot/../arch/auto/$dir/$file",
54             );
55             });
56              
57 27         148 my $lib;
58              
59 27 100       150 if($txtfn)
    50          
60             {
61 24         42 $lib = do {
62 24         67 my $fh;
63 24 50       1028 open($fh, '<', $txtfn) or die "unable to read $txtfn $!";
64 24         514 my $line = <$fh>;
65 24         276 close $fh;
66 24 50       357 $line =~ /^FFI::Build\@(.*)$/
67             ? "$incroot/$1"
68             : Carp::croak "bad format $txtfn";
69             };
70 24 50       636 Carp::croak "bundle code is missing: $lib" unless -f $lib;
71             }
72             elsif(-d "$incroot/../ffi")
73             {
74 3         1024 require FFI::Build::MM;
75 3         20 require Capture::Tiny;
76 3         13 require Cwd;
77 3         11 require File::Spec;
78 3         37 my $save = Cwd::getcwd();
79 3         58 chdir "$incroot/..";
80             my($output, $error) = Capture::Tiny::capture_merged(sub {
81 3     3   5114 $lib = eval {
82 3         11 my $dist_name = $package;
83 3         29 $dist_name =~ s/::/-/g;
84 3         78 my $fbmm = FFI::Build::MM->new( save => 0 );
85 3         26 $fbmm->mm_args( DISTNAME => $dist_name );
86 3         12 my $build = $fbmm->load_build('ffi', undef, 'ffi/_build');
87 3         19 $build->build;
88             };
89 3         79 $@;
90 3         134 });
91 3 50       4578 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         261 $lib = File::Spec->rel2abs($lib);
100 3         82 chdir $save;
101             }
102             }
103             else
104             {
105 0         0 Carp::croak "unable to find bundle code for $package";
106             }
107              
108 27 50       3939 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         276 $self->{handles}->{$lib} = $handle;
112              
113 27         276 $self->lib($lib);
114              
115 27 100       71 if(my $init = eval { $self->function( 'ffi_pl_bundle_init' => [ 'string', 'sint32', 'opaque[]' ] => 'void' ) })
  27         202  
116             {
117 1         102 $init->call($package, scalar(@arg_ptrs)-1, \@arg_ptrs);
118             }
119              
120 27 100       1300 if(my $init = eval { $self->function( 'ffi_pl_bundle_constant' => [ 'string', 'opaque' ] => 'void' ) })
  27         225  
121             {
122 1         14 require FFI::Platypus::Constant;
123 1         31 my $api = FFI::Platypus::Constant->new($package);
124 1         15 $init->call($package, $api->ptr);
125             }
126              
127 27 100       865 if(my $address = $self->find_symbol( 'ffi_pl_bundle_fini' ))
128             {
129 1         17 push @{ $self->{fini} }, sub {
130 1     1   12 my $self = shift;
131 1         11 $self->function( $address => [ 'string' ] => 'void' )
132             ->call( $package );
133 1         9 };
134             }
135              
136 27         155 $self;
137             }
138              
139             1;
140              
141             __END__