File Coverage

blib/lib/Alien/bz2/Installer.pm
Criterion Covered Total %
statement 13 207 6.2
branch 2 88 2.2
condition 0 33 0.0
subroutine 5 20 25.0
pod 13 13 100.0
total 33 361 9.1


line stmt bran cond sub pod time code
1             package Alien::bz2::Installer;
2              
3 8     8   329676 use strict;
  8         21  
  8         272  
4 8     8   41 use warnings;
  8         14  
  8         23243  
5              
6             # ABSTRACT: Installer for bz2
7             our $VERSION = '0.03'; # VERSION
8              
9             sub _catfile {
10 0     0   0 my $path = File::Spec->catfile(@_);
11 0 0       0 $path =~ s{\\}{/}g if $^O eq 'MSWin32';
12 0         0 $path;
13             }
14              
15             sub _catdir {
16 0     0   0 my $path = File::Spec->catdir(@_);
17 0 0       0 $path =~ s{\\}{/}g if $^O eq 'MSWin32';
18 0         0 $path;
19             }
20              
21              
22             sub versions_available
23             {
24 1 50   1 1 16 ($^O eq 'MSWin32' ? '1.0.5' : '1.0.6');
25             }
26              
27              
28             sub fetch
29             {
30 0     0 1 0 my($class, %options) = @_;
31            
32 0   0     0 my $dir = $options{dir} || eval { require File::Temp; File::Temp::tempdir( CLEANUP => 1 ) };
33            
34             # actually we ignore the version argument.
35              
36 0         0 require File::Spec;
37            
38 0         0 my $url = 'http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz';
39 0         0 my $fn = _catfile($dir, 'bzip2-1.0.6.tar.gz');
40 0         0 my($version) = $class->versions_available;
41 0 0       0 if($^O eq 'MSWin32')
42             {
43 0         0 $url = 'http://gnuwin32.sourceforge.net/downlinks/bzip2-src-zip.php';
44 0         0 $fn = _catfile($dir, 'bzip2-1.0.5-src.zip');
45             }
46            
47 0         0 require HTTP::Tiny;
48 0         0 my $response = HTTP::Tiny->new->get($url);
49            
50 0 0       0 die sprintf("%s %s %s", $response->{status}, $response->{reason}, $url)
51             unless $response->{success};
52              
53 0         0 open my $fh, '>', $fn;
54 0         0 binmode $fh;
55 0         0 print $fh $response->{content};
56 0         0 close $fh;
57            
58 0 0       0 wantarray ? ($fn, $version) : $fn;
59             }
60              
61              
62             sub build_requires
63             {
64 1     1 1 13 my %prereqs = (
65             'HTTP::Tiny' => 0,
66             );
67            
68 1 50       6 if($^O eq 'MSWin32')
69             {
70 0         0 $prereqs{'Archive::Zip'} = 0;
71 0         0 $prereqs{'Alien::o2dll'} = 0;
72 0         0 $prereqs{'Alien::MSYS'} = 0;
73             }
74             else
75             {
76 1         3 $prereqs{'Archive::Tar'} = 0;
77             }
78            
79 1         4 \%prereqs;
80             }
81              
82              
83             sub system_requires
84             {
85 1     1 1 11 my %prereqs;
86 1         4 \%prereqs;
87             }
88              
89              
90             sub system_install
91             {
92 0     0 1   my($class, %options) = @_;
93            
94 0 0         $options{alien} = 1 unless defined $options{alien};
95 0   0       $options{test} ||= 'compile';
96 0 0         die "test must be one of compile, ffi or both"
97             unless $options{test} =~ /^(compile|ffi|both)$/;
98            
99 0           my $build = bless {
100             cflags => [],
101             libs => ['-lbz2'],
102             }, $class;
103            
104 0 0 0       $build->test_compile_run || die $build->error if $options{test} =~ /^(compile|both)$/;
105 0 0 0       $build->test_ffi || die $build->error if $options{test} =~ /^(ffi|both)$/;
106 0           $build;
107             }
108              
109              
110             sub _msys
111             {
112 0     0     my($sub) = @_;
113 0           require Config;
114 0 0         if($^O eq 'MSWin32')
115             {
116 0 0         if($Config::Config{cc} !~ /cl(\.exe)?$/i)
117             {
118 0           require Alien::MSYS;
119 0     0     return Alien::MSYS::msys(sub{ $sub->('make') });
  0            
120             }
121             }
122 0           $sub->($Config::Config{make});
123             }
124              
125             sub build_install
126             {
127 0     0 1   my($class, $prefix, %options) = @_;
128            
129 0   0       $options{test} ||= 'compile';
130 0 0         die "test must be one of compile, ffi or both"
131             unless $options{test} =~ /^(compile|ffi|both)$/;
132 0 0         die "need an install prefix" unless $prefix;
133            
134 0           $prefix =~ s{\\}{/}g;
135            
136 0   0       my $dir = $options{dir} || do { require File::Temp; File::Temp::tempdir( CLEANUP => 1 ) };
137            
138 0           require Cwd;
139 0           require File::Spec;
140 0           my $save = Cwd::getcwd();
141            
142 0           my $build = eval {
143 0 0         if($^O eq 'MSWin32')
144             {
145 0           require Archive::Zip;
146 0           my $zip = Archive::Zip->new;
147 0   0       $zip->read(scalar $options{tar} || $class->fetch);
148 0           chdir $dir;
149 0           mkdir 'bzip2-1.0.5';
150 0           chdir 'bzip2-1.0.5';
151 0           $zip->extractTree;
152 0           chdir(_catdir(qw( src bzip2 1.0.5 bzip2-1.0.5 )));
153             }
154             else
155             {
156 0           require Archive::Tar;
157 0           my $tar = Archive::Tar->new;
158 0   0       $tar->read($options{tar} || $class->fetch);
159 0           chdir $dir;
160 0           $tar->extract;
161 0           chdir do {
162 0           opendir my $dh, '.';
163 0           my(@list) = grep !/^\./,readdir $dh;
164 0           close $dh;
165 0 0         die "unable to find source in build root" if @list == 0;
166 0 0         die "confused by multiple entries in the build root" if @list > 1;
167 0           $list[0];
168             };
169             }
170            
171 0 0         if($^O eq 'MSWin32')
172             {
173 0           open my $fh, '<', 'Makefile';
174 0           my $makefile = do { local $/; <$fh> };
  0            
  0            
175 0           close $fh;
176            
177 0           $makefile =~ s/\to2dll/\t$^X -MAlien::o2dll=o2dll o2dll.pl/g;
178            
179 0           open $fh, '>', 'Makefile';
180 0           print $fh $makefile;
181 0           close $fh;
182            
183 0           open $fh, '>', 'o2dll.pl';
184 0           print $fh "use Alien::o2dll qw( o2dll );\n";
185 0           print $fh "o2dll(\@ARGV)\n";
186 0           close $fh;
187            
188             _msys(sub {
189 0     0     system 'make', 'all';
190 0 0         die "make all failed" if $?;
191 0           system 'make', 'install', "PREFIX=$prefix";
192 0 0         die "make install failed" if $?;
193 0           });
194 0           mkdir(_catdir($prefix, 'dll'));
195 0           File::Copy::copy('bzip2.dll', _catfile($prefix, 'dll', 'bzip2.dll'));
196 0           File::Copy::copy('libbz2.dll.a', _catfile($prefix, 'dll', 'libbz2.dll.a'));
197             }
198             else
199             {
200 0           require Config;
201 0           require File::Copy;
202 0           my $make = $Config::Config{make};
203 0           system $make, -f => 'Makefile-libbz2_so';
204 0 0         die "make -f Makefile-libbz2_so failed" if $?;
205 0           system $make, 'all';
206 0 0         die "make all failed" if $?;
207 0           system $make, 'install', "PREFIX=$prefix";
208 0 0         die "make install failed" if $?;
209 0           mkdir(_catdir($prefix, 'dll'));
210 0           File::Copy::copy('libbz2.so.1.0.6', _catfile($prefix, 'dll', 'libbz2.so.1.0.6'));
211 0           eval { chmod 0755, _catfile($prefix, 'dll', 'libbz2.so.1.0.6') };
  0            
212             }
213            
214             my $build = bless {
215             cflags => [ "-I" . _catdir($prefix, 'include') ],
216             libs => [ "-L" . _catdir($prefix, 'lib'), '-lbz2' ],
217             prefix => $prefix,
218             dll_dir => [ 'dll' ],
219 0           dlls => do {
220 0           opendir(my $dh, File::Spec->catdir($prefix, 'dll'));
221 0 0         [grep { ! -l File::Spec->catfile($prefix, 'dll', $_) } grep { /\.so/ || /\.(dll|dylib)$/ } grep !/^\./, readdir $dh];
  0            
  0            
222             },
223             }, $class;
224            
225 0 0 0       $build->test_compile_run || die $build->error if $options{test} =~ /^(compile|both)$/;
226 0 0 0       $build->test_ffi || die $build->error if $options{test} =~ /^(ffi|both)$/;
227            
228 0           $build;
229             };
230            
231 0           my $error = $@;
232 0           chdir $save;
233 0 0         die $error if $error;
234 0           $build;
235             }
236              
237              
238              
239 0     0 1   sub cflags { shift->{cflags} }
240 0     0 1   sub libs { shift->{libs} }
241 0     0 1   sub version { shift->{version} }
242              
243             sub dlls
244             {
245 0     0 1   my($self, $prefix) = @_;
246            
247 0 0         $prefix = $self->{prefix} unless defined $prefix;
248            
249 0           require File::Spec;
250            
251 0 0 0       unless(defined $self->{dlls} && defined $self->{dll_dir})
252             {
253 0 0         if($^O eq 'cygwin')
254             {
255 0           opendir my $dh, '/usr/bin';
256 0           $self->{dlls} = [grep /^cygbz2-[0-9]+\.dll$/i, readdir $dh];
257 0           $self->{dll_dir} = [];
258 0           $prefix = '/usr/bin';
259 0           closedir $dh;
260             }
261             else
262             {
263 0           require DynaLoader;
264 0           my $path = DynaLoader::dl_findfile(grep /^-l/, @{ $self->libs});
  0            
265 0 0         die "unable to find dynamic library" unless defined $path;
266 0           my($vol, $dirs, $file) = File::Spec->splitpath($path);
267 0 0         if($^O eq 'openbsd')
268             {
269             # on openbsd we get the .a file back, so have to scan
270             # for .so.#.# as there is no .so symlink
271 0           opendir(my $dh, $dirs);
272 0           $self->{dlls} = [grep /^libbz2.so/, readdir $dh];
273 0           closedir $dh;
274             }
275             else
276             {
277 0           $self->{dlls} = [ $file ];
278             }
279 0           $self->{dll_dir} = [];
280 0           $prefix = File::Spec->catpath($vol, $dirs);
281 0           $prefix =~ s{\\}{/}g;
282             }
283             }
284            
285 0           map { _catfile($prefix, @{ $self->{dll_dir} }, $_) } @{ $self->{dlls} };
  0            
  0            
  0            
286             }
287              
288              
289             sub test_compile_run
290             {
291 0     0 1   my($self, %opt) = @_;
292            
293 0           delete $self->{error};
294 0   0       my $cbuilder = $opt{cbuilder} || do { require ExtUtils::CBuilder; ExtUtils::CBuilder->new(quiet => 1) };
295            
296 0 0         unless($cbuilder->have_compiler)
297             {
298 0           $self->{error} = 'no compiler';
299 0           return;
300             }
301            
302 0   0       my $dir = $opt{dir} || do { require File::Temp; File::Temp::tempdir(CLEANUP => 1) };
303 0           require File::Spec;
304 0           my $fn = _catfile($dir, 'test.c');
305 0           do {
306 0           open my $fh, '>', $fn;
307 0           print $fh "#include \n",
308             "#include \n",
309             "int\n",
310             "main(int argc, char *argv[])\n",
311             "{\n",
312             " printf(\"version = '%s'\\n\", BZ2_bzlibVersion());\n",
313             " return 0;\n",
314             "}\n";
315 0           close $fh;
316             };
317            
318 0           my $test_exe = eval {
319 0           my $test_object = $cbuilder->compile(
320             source => $fn,
321             extra_compiler_flags => $self->cflags,
322             );
323 0           $cbuilder->link_executable(
324             objects => $test_object,
325             extra_linker_flags => $self->libs,
326             );
327             };
328              
329 0 0         if(my $error = $@)
330             {
331 0           $self->{error} = $error;
332 0           return;
333             }
334            
335 0 0         if($test_exe =~ /\s/)
336             {
337 0 0         $test_exe = Win32::GetShortPathName($test_exe) if $^O eq 'MSWin32';
338 0 0         $test_exe = Cygwin::win_to_posix_path(Win32::GetShortPathName(Cygwin::posix_to_win_path($test_exe))) if $^O eq 'cygwin';
339             }
340            
341 0           my $output = `$test_exe`;
342              
343 0 0         if($?)
344             {
345 0 0         if($? == -1)
    0          
346             {
347 0           $self->{error} = "failed to execute $!";
348             }
349             elsif($? & 127)
350             {
351 0           $self->{error} = "child died with signal" . ($? & 127);
352             }
353             else
354             {
355 0           $self->{error} = "child exited with value " . ($? >> 8);
356             }
357 0           return;
358             }
359            
360 0 0         if($output =~ /version = '(.*?),/)
361             {
362 0           return $self->{version} = $1;
363             }
364             else
365             {
366 0           $self->{error} = "unable to retrieve version from output";
367 0           return;
368             }
369             }
370              
371              
372             sub test_ffi
373             {
374 0     0 1   my($self) = @_;
375 0           require FFI::Raw;
376            
377 0           foreach my $dll ($self->dlls)
378             {
379 0           my $get_version = eval {
380 0           FFI::Raw->new(
381             $dll, 'BZ2_bzlibVersion', FFI::Raw::str(),
382             );
383             };
384 0 0         next if $@;
385 0 0         if($get_version->() =~ /^(.*?),/)
386             {
387 0           return $self->{version} = $1;
388             }
389             }
390 0           $self->{error} = 'BZ2_bzlibVersion not found (ffi)';
391 0           return;
392             }
393              
394              
395 0     0 1   sub error { $_[0]->{error} }
396              
397             1;
398              
399             __END__