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