File Coverage

blib/lib/Perl/Build.pm
Criterion Covered Total %
statement 73 200 36.5
branch 4 78 5.1
condition 2 41 4.8
subroutine 20 30 66.6
pod 4 14 28.5
total 103 363 28.3


line stmt bran cond sub pod time code
1             package Perl::Build;
2 3     3   129614 use strict;
  3         28  
  3         89  
3 3     3   13 use warnings;
  3         5  
  3         80  
4 3     3   1797 use utf8;
  3         41  
  3         14  
5              
6 3     3   152 use 5.008001;
  3         9  
7             our $VERSION = '1.33';
8              
9 3     3   16 use Carp ();
  3         5  
  3         42  
10 3     3   14 use File::Basename;
  3         4  
  3         300  
11 3     3   1320 use File::Spec::Functions qw(catfile catdir rel2abs);
  3         2526  
  3         203  
12 3     3   1579 use CPAN::Perl::Releases;
  3         6677  
  3         251  
13 3     3   1518 use CPAN::Perl::Releases::MetaCPAN;
  3         51301  
  3         153  
14 3     3   1323 use File::pushd qw(pushd);
  3         53244  
  3         169  
15 3     3   19 use File::Temp;
  3         6  
  3         225  
16 3     3   16 use HTTP::Tinyish;
  3         23  
  3         63  
17 3     3   12 use JSON::PP qw(decode_json);
  3         6  
  3         138  
18 3     3   4706 use Devel::PatchPerl 0.88;
  3         343638  
  3         182  
19 3     3   1238 use Perl::Build::Built;
  3         6  
  3         83  
20 3     3   1366 use Time::Local;
  3         3992  
  3         6884  
