File Coverage

blib/lib/PPM/Make/Bundle.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package PPM::Make::Bundle;
2              
3 2     2   35392 use strict;
  2         5  
  2         151  
4 2     2   12 use warnings;
  2         3  
  2         61  
5 2     2   11 use Cwd;
  2         4  
  2         226  
6 2     2   1964 use File::Spec::Functions qw(:ALL);
  2         1895  
  2         533  
7 2     2   902 use File::Copy;
  2         6656  
  2         156  
8 2     2   13 use File::Path;
  2         3  
  2         99  
9 2     2   2959 use PPM::Make;
  0            
  0            
10             use PPM::Make::Util qw(:all);
11             use PPM::Make::Config qw(:all);
12             use PPM::Make::Search;
13              
14             our $VERSION = '0.9902';
15              
16             my @cpan_mirrors = url_list();
17             my $protocol = qr{^(http|ftp)://};
18             my $ext = qr{\.(tar\.gz|tgz|tar\.Z|zip)};
19              
20             sub new {
21             my ($class, %opts) = @_;
22              
23             my $bundle_name = delete $opts{bundle_name};
24             if ($bundle_name) {
25             $bundle_name =~ s{$ext$}{} if $bundle_name;
26             $bundle_name .= '.zip';
27             }
28              
29             my $clean = delete $opts{clean};
30              
31             my ($arch, $os) = arch_and_os($opts{arch}, $opts{os}, $opts{noas});
32             my $has = what_have_you($opts{program}, $arch, $os);
33              
34             die "\nInvalid option specification" unless check_opts(%opts);
35             my %cfg;
36             unless ($opts{no_cfg}) {
37             if (my $file = get_cfg_file()) {
38             %cfg = read_cfg($file, $arch) or die "\nError reading config file";
39             }
40             }
41             my $opts = %cfg ? merge_opts(\%cfg, \%opts) : \%opts;
42             my $search = PPM::Make::Search->new(
43             no_remote_lookup => $opts->{no_remote_lookup},
44             );
45              
46             my $cwd = cwd;
47             my $build_dir = catdir(tmpdir, "ppm_make-$$");
48             mkdir $build_dir or die qq{Cannot mkdir $build_dir: $!};
49             my $self = {cwd => $cwd, opts => $opts, files => {}, name => '',
50             build_dir => $build_dir, has => $has, zipdist => $bundle_name,
51             clean => $clean, arch => $arch, os => $os,
52             search => $search,
53             };
54             bless $self, $class;
55             }
56              
57             sub make_bundle {
58             my $self = shift;
59             $self->make_package($self->{opts}->{dist}) or return;
60             $self->make_zip() or return;
61             if ($self->{opts}->{upload}) {
62             $self->upload_zip() or return;
63             }
64             my $cwd = $self->{cwd};
65             chdir($cwd) or die qq{Cannot chdir to $cwd: $!};
66             if ($self->{clean}) {
67             chdir($self->{cwd}) or die qq{Cannot chdir to $self->{cwd}: $!};
68             my $build_dir = $self->{build_dir};
69             if (-d $build_dir) {
70             rmtree($build_dir, 1, 1) or warn qq{Cannot rmtree $build_dir: $!};
71             }
72             }
73             return 1;
74             }
75              
76             sub make_package {
77             my ($self, $dist, $info) = @_;
78              
79             my ($dist_name, $cpan_file);
80             if ($dist and $dist !~ /$ext$/) {
81             return 1 if (defined $self->{files}->{$dist} or is_ap_core($dist));
82             $info = $self->get_info($dist) unless ($info and (ref($info) eq 'HASH'));
83             $dist_name = $info->{dist_name};
84             $cpan_file = $info->{cpan_file};
85             }
86             my $name;
87             TRY: {
88             (not $dist and (-e 'Makefile.PL' || -e 'Build.PL')) and do {
89             last TRY if ($name = $self->from_cpan());
90             };
91             ($dist =~ /$ext$/) and do {
92             last TRY if ($name = $self->from_cpan($dist));
93             };
94             ($dist_name) and do {
95             last TRY if ($name = $self->from_repository($dist_name));
96             };
97             ($cpan_file) and do {
98             my $url = $cpan_mirrors[0] . '/authors/id/' . $cpan_file;
99             last TRY if ($name = $self->from_cpan($url));
100             };
101             last TRY if ($name = $self->from_cpan($dist));
102             die qq{Cannot build "$dist"};
103             }
104             $self->{name} ||= $name;
105             my $prereqs = $self->{files}->{$name}->{prereqs};
106             if ($prereqs and (ref($prereqs) eq 'ARRAY')) {
107             foreach my $item(@$prereqs) {
108             $self->make_package($item->{dist_name}, $item);
109             }
110             }
111             return 1;
112             }
113              
114             sub get_info {
115             my ($self, $dist) = @_;
116             return if (-f $dist or $dist =~ /^$protocol/ or $dist =~ /$ext$/);
117             my $search = $self->{search};
118             $dist =~ s{::}{-}g;
119             {
120             if ($search->search($dist, mode => 'dist')) {
121             my $results = $search->{dist_results}->{$dist};
122             my $cpan_file = cpan_file($results->{cpanid}, $results->{dist_file});
123             my $info = {cpan_file => $cpan_file, dist_name => $results->{dist_name}};
124             return $info;
125             }
126             else {
127             $search->search_error(qq{Cannot obtain information on '$dist'});
128             }
129             }
130             return;
131             }
132              
133             sub from_cpan {
134             my ($self, $pack) = @_;
135             my $ppm = PPM::Make->new(%{$self->{opts}}, dist => $pack, no_cfg => 1);
136             $ppm->make_ppm();
137             my $name;
138             if (defined $ppm->{ppd} and defined $ppm->{codebase}) {
139             ($name = $ppm->{ppd}) =~ s{\.ppd$}{};
140             (my $ar = $ppm->{codebase}) =~ s{.*/([^/]+)$}{$1};
141             $self->{files}->{$name} = {cwd => $ppm->{cwd},
142             ppd => $ppm->{ppd},
143             ar => $ar};
144             }
145             else {
146             return;
147             }
148             my @full_prereqs = keys %{$ppm->{args}->{PREREQ_PM}};
149             return $name unless (scalar @full_prereqs > 0);
150             my @prereqs = ();
151             foreach my $mod(@full_prereqs) {
152             push @prereqs, $mod unless ($mod eq 'perl' or is_core($mod));
153             }
154             my $search = $self->{search};
155             {
156             if (scalar @prereqs > 0) {
157             my $matches = $search->search(\@prereqs, mode => 'mod');
158             if ($matches and (ref($matches) eq 'HASH')) {
159             foreach my $mod (keys %$matches) {
160             my $item = $matches->{$mod};
161             my $dist_name = $item->{dist_name};
162             next if is_ap_core($dist_name);
163             my $cpan_file = cpan_file($item->{cpanid}, $item->{dist_file});
164             push @{$self->{files}->{$name}->{prereqs}},
165             {dist_name => $dist_name,
166             cpan_file => $cpan_file};
167             }
168             }
169             }
170             }
171             return $name;
172             }
173              
174             sub from_repository {
175             my ($self, $pack) = @_;
176             return if (-f $pack or $pack =~ /^$protocol/ or $pack =~ /$ext$/);
177             my $cwd = $self->{build_dir};
178             $pack =~ s/::/-/g;
179             my $reps = $self->{opts}->{reps};
180             return unless $reps;
181             my @reps = ref($reps) eq 'ARRAY' ? @$reps : ($reps);
182             chdir($cwd) or die qq{Cannot chdir to $cwd: $!};
183              
184             my $dist_name = $pack;
185             my $ppd_local = $dist_name . '.ppd';
186             my $arch = $self->{arch};
187             my ($url, $ppd_remote, $info);
188             foreach my $item (@reps) {
189             if ($item !~ /^$protocol/) {
190             $ppd_remote = catfile($item, $ppd_local);
191             if (-f $ppd_remote) {
192             copy($ppd_remote, $ppd_local) or do {
193             warn qq{Cannot copy "$ppd_remote" to "$ppd_local": $!};
194             return;
195             };
196             $info = parse_ppd(catfile($cwd, $ppd_local), $arch);
197             next unless ($info and (ref($info) eq 'HASH'));
198             my $info_arch = $info->{ARCHITECTURE}->{NAME};
199             if ($info_arch and ($info_arch eq $arch)) {
200             $url = $item;
201             print qq{\nUsing $ppd_local from $url\n};
202             last;
203             }
204             }
205             }
206             else {
207             $item .= '/' unless $item =~ m{/$};
208             my $ppd_remote = $item . $ppd_local;
209             if (head($ppd_remote)) {
210             if (mirror($ppd_remote, $ppd_local)) {
211             $info = parse_ppd(catfile($cwd, $ppd_local), $arch);
212             next unless ($info and (ref($info) eq 'HASH'));
213             my $info_arch = $info->{ARCHITECTURE}->{NAME};
214             if ($info_arch and ($info_arch eq $arch)) {
215             $url = $item;
216             print qq{\nUsing $ppd_local from $url\n};
217             last;
218             }
219             }
220             }
221             }
222             }
223             return unless (-f $ppd_local);
224             return unless ($info and (ref($info) eq 'HASH'));
225              
226             my $codebase = $info->{CODEBASE}->{HREF};
227             (my $ar_local = $codebase) =~ s{.*?/([^/]+)$}{$1};
228             if ($codebase =~ /^$protocol/) {
229             my $ar_remote = $codebase;
230             return unless mirror($ar_remote, $ar_local);
231             }
232             elsif ($url !~ /^$protocol/) {
233             my $ar_remote = catfile($url, $codebase);
234             if (-f $ar_remote) {
235             copy($ar_remote, $ar_local) or do {
236             warn qq{Cannot copy "$ar_remote" to "$ar_local": $!};
237             return;
238             };
239             }
240             }
241             else {
242             my $ar_remote = $url . $codebase;
243             return unless mirror($ar_remote, $ar_local);
244             }
245             unless (-f $ar_local) {
246             warn qq{Cannot get "$ar_local"};
247             return;
248             }
249             (my $name = $ppd_local) =~ s{\.ppd$}{};
250             $self->{files}->{$name} = {cwd => $cwd,
251             ppd => $ppd_local,
252             ar => $ar_local};
253              
254             my $deps = $info->{DEPENDENCY};
255             return 1 unless ($deps and (ref($deps) eq 'ARRAY'));
256             foreach my $item (@$deps) {
257             my $dist_name = $item->{NAME};
258             next if is_ap_core($dist_name);
259             push @{$self->{files}->{$name}->{prereqs}}, {dist_name => $dist_name};
260             }
261             return $name;
262             }
263              
264             sub fetch_prereqs {
265             my ($self, $ppm) = @_;
266             die qq{Please supply a PPM::Make object}
267             unless ($ppm and (ref($ppm) eq 'PPM::Make'));
268            
269             my @full_prereqs = keys %{$ppm->{args}->{PREREQ_PM}};
270             my @prereqs = ();
271             foreach my $mod(@full_prereqs) {
272             push @prereqs, $mod unless ($mod eq 'perl' or is_core($mod));
273             }
274             my $search = $self->{search};
275             {
276             if (scalar @prereqs > 0) {
277             my $matches = $search->search(\@prereqs, mode => 'mod');
278             if ($matches and (ref($matches) eq 'HASH')) {
279             foreach my $mod(keys %$matches) {
280             next if is_ap_core($matches->{$mod}->{dist_name});
281             print qq{\nFetching prerequisite "$mod"\n};
282             my $download = $cpan_mirrors[0] . '/authors/id/' .
283             $matches->{$mod}->{download};
284             my $ppm = PPM::Make->new(%{$self->{opts}},
285             no_cfg => 1, dist => $download);
286             $ppm->make_ppm();
287             (my $name = $ppm->{ppd}) =~ s{\.ppd$}{};
288             $self->{files}->{$name} = {cwd => $ppm->{cwd},
289             ppd => $ppm->{ppd},
290             ar => $ppm->{codebase}};
291             $self->fetch_prereqs($ppm);
292             }
293             }
294             }
295             }
296             }
297              
298             sub make_zip {
299             my $self = shift;
300             my $cwd = $self->{build_dir};
301             chdir($cwd) or die qq{Cannot chdir to $cwd: $!};
302             my $files = $self->{files};
303             my $bundle_name = $self->{name};
304             foreach my $name(keys %$files) {
305             my $item = $self->{files}->{$name};
306             my $item_cwd = $item->{cwd};
307             next if ($item_cwd eq $cwd);
308             my $ppd = $item->{ppd};
309             my $ar = $item->{ar};
310             copy(catfile($item_cwd, $ppd), $ppd)
311             or die qq{Cannot copy $ppd from $item_cwd: $!};
312             copy(catfile($item_cwd, $ar), $ar)
313             or die qq{Cannot copy $ar from $item_cwd: $!};
314             }
315             my $ppd_master = $self->{files}->{$bundle_name}->{ppd};
316             my $zipdist = $self->{zipdist} ||
317             ($bundle_name =~ /^(Bundle|Task)/ ?
318             $bundle_name : ('Bundle-' . $bundle_name)) . '.zip';
319             if (-f $zipdist) {
320             unlink $zipdist or warn "Could not unlink $zipdist: $!";
321             }
322             my $readme = 'README';
323             open(my $fh, '>', $readme) or die "Cannot open $readme: $!";
324             print $fh <<"END";
325             To install this ppm package, run the following command
326             in the current directory:
327              
328             ppm rep add temp_repository file://C:/Path/to/current/directory
329             ppm install $ppd_master
330             ppm rep del temp_repository_id_number
331              
332             END
333             close $fh;
334              
335             my %contents = ($readme => 'README');
336             foreach my $name(keys %$files) {
337             my $item = $self->{files}->{$name};
338             my $item_cwd = $item->{cwd};
339             my $ppd = $item->{ppd};
340             my $ar = $item->{ar};
341             my $ppd_orig = $ppd . '.orig';
342             rename($ppd, $ppd_orig) or die "Cannot rename $ppd to $ppd_orig: $!";
343             open(my $rfh, '<', $ppd_orig) or die "Cannot open $ppd_orig: $!";
344             open(my $wfh, '>', $ppd) or die "Cannot open $ppd: $!";
345             while (my $line = <$rfh>) {
346             $line =~ s{HREF=\".*/([^/]+)\"}{HREF="$1"};
347             print $wfh $line;
348             }
349             close($rfh);
350             close($wfh);
351             $contents{$ar} = $ar;
352             $contents{$ppd} = $ppd;
353             }
354              
355             my $zip = $self->{has}->{zip};
356             print qq{\nCreating $zipdist ...\n};
357             if ($zip eq 'Archive::Zip') {
358             my $arc = Archive::Zip->new();
359             foreach (sort keys %contents) {
360             print "Adding $contents{$_}\n";
361             unless ($arc->addFile($_, $contents{$_})) {
362             die "Failed to add $_";
363             }
364             }
365             die "Writing to $zipdist failed"
366             unless $arc->writeToFileNamed($zipdist) == Archive::Zip::AZ_OK();
367             }
368             else {
369             my @args = ($zip, $zipdist, keys %contents);
370             print "@args\n";
371             system(@args) == 0 or die "@args failed: $?";
372             }
373             unless ($self->{opts}->{upload}) {
374             my $cwd = $self->{cwd};
375             copy($zipdist, $cwd) or warn qq{Cannot copy $zipdist to $cwd: $!};
376             print qq{\nCopying $zipdist to $cwd.\n};
377             }
378             $self->{zipdist} = $zipdist;
379             return 1;
380             }
381              
382             sub upload_zip {
383             my $self = shift;
384             my $upload = $self->{opts}->{upload};
385             my $bundle_loc = $upload->{bundle};
386             my $zipdist = $self->{zipdist};
387             my $cwd = $self->{build_dir};
388             chdir($cwd) or die qq{Cannot chdir to $cwd: $!};
389              
390             if (my $host = $upload->{host}) {
391             print qq{\nUploading $zipdist to $host ...\n};
392             my ($user, $passwd) = ($upload->{user}, $upload->{passwd});
393             die "Must specify a username and password to log into $host"
394             unless ($user and $passwd);
395             my $ftp = Net::FTP->new($host)
396             or die "Cannot connect to $host: $@";
397             $ftp->login($user, $passwd)
398             or die "Login for user $user failed: ", $ftp->message;
399             $ftp->cwd($bundle_loc) or die
400             "cwd to $bundle_loc failed: ", $ftp->message;
401             $ftp->binary;
402             $ftp->put($zipdist)
403             or die "Cannot upload $zipdist: ", $ftp->message;
404             $ftp->quit;
405             }
406             else {
407             print qq{\nCopying $zipdist to $bundle_loc\n};
408             copy($zipdist, "$bundle_loc/$zipdist")
409             or die "Cannot copy $zipdist to $bundle_loc: $!";
410             }
411             print qq{Done!\n};
412             return 1;
413             }
414              
415             1;
416              
417             __END__