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