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