21              
22             our $CPAN_MIRROR = $ENV{PERL_BUILD_CPAN_MIRROR} || 'https://cpan.metacpan.org';
23              
24             sub available_perls {
25 0     0 0 0 my $class = shift;
26              
27 0         0 my $releases = CPAN::Perl::Releases::MetaCPAN->new->get;
28 0         0 my @available_versions;
29 0         0 for my $release (@$releases) {
30 0 0       0 if ($release->{name} =~ /^perl-(5.(\d+).(\d+)(-\w+)?)$/) {
31 0         0 my ($version, $major, $minor, $rc) = ($1, $2, $3, $4);
32 0   0     0 my $sort_by = sprintf "%03d%03d%s", $major, $minor, $rc || "ZZZ";
33 0         0 push @available_versions, { version => $version, sort_by => $sort_by };
34             }
35             }
36 0         0 map { $_->{version} } sort { $b->{sort_by} cmp $a->{sort_by} } @available_versions;
  0         0  
  0         0  
37             }
38              
39             # @return extracted source directory
40             sub extract_tarball {
41 0     0 0 0 my ($class, $dist_tarball, $destdir) = @_;
42              
43             # Was broken on Solaris, where GNU tar is probably
44             # installed as 'gtar' - RT #61042
45 0 0       0 my $tar = $^O eq 'solaris' ? 'gtar' : 'tar';
46              
47 0 0       0 my $type
    0          
48             = $dist_tarball =~ m/bz2$/ ? 'j'
49             : $dist_tarball =~ m/xz$/ ? 'J'
50             : 'z';
51              
52 0         0 my $abs_tarball = File::Spec->rel2abs($dist_tarball);
53              
54 0         0 my @tar_files = `$tar t${type}f "$abs_tarball"`;
55 0 0       0 $? == 0
56             or die "Failed to extract $dist_tarball";
57              
58 0         0 chomp @tar_files;
59 0         0 my %seen;
60 0         0 my @prefixes = grep !$seen{$_}++, map m{\A(?:\./)?([^/]+)}, @tar_files;
61              
62 0 0       0 die "$dist_tarball does not contain single directory : @prefixes"
63             if @prefixes != 1;
64              
65 0 0       0 system(qq{cd "$destdir"; $tar x${type}f "$abs_tarball"}) == 0
66             or die "Failed to extract $dist_tarball";
67              
68 0         0 return catfile($destdir, $prefixes[0]);
69             }
70              
71             sub perl_release {
72 5     5 0 3827 my ($class, $version) = @_;
73              
74 5         20 my ($dist_tarball, $dist_tarball_url);
75 5         0 my @err;
76 5         7 for my $func (qw/cpan_perl_releases metacpan/) {
77 5         7 eval {
78 5         32 ($dist_tarball, $dist_tarball_url) = $class->can("perl_release_by_$func")->($class,$version);
79             };
80 5 50       18 push @err, "[$func] $@" if $@;
81 5 50 33     23 last if $dist_tarball && $dist_tarball_url;
82             }
83 5 0 33     13 if (!$dist_tarball and !$dist_tarball_url) {
84 0         0 push @err, "ERROR: Cannot find the tarball for perl-$version\n";
85 0         0 die join "", @err;
86             }
87              
88 5         14 return ($dist_tarball, $dist_tarball_url);
89             }
90              
91             sub perl_release_by_cpan_perl_releases {
92 5     5 0 8 my ($class, $version) = @_;
93 5         18 my $tarballs = CPAN::Perl::Releases::perl_tarballs($version);
94 5   0     383 my $x = $tarballs->{'tar.gz'} || $tarballs->{'tar.bz2'} || $tarballs->{'tar.xz'};
95 5 50       11 die "not found the tarball for perl-$version\n" unless $x;
96 5         16 my $dist_tarball = (split("/", $x))[-1];
97 5         13 my $dist_tarball_url = $CPAN_MIRROR . "/authors/id/$x";
98 5         17 return ($dist_tarball, $dist_tarball_url);
99             }
100              
101             sub perl_release_by_metacpan {
102 0     0 0 0 my ($class, $version) = @_;
103 0         0 my $releases = CPAN::Perl::Releases::MetaCPAN->new->get;
104 0         0 for my $release (@$releases) {
105 0 0       0 if ($release->{name} eq "perl-$version") {
106 0         0 my ($path) = $release->{download_url} =~ m{(/authors/id/.*)};
107 0         0 my $dist_tarball = (split("/", $path))[-1];
108 0         0 my $dist_tarball_url = $CPAN_MIRROR . $path;
109 0         0 return ($dist_tarball, $dist_tarball_url);
110             }
111             }
112 0         0 die "not found the tarball for perl-$version\n";
113             }
114              
115             sub http_get {
116 0     0 0 0 my ($url) = @_;
117              
118 0         0 my $http = HTTP::Tinyish->new(verify_SSL => 1);
119 0         0 my $response = $http->get($url);
120 0 0       0 if ($response->{success}) {
121 0         0 return $response->{content};
122             } else {
123 0 0       0 my $msg = $response->{status} == 599 ? ", $response->{content}" : "";
124 0         0 chomp $msg;
125 0         0 die "Cannot get content from $url: $response->{status} $response->{reason}$msg\n";
126             }
127             }
128              
129             sub http_mirror {
130 0     0 0 0 my ($url, $path) = @_;
131              
132 0         0 my $http = HTTP::Tinyish->new(verify_SSL => 1);
133 0         0 my $response = $http->mirror($url, $path);
134 0 0       0 if ($response->{success}) {
135 0         0 print "Downloaded $url to $path.\n";
136             } else {
137 0 0       0 my $msg = $response->{status} == 599 ? ", $response->{content}" : "";
138 0         0 chomp $msg;
139 0         0 die "Cannot get file from $url: $response->{status} $response->{reason}$msg";
140             }
141             }
142              
143             sub install_from_cpan {
144 0     0 1 0 my ($class, $version, %args) = @_;
145              
146 0 0       0 $args{patchperl} && Carp::croak "The patchperl argument was deprected.";
147              
148             my $tarball_dir = $args{tarball_dir}
149 0   0     0 || File::Temp::tempdir( CLEANUP => 1 );
150             my $build_dir = $args{build_dir}
151 0   0     0 || File::Temp::tempdir( CLEANUP => 1 );
152             my $dst_path = $args{dst_path}
153 0 0       0 or die "Missing mandatory parameter: dst_path";
154             my $configure_options = $args{configure_options}
155 0   0     0 || ['-de'];
156              
157             # download tar ball
158 0         0 my ($dist_tarball, $dist_tarball_url) = Perl::Build->perl_release($version);
159 0         0 my $dist_tarball_path = catfile($tarball_dir, $dist_tarball);
160 0 0       0 if (-f $dist_tarball_path) {
161 0         0 print "Use the previously fetched ${dist_tarball}\n";
162             }
163             else {
164 0         0 print "Fetching $version as $dist_tarball_path ($dist_tarball_url)\n";
165 0         0 http_mirror( $dist_tarball_url, $dist_tarball_path );
166             }
167              
168             # and extract tar ball.
169 0         0 my $dist_extracted_path = Perl::Build->extract_tarball($dist_tarball_path, $build_dir);
170             Perl::Build->install(
171             src_path => $dist_extracted_path,
172             dst_path => $dst_path,
173             configure_options => $configure_options,
174             test => $args{test},
175             jobs => $args{jobs},
176 0         0 );
177             }
178              
179             sub install_from_url {
180 0     0 0 0 my ($class, $dist_tarball_url, %args) = @_;
181 0 0       0 $args{patchperl} && Carp::croak "The patchperl argument was deprected.";
182              
183             my $build_dir = $args{build_dir}
184 0   0     0 || File::Temp::tempdir( CLEANUP => 1 );
185             my $tarball_dir = $args{tarball_dir}
186 0   0     0 || File::Temp::tempdir( CLEANUP => 1 );
187             my $dst_path = $args{dst_path}
188 0 0       0 or die "Missing mandatory parameter: dst_path";
189             my $configure_options = $args{configure_options}
190 0   0     0 || ['-de'];
191              
192 0         0 my $dist_tarball = basename($dist_tarball_url);
193 0         0 my $dist_tarball_path = catfile($tarball_dir, $dist_tarball);
194 0 0       0 if (-f $dist_tarball_path) {
195 0         0 print "Use the previously fetched ${dist_tarball}\n";
196             }
197             else {
198 0         0 print "Fetching $dist_tarball_path ($dist_tarball_url)\n";
199 0         0 http_mirror( $dist_tarball_url, $dist_tarball_path );
200             }
201              
202 0         0 my $dist_extracted_path = Perl::Build->extract_tarball($dist_tarball_path, $build_dir);
203             Perl::Build->install(
204             src_path => $dist_extracted_path,
205             dst_path => $dst_path,
206             configure_options => $configure_options,
207             test => $args{test},
208             jobs => $args{jobs},
209 0         0 );
210             }
211              
212             sub install_from_tarball {
213 0     0 1 0 my ($class, $dist_tarball_path, %args) = @_;
214 0 0       0 $args{patchperl} && Carp::croak "The patchperl argument was deprected.";
215              
216             my $build_dir = $args{build_dir}
217 0   0     0 || File::Temp::tempdir( CLEANUP => 1 );
218             my $dst_path = $args{dst_path}
219 0 0       0 or die "Missing mandatory parameter: dst_path";
220             my $configure_options = $args{configure_options}
221 0   0     0 || ['-de'];
222              
223 0         0 my $dist_extracted_path = Perl::Build->extract_tarball($dist_tarball_path, $build_dir);
224             Perl::Build->install(
225             src_path => $dist_extracted_path,
226             dst_path => $dst_path,
227             configure_options => $configure_options,
228             test => $args{test},
229             jobs => $args{jobs},
230 0         0 );
231             }
232              
233             sub install {
234 0     0 1 0 my ($class, %args) = @_;
235 0 0       0 $args{patchperl} && Carp::croak "The patchperl argument was deprected.";
236              
237             my $src_path = $args{src_path}
238 0 0       0 or die "Missing mandatory parameter: src_path";
239             my $dst_path = $args{dst_path}
240 0 0       0 or die "Missing mandatory parameter: dst_path";
241             my $configure_options = $args{configure_options}
242 0 0       0 or die "Missing mandatory parameter: configure_options";
243 0         0 my $jobs = $args{jobs}; # optional
244 0         0 my $test = $args{test}; # optional
245              
246 0         0 unshift @$configure_options, qq(-Dprefix=$dst_path);
247              
248             # Perl5 installs public executable scripts(like `prove`) to /usr/local/share/
249             # if it exists.
250             #
251             # This -A'eval:scriptdir=$prefix/bin' option avoid this feature.
252 0 0       0 unless (grep { /eval:scriptdir=/} @$configure_options) {
  0         0  
253 0         0 push @$configure_options, "-A'eval:scriptdir=${dst_path}/bin'";
254             }
255              
256             # clean up environment
257 0         0 delete $ENV{$_} for qw(PERL5LIB PERL5OPT);
258              
259             {
260 0         0 my $dir = pushd($src_path);
  0         0  
261              
262             # determine_version is a public API.
263 0         0 my $dist_version = Devel::PatchPerl->determine_version();
264 0         0 print "Configuring perl '$dist_version'\n";
265              
266             # clean up
267 0         0 $class->do_system("rm -f config.sh Policy.sh");
268              
269             # apply patches
270 0         0 Devel::PatchPerl->patch_source();
271              
272             # configure
273 0         0 $class->do_system(['sh', 'Configure', @$configure_options]);
274             # patch for older perls
275             # XXX is this needed? patchperl do this?
276             # if (Perl::Build->perl_version_to_integer($dist_version) < Perl::Build->perl_version_to_integer( '5.8.9' )) {
277             # $class->do_system("$^X -i -nle 'print unless /command-line/' makefile x2p/makefile");
278             # }
279              
280             # build
281 0         0 my @make = qw(make);
282 0 0       0 if ($ENV{PERL_BUILD_COMPILE_OPTIONS}) {
283 0         0 push @make, $ENV{PERL_BUILD_COMPILE_OPTIONS};
284             }
285 0 0       0 if ($jobs) {
286 0         0 push @make, '-j', $jobs;
287             }
288 0         0 $class->do_system(\@make);
289 0 0       0 if ($test) {
290 0 0       0 local $ENV{TEST_JOBS} = $jobs if $jobs;
291             # Test via "make test_harness" if available so we'll get
292             # automatic parallel testing via $HARNESS_OPTIONS. The
293             # "test_harness" target was added in 5.7.3, which was the last
294             # development release before 5.8.0.
295 0         0 my $test_target = 'test';
296 0 0 0     0 if ($dist_version && $dist_version =~ /^5\.([0-9]+)\.([0-9]+)/
      0        
      0        
297             && ($1 >= 8 || $1 == 7 && $2 == 3)) {
298 0         0 $test_target = "test_harness";
299             }
300 0         0 $class->do_system([@make, $test_target]);
301             }
302 0         0 @make = qw(make install);
303 0 0       0 if ($ENV{PERL_BUILD_INSTALL_OPTIONS}) {
304 0         0 push @make, $ENV{PERL_BUILD_INSTALL_OPTIONS};
305             }
306 0         0 $class->do_system(\@make);
307             }
308 0         0 return Perl::Build::Built->new({
309             installed_path => $dst_path,
310             });
311             }
312              
313             sub do_system {
314 0     0 0 0 my ($class, $cmd) = @_;
315              
316 0 0       0 if (ref $cmd eq 'ARRAY') {
317 0         0 $class->info(join(' ', @$cmd));
318 0 0       0 system(@$cmd) == 0
319             or die "Installation failure: @$cmd";
320             } else {
321 0         0 $class->info($cmd);
322 0 0       0 system($cmd) == 0
323             or die "Installation failure: $cmd";
324             }
325             }
326              
327             sub symlink_devel_executables {
328 1     1 1 107 my ($class, $bin_dir) = @_;
329              
330 1         6 for my $executable (glob("$bin_dir/*")) {
331 1         63 my ($name, $version) = basename( $executable ) =~ m/(.+?)(5\.\d.*)?$/;
332 1 50       4 if ($version) {
333 1         5 my $cmd = "ln -fs $executable $bin_dir/$name";
334 1         4 $class->info($cmd);
335 1         6 system($cmd);
336             }
337             }
338             }
339              
340             sub info {
341 1     1 0 3 my ($class, @msg) = @_;
342 1         43 print @msg, "\n";
343             }
344              
345             1;
346             __END__