Move CPANPLUS from lib/ to ext/
[p5sagit/p5-mst-13.2.git] / ext / CPANPLUS / bin / cpan2dist
CommitLineData
6aaee015 1#!/usr/bin/perl -w
2use strict;
3use CPANPLUS::Backend;
4use CPANPLUS::Dist;
5use CPANPLUS::Internals::Constants;
6use Data::Dumper;
7use Getopt::Long;
8use File::Spec;
502c7995 9use File::Temp qw|tempfile|;
6aaee015 10use File::Basename;
11use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
12
622d31ac 13local $Data::Dumper::Indent = 1;
14
6aaee015 15use constant PREREQ_SKIP_CLASS => 'CPANPLUS::To::Dist::PREREQ_SKIP';
16use constant ALARM_CLASS => 'CPANPLUS::To::Dist::ALARM';
17
18### print when you can
19$|++;
20
21my $cb = CPANPLUS::Backend->new
22 or die loc("Could not create new CPANPLUS::Backend object");
23my $conf = $cb->configure_object;
24
25my %formats = map { $_ => $_ } CPANPLUS::Dist->dist_types;
26
27my $opts = {};
28GetOptions( $opts,
622d31ac 29 'format=s', 'archive',
30 'verbose!', 'force!',
31 'skiptest!', 'keepsource!',
32 'makefile!', 'buildprereq!',
33 'help', 'flushcache',
34 'ban=s@', 'banlist=s@',
35 'ignore=s@', 'ignorelist=s@',
36 'defaults', 'modulelist=s@',
37 'logfile=s', 'timeout=s',
38 'dist-opts=s%', 'set-config=s%',
39 'default-banlist!', 'set-program=s%',
5879cbe1 40 'default-ignorelist!', 'edit-metafile!',
41 'install!'
6aaee015 42 );
43
44die usage() if exists $opts->{'help'};
45
46### parse options
47my $tarball = $opts->{'archive'} || 0;
48my $keep = $opts->{'keepsource'} ? 1 : 0;
49my $prereqbuild = exists $opts->{'buildprereq'}
50 ? $opts->{'buildprereq'}
51 : 0;
52my $timeout = exists $opts->{'timeout'}
53 ? $opts->{'timeout'}
54 : 300;
55
56### use default answers?
57$ENV{'PERL_MM_USE_DEFAULT'} = $opts->{'defaults'} ? 1 : 0;
58
59my $format;
60### if provided, we go with the command line option, fall back to conf setting
61{ $format = $opts->{'format'} || $conf->get_conf('dist_type');
62 $conf->set_conf( dist_type => $format );
63
64 ### is this a valid format??
65 die loc("Invalid format: " . ($format || "[NONE]") ) . usage()
66 unless $formats{$format};
67
622d31ac 68 ### any options to fix config entries
69 { my $set_conf = $opts->{'set-config'} || {};
70 while( my($key,$val) = each %$set_conf ) {
71 $conf->set_conf( $key => $val );
72 }
73 }
74
75 ### any options to fix program entries
76 { my $set_prog = $opts->{'set-program'} || {};
77 while( my($key,$val) = each %$set_prog ) {
78 $conf->set_program( $key => $val );
79 }
80 }
81
82 ### any other options passed
83 { my %map = ( verbose => 'verbose',
84 force => 'force',
85 skiptest => 'skiptest',
86 makefile => 'prefer_makefile'
87 );
88
89 ### set config options from arguments
90 while (my($key,$val) = each %map) {
91 my $bool = exists $opts->{$key}
92 ? $opts->{$key}
93 : $conf->get_conf($val);
94 $conf->set_conf( $val => $bool );
95 }
96 }
6aaee015 97}
98
99my @modules = @ARGV;
100if( exists $opts->{'modulelist'} ) {
101 push @modules, map { parse_file( $_ ) } @{ $opts->{'modulelist'} };
102}
103
104die usage() unless @modules;
105
502c7995 106### set up munge callback if requested
107{ if( $opts->{'edit-metafile'} ) {
108 my $editor = $conf->get_program('editor');
109
110 if( $editor ) {
111
112 ### register install callback ###
113 $cb->_register_callback(
114 name => 'munge_dist_metafile',
115 code => sub {
116 my $self = shift;
117 my $text = shift or return;
118
119 my($fh,$file) = tempfile( UNLINK => 1 );
120
121 unless( print $fh $text ) {
122 warn "Could not print metafile information: $!";
123 return;
124 }
125
126 close $fh;
127
128 system( $editor => $file );
129
130 my $cont = $cb->_get_file_contents( file => $file );
131
132 return $cont;
133 },
134 );
135
136 } else {
137 warn "No editor configured. Can not edit metafiles!\n";
138 }
139 }
140}
6aaee015 141
142my $fh;
143LOGFILE: {
144 if( my $file = $opts->{logfile} ) {
145 open $fh, ">$file" or (
146 warn loc("Could not open '%1' for writing: %2", $file,$!),
147 last LOGFILE
148 );
149
150 warn "Logging to '$file'\n";
151
152 *STDERR = $fh;
153 *STDOUT = $fh;
154 }
155}
156
157### reload indices if so desired
158$cb->reload_indices() if $opts->{'flushcache'};
159
160{ my @ban = exists $opts->{'ban'}
161 ? map { qr/$_/ } @{ $opts->{'ban'} }
162 : ();
163
164
165 if( exists $opts->{'banlist'} ) {
166 push @ban, map { parse_file( $_, 1 ) } @{ $opts->{'banlist'} };
167 }
168
169 push @ban, map { s/\s+//; $_ }
170 map { [split /\s*#\s*/]->[0] }
171 grep { /#/ }
172 map { split /\n/ } _default_ban_list()
173 if $opts->{'default-banlist'};
174
175 ### use our prereq install callback
176 $conf->set_conf( prereqs => PREREQ_ASK );
177
178 ### register install callback ###
179 $cb->_register_callback(
180 name => 'install_prerequisite',
181 code => \&__ask_about_install,
182 );
183
184
185 ### check for ban patterns when handling prereqs
186 sub __ask_about_install {
187
188 my $mod = shift or return;
189 my $prereq = shift or return;
190
191
192 ### die with an error object, so we can verify that
193 ### the die came from this location, and that it's an
194 ### 'acceptable' death
195 my $pat = ban_me( $prereq );
622d31ac 196 die bless sub { loc("Module '%1' requires '%2' to be installed " .
197 "but found in your ban list (%3) -- skipping",
198 $mod->module, $prereq->module, $pat )
199 }, PREREQ_SKIP_CLASS if $pat;
6aaee015 200 return 1;
201 }
202
203 ### should we skip this module?
204 sub ban_me {
205 my $mod = shift;
206
207 for my $pat ( @ban ) {
622d31ac 208 return $pat if $mod->module =~ /$pat/i;
6aaee015 209 }
210 return;
211 }
212}
213
214### patterns to strip from prereq lists
215{ my @ignore = exists $opts->{'ignore'}
216 ? map { qr/$_/ } @{ $opts->{'ignore'} }
217 : ();
218
219 if( exists $opts->{'ignorelist'} ) {
220 push @ignore, map { parse_file( $_, 1 ) } @{ $opts->{'ignorelist'} };
221 }
222
223 push @ignore, map { s/\s+//; $_ }
224 map { [split /\s*#\s*/]->[0] }
225 grep { /#/ }
226 map { split /\n/ } _default_ignore_list()
227 if $opts->{'default-ignorelist'};
228
229
230 ### register install callback ###
231 $cb->_register_callback(
232 name => 'filter_prereqs',
233 code => \&__filter_prereqs,
234 );
235
236 sub __filter_prereqs {
237 my $cb = shift;
238 my $href = shift;
239
240 for my $name ( keys %$href ) {
241 my $obj = $cb->parse_module( module => $name ) or (
242 warn "Cannot make a module object out of ".
243 "'$name' -- skipping\n",
244 next );
245
246 if( my $pat = ignore_me( $obj ) ) {
247 warn loc("'%1' found in your ignore list (%2) ".
248 "-- filtering it out\n", $name, $pat);
249
250 delete $href->{ $name };
251 }
252 }
253
254 return $href;
255 }
256
257 ### should we skip this module?
258 sub ignore_me {
259 my $mod = shift;
260
261 for my $pat ( @ignore ) {
622d31ac 262 return $pat if $mod->module =~ /$pat/i;
263 return $pat if $mod->package_name =~ /$pat/i;
6aaee015 264 }
265 return;
266 }
267}
268
269
270my %done;
271for my $name (@modules) {
272
273 my $obj;
274
275 ### is it a tarball? then we get it locally and transform it
4443dd53 276 ### and its dependencies into .debs
6aaee015 277 if( $tarball ) {
278 ### make sure we use an absolute path, so chdirs() dont
279 ### mess things up
280 $name = File::Spec->rel2abs( $name );
281
282 ### ENOTARBALL?
283 unless( -e $name ) {
284 warn loc("Archive '$name' does not exist");
285 next;
286 }
287
288 $obj = CPANPLUS::Module::Fake->new(
289 module => basename($name),
290 path => dirname($name),
291 package => basename($name),
292 );
293
294 ### if it's a traditional CPAN package, we can tidy
295 ### up the module name some
296 $obj->module( $obj->package_name ) if $obj->package_name;
297
298 ### get the version from the package name
299 $obj->version( $obj->package_version || 0 );
300
301 ### set the location of the tarball
302 $obj->status->fetch($name);
303
304 ### plain old cpan module?
305 } else {
306
307 ### find the corresponding module object ###
308 $obj = $cb->parse_module( module => $name ) or (
309 warn "Cannot make a module object out of ".
310 "'$name' -- skipping\n",
311 next );
312 }
313
314 ### you banned it?
315 if( my $pat = ban_me( $obj ) ) {
316 warn loc("'%1' found in your ban list (%2) -- skipping\n",
317 $obj->module, $pat );
318 next;
319 }
320
321 ### or just ignored it?
322 if( my $pat = ignore_me( $obj ) ) {
323 warn loc("'%1' found in your ignore list (%2) -- skipping\n",
324 $obj->module, $pat );
325 next;
326 }
327
328
5879cbe1 329 my $target = $opts->{'install'} ? 'install' : 'create';
330 my $dist = eval {
6aaee015 331 local $SIG{ALRM} = sub { die bless {}, ALARM_CLASS }
332 if $timeout;
333
334 alarm $timeout || 0;
335
336 my $dist_opts = $opts->{'dist-opts'} || {};
337
338 my $rv = $obj->install(
5879cbe1 339 prereq_target => $target,
340 target => $target,
6aaee015 341 keep_source => $keep,
342 prereq_build => $prereqbuild,
343
344 ### any passed arbitrary options
345 %$dist_opts,
346 );
347
348 alarm 0;
349
350 $rv;
351 };
352
353 ### set here again, in case the install dies
354 alarm 0;
355
356 ### install failed due to a 'die' in our prereq skipper?
357 if( $@ and ref $@ and $@->isa( PREREQ_SKIP_CLASS ) ) {
358 warn loc("Dist creation of '%1' skipped: '%2'",
622d31ac 359 $obj->module, $@->() );
6aaee015 360 next;
361
362 } elsif ( $@ and ref $@ and $@->isa( ALARM_CLASS ) ) {
363 warn loc("\nDist creation of '%1' skipped, build time exceeded: ".
364 "%2 seconds\n", $obj->module, $timeout );
365 next;
366
367 ### died for some other reason? just report and skip
368 } elsif ( $@ ) {
369 warn loc("Dist creation of '%1' failed: '%2'",
370 $obj->module, $@ );
371 next;
372 }
373
374 ### we didn't get a dist object back?
375 unless ($dist and $obj->status->dist) {
376 warn loc("Unable to create '%1' dist of '%2'", $format, $obj->module);
377 next
378 }
379
380 print "Created '$format' distribution for ", $obj->module,
381 " to:\n\t", $obj->status->dist->status->dist, "\n";
382}
383
384
385sub parse_file {
386 my $file = shift or return;
387 my $qr = shift() ? 1 : 0;
388
389 my $fh = OPEN_FILE->( $file ) or return;
390
391 my @rv;
392 while( <$fh> ) {
393 chomp;
394 next if /^#/; # skip comments
395 next unless /\S/; # skip empty lines
396 s/^(\S+).*/$1/; # skip extra info
397 push @rv, $qr ? qr/$_/ : $_; # add pattern to the list
398 }
399
400 return @rv;
401}
402
403=head1 NAME
404
405cpan2dist - The CPANPLUS distribution creator
406
407=head1 DESCRIPTION
408
409This script will create distributions of C<CPAN> modules of the format
410you specify, including its prerequisites. These packages can then be
411installed using the corresponding package manager for the format.
412
413Note, you can also do this interactively from the default shell,
414C<CPANPLUS::Shell::Default>. See the C<CPANPLUS::Dist> documentation,
415as well as the documentation of your format of choice for any format
416specific documentation.
417
418=head1 USAGE
419
420=cut
421
422sub usage {
423 my $me = basename($0);
424 my $formats = join "\n", map { "\t\t$_" } sort keys %formats;
425
426 my $usage = << '=cut';
427=pod
428
429 Usage: cpan2dist [--format FMT] [OPTS] Mod::Name [Mod::Name, ...]
430 cpan2dist [--format FMT] [OPTS] --modulelist /tmp/mods.list
431 cpan2dist [--format FMT] [OPTS] --archive /tmp/dist [/tmp/dist2]
432
433 Will create a distribution of type FMT of the modules
434 specified on the command line, and all their prerequisites.
435
436 Can also create a distribution of type FMT from a local
4443dd53 437 archive and all of its prerequisites.
6aaee015 438
439=cut
440
441 $usage .= qq[
442 Possible formats are:
443$formats
444
445 You can install more formats from CPAN!
446 \n];
447
448 $usage .= << '=cut';
449=pod
450
451Options:
452
453 ### take no argument:
502c7995 454 --help Show this help message
5879cbe1 455 --install Install this package (and any prerequisites you built)
456 after building it.
502c7995 457 --skiptest Skip tests. Can be negated using --noskiptest
458 --force Force operation. Can be negated using --noforce
459 --verbose Be verbose. Can be negated using --noverbose
460 --keepsource Keep sources after building distribution. Can be
461 negated by --nokeepsource. May not be supported
462 by all formats
463 --makefile Prefer Makefile.PL over Build.PL. Can be negated
464 using --nomakefile. Defaults to your config setting
465 --buildprereq Build packages of any prerequisites, even if they are
466 already uptodate on the local system. Can be negated
467 using --nobuildprereq. Defaults to false.
468 --archive Indicate that all modules listed are actually archives
469 --flushcache Update CPANPLUS' cache before commencing any operation
470 --defaults Instruct ExtUtils::MakeMaker and Module::Build to use
471 default answers during 'perl Makefile.PL' or 'perl
472 Build.PL' calls where possible
473 --edit-metafile Edit the distributions metafile(s) before the distribution
474 is built. Requires a configured editor.
6aaee015 475
476 ### take argument:
477 --format Installer format to use (defaults to config setting)
622d31ac 478 --ban Patterns of module names to skip during installation,
479 case-insensitive (affects prerequisites too)
480 May be given multiple times
6aaee015 481 --banlist File containing patterns that could be given to --ban
482 Are appended to the ban list built up by --ban
483 May be given multiple times.
484 --ignore Patterns of modules to exclude from prereq list. Useful
485 for when a prereq listed by a CPAN module is resolved
486 in another way than from its corresponding CPAN package
487 (Match is done on both module name, and package name of
622d31ac 488 the package the module is in, case-insensitive)
6aaee015 489 --ignorelist File containing patterns that may be given to --ignore.
622d31ac 490 Are appended to the ban list built up by --ignore.
6aaee015 491 May be given multiple times.
492 --modulelist File containing a list of modules that should be built.
493 Are appended to the list of command line modules.
494 May be given multiple times.
495 --logfile File to log all output to. By default, all output goes
496 to the console.
497 --timeout The allowed time for buliding a distribution before
498 aborting. This is useful to terminate any build that
499 hang or happen to be interactive despite being told not
500 to be. Defaults to 300 seconds. To turn off, you can
501 set it to 0.
622d31ac 502 --set-config Change any options as specified in your config for this
503 invocation only. See CPANPLUS::Config for a list of
504 supported options.
505 --set-program Change any programs as specified in your config for this
506 invocation only. See CPANPLUS::Config for a list of
507 supported programs.
6aaee015 508 --dist-opts Arbitrary options passed along to the chosen installer
622d31ac 509 format's prepare()/create() routine. Please see the
510 documentation of the installer of your choice for
511 options it accepts.
6aaee015 512
513 ### builtin lists
514 --default-banlist Use our builtin banlist. Works just like --ban
515 and --banlist, but with pre-set lists. See the
516 "Builtin Lists" section for details.
517 --default-ignorelist Use our builtin ignorelist. Works just like
518 --ignore and --ignorelist but with pre-set lists.
519 See the "Builtin Lists" section for details.
520
521Examples:
522
4443dd53 523 ### build a debian package of DBI and its prerequisites,
6aaee015 524 ### don't bother running tests
525 cpan2dist --format CPANPLUS::Dist::Deb --buildprereq --skiptest DBI
526
4443dd53 527 ### build a debian package of DBI and its prerequisites and install them
5879cbe1 528 cpan2dist --format CPANPLUS::Dist::Deb --buildprereq --install DBI
529
622d31ac 530 ### Build a package, whose format is determined by your config, of
6aaee015 531 ### the local tarball, reloading cpanplus' indices first and using
532 ### the tarballs Makefile.PL if it has one.
533 cpan2dist --makefile --flushcache --archive /path/to/Cwd-1.0.tgz
534
535 ### build a package from Net::FTP, but dont build any packages or
536 ### dependencies whose name match 'Foo', 'Bar' or any of the
537 ### patterns mentioned in /tmp/ban
538 cpan2dist --ban Foo --ban Bar --banlist /tmp/ban Net::FTP
539
4443dd53 540 ### build a package from Net::FTP, but ignore its listed dependency
6aaee015 541 ### on IO::Socket, as it's shipped per default with the OS we're on
542 cpan2dist --ignore IO::Socket Net::FTP
543
544 ### building all modules listed, plus their prerequisites
545 cpan2dist --ignorelist /tmp/modules.ignore --banlist /tmp/modules.ban
546 --modulelist /tmp/modules.list --buildprereq --flushcache
547 --makefile --defaults
548
549 ### pass arbitrary options to the format's prepare()/create() routine
550 cpan2dist --dist-opts deb_version=3 --dist-opts prefix=corp
551
552=cut
553
554 $usage .= qq[
555Builtin Lists:
556
557 Ignore list:] . _default_ignore_list() . qq[
558 Ban list:] . _default_ban_list();
559
560 ### strip the pod directives
561 $usage =~ s/=pod\n//g;
562
563 return $usage;
564}
565
566=pod
567
568=head1 Built-In Filter Lists
569
570Some modules you'd rather not package. Some because they
571are part of core-perl and you dont want a new package.
572Some because they won't build on your system. Some because
573your package manager of choice already packages them for you.
574
575There may be a myriad of reasons. You can use the C<--ignore>
576and C<--ban> options for this, but we provide some built-in
577lists that catch common cases. You can use these built-in lists
578if you like, or supply your own if need be.
579
580=head2 Built-In Ignore List
581
582=pod
583
584You can use this list of regexes to ignore modules matching
585to be listed as prerequisites of a package. Particulaly useful
586if they are bundled with core-perl anyway and they have known
587issues building.
588
589Toggle it by supplying the C<--default-ignorelist> option.
590
591=cut
592
593sub _default_ignore_list {
594
595 my $list = << '=cut';
596=pod
597
598 ^IO$ # Provided with core anyway
599 ^Cwd$ # Provided with core anyway
600 ^File::Spec # Provided with core anyway
601 ^Config$ # Perl's own config, not shipped separately
602 ^ExtUtils::MakeMaker$ # Shipped with perl, recent versions
603 # have bug 14721 (see rt.cpan.org)
604 ^ExtUtils::Install$ # Part of of EU::MM, same reason
605
606=cut
607
608 return $list;
609}
610
611=head2 Built-In Ban list
612
613You can use this list of regexes to disable building of these
614modules altogether.
615
616Toggle it by supplying the C<--default-banlist> option.
617
618=cut
619
620sub _default_ban_list {
621
622 my $list = << '=cut';
623=pod
624
625 ^GD$ # Needs c libaries
626 ^Berk.*DB # DB packages require specific options & linking
627 ^DBD:: # DBD drives require database files/headers
628 ^XML:: # XML modules usually require expat libraries
629 Apache # These usually require apache libraries
630 SSL # These usually require SSL certificates & libs
631 Image::Magick # Needs ImageMagick C libraries
632 Mail::ClamAV # Needs ClamAV C Libraries
633 ^Verilog # Needs Verilog C Libraries
634 ^Authen::PAM$ # Needs PAM C libraries & Headers
635
636=cut
637
638 return $list;
639}
640
641__END__
642
643=head1 SEE ALSO
644
645L<CPANPLUS::Dist>, L<CPANPLUS::Module>, L<CPANPLUS::Shell::Default>,
646C<cpanp>
647
648=head1 BUG REPORTS
649
650Please report bugs or other issues to E<lt>bug-cpanplus@rt.cpan.org<gt>.
651
652=head1 AUTHOR
653
654This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
655
656=head1 COPYRIGHT
657
658The CPAN++ interface (of which this module is a part of) is copyright (c)
6592001 - 2007, Jos Boumans E<lt>kane@cpan.orgE<gt>. All rights reserved.
660
661This library is free software; you may redistribute and/or modify it
662under the same terms as Perl itself.
663
664=cut
665
666# Local variables:
667# c-indentation-style: bsd
668# c-basic-offset: 4
669# indent-tabs-mode: nil
670# End:
671# vim: expandtab shiftwidth=4: