1 package CPANPLUS::Dist::Build;
5 use vars qw[@ISA $STATUS $VERSION];
6 @ISA = qw[CPANPLUS::Dist];
9 use CPANPLUS::Internals::Constants;
11 ### these constants were exported by CPANPLUS::Internals::Constants
12 ### in previous versions.. they do the same though. If we want to have
13 ### a normal 'use' here, up the dependency to CPANPLUS 0.056 or higher
15 require CPANPLUS::Dist::Build::Constants;
16 CPANPLUS::Dist::Build::Constants->import()
17 if not __PACKAGE__->can('BUILD') && __PACKAGE__->can('BUILD_DIR');
28 use Params::Check qw[check];
29 use Module::Load::Conditional qw[can_load check_install];
30 use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
32 local $Params::Check::VERBOSE = 1;
40 CPANPLUS::Dist::Build - CPANPLUS plugin to install packages that use Build.PL
44 my $build = CPANPLUS::Dist->new(
45 format => 'CPANPLUS::Dist::Build',
49 $build->prepare; # runs Build.PL
50 $build->create; # runs build && build test
51 $build->install; # runs build install
56 C<CPANPLUS::Dist::Build> is a distribution class for C<Module::Build>
58 Using this package, you can create, install and uninstall perl
59 modules. It inherits from C<CPANPLUS::Dist>.
61 Normal users won't have to worry about the interface to this module,
62 as it functions transparently as a plug-in to C<CPANPLUS> and will
63 just C<Do The Right Thing> when it's loaded.
71 Returns the C<CPANPLUS::Module> object that parented this object.
75 Returns the C<Object::Accessor> object that keeps the status for
80 =head1 STATUS ACCESSORS
82 All accessors can be accessed as follows:
83 $build->status->ACCESSOR
89 Location of the Build file.
90 Set to 0 explicitly if something went wrong.
94 BOOL indicating if the C<Build> command was successful.
98 BOOL indicating if the C<Build test> command was successful.
102 BOOL indicating if the C<prepare> call exited succesfully
103 This gets set after C<perl Build.PL>
107 Full path to the directory in which the C<prepare> call took place,
108 set after a call to C<prepare>.
112 BOOL indicating if the C<create> call exited succesfully. This gets
113 set after C<Build> and C<Build test>.
115 =item C<installed ()>
117 BOOL indicating if the module was installed. This gets set after
118 C<Build install> exits successfully.
122 BOOL indicating if the module was uninstalled properly.
124 =item C<_create_args ()>
126 Storage of the arguments passed to C<create> for this object. Used
127 for recursive calls when satisfying prerequisites.
129 =item C<_install_args ()>
131 Storage of the arguments passed to C<install> for this object. Used
132 for recursive calls when satisfying prerequisites.
140 =head2 $bool = CPANPLUS::Dist::Build->format_available();
142 Returns a boolean indicating whether or not you can use this package
143 to create and install modules in your environment.
147 ### check if the format is available ###
148 sub format_available {
149 my $mod = "Module::Build";
150 unless( can_load( modules => { $mod => '0.2611' } ) ) {
151 error( loc( "You do not have '%1' -- '%2' not available",
152 $mod, __PACKAGE__ ) );
160 =head2 $bool = $dist->init();
162 Sets up the C<CPANPLUS::Dist::Build> object for use.
163 Effectively creates all the needed status accessors.
165 Called automatically whenever you create a new C<CPANPLUS::Dist> object.
171 my $status = $dist->status;
173 $status->mk_accessors(qw[build_pl build test created installed uninstalled
174 _create_args _install_args _prepare_args
175 _mb_object _buildflags
178 ### just in case 'format_available' didn't get called
179 require Module::Build;
186 =head2 $bool = $dist->prepare([perl => '/path/to/perl', buildflags => 'EXTRA=FLAGS', force => BOOL, verbose => BOOL])
188 C<prepare> prepares a distribution, running C<Build.PL>
189 and establishing any prerequisites this
192 The variable C<PERL5_CPANPLUS_IS_EXECUTING> will be set to the full path
193 of the C<Build.PL> that is being executed. This enables any code inside
194 the C<Build.PL> to know that it is being installed via CPANPLUS.
196 After a succcesfull C<prepare> you may call C<create> to create the
197 distribution, followed by C<install> to actually install it.
199 Returns true on success and false on failure.
204 ### just in case you already did a create call for this module object
205 ### just via a different dist object
207 my $self = $dist->parent;
209 ### we're also the cpan_dist, since we don't need to have anything
210 ### prepared from another installer
211 $dist = $self->status->dist_cpan if $self->status->dist_cpan;
212 $self->status->dist_cpan( $dist ) unless $self->status->dist_cpan;
214 my $cb = $self->parent;
215 my $conf = $cb->configure_object;
219 unless( $dir = $self->status->extract ) {
220 error( loc( "No dir found to operate on!" ) );
225 my( $force, $verbose, $buildflags, $perl, $prereq_target, $prereq_format,
227 { local $Params::Check::ALLOW_UNKNOWN = 1;
229 force => { default => $conf->get_conf('force'),
231 verbose => { default => $conf->get_conf('verbose'),
232 store => \$verbose },
233 perl => { default => $^X, store => \$perl },
234 buildflags => { default => $conf->get_conf('buildflags'),
235 store => \$buildflags },
236 prereq_target => { default => '', store => \$prereq_target },
237 prereq_format => { default => '',
238 store => \$prereq_format },
239 prereq_build => { default => 0, store => \$prereq_build },
242 $args = check( $tmpl, \%hash ) or return;
245 return 1 if $dist->status->prepared && !$force;
247 $dist->status->_prepare_args( $args );
249 ### chdir to work directory ###
251 unless( $cb->_chdir( dir => $dir ) ) {
252 error( loc( "Could not chdir to build directory '%1'", $dir ) );
256 ### by now we've loaded module::build, and we're using the API, so
257 ### it's safe to remove CPANPLUS::inc from our inc path, especially
258 ### because it can trip up tests run under taint (just like EU::MM).
259 ### turn off our PERL5OPT so no modules from CPANPLUS::inc get
260 ### included in make test -- it should build without.
261 ### also, modules that run in taint mode break if we leave
262 ### our code ref in perl5opt
263 ### XXX we've removed the ENV settings from cp::inc, so only need
264 ### to reset the @INC
265 #local $ENV{PERL5OPT} = CPANPLUS::inc->original_perl5opt;
266 #local $ENV{PERL5LIB} = CPANPLUS::inc->original_perl5lib;
267 local @INC = CPANPLUS::inc->original_inc;
269 ### this will generate warnings under anything lower than M::B 0.2606
270 my %buildflags = $dist->_buildflags_as_hash( $buildflags );
271 $dist->status->_buildflags( $buildflags );
276 ### we resolve 'configure requires' here, so we can run the 'perl
277 ### Makefile.PL' command
278 ### XXX for tests: mock f_c_r to something that *can* resolve and
279 ### something that *doesnt* resolve. Check the error log for ok
280 ### on this step or failure
281 ### XXX make a seperate tarball to test for this scenario: simply
282 ### containing a makefile.pl/build.pl for test purposes?
283 my $safe_ver = version->new('0.85_01');
284 if ( version->new($CPANPLUS::Internals::VERSION) >= $safe_ver )
285 { my $configure_requires = $dist->find_configure_requires;
286 my $ok = $dist->_resolve_prereqs(
287 format => $prereq_format,
289 prereqs => $configure_requires,
290 target => $prereq_target,
292 prereq_build => $prereq_build,
297 #### use $dist->flush to reset the cache ###
298 error( loc( "Unable to satisfy '%1' for '%2' " .
299 "-- aborting install",
300 'configure_requires', $self->module ) );
301 $dist->status->prepared(0);
305 ### end of prereq resolving ###
308 # Wrap the exception that may be thrown here (should likely be
309 # done at a much higher level).
312 my $env = 'ENV_CPANPLUS_IS_EXECUTING';
313 local $ENV{$env} = BUILD_PL->( $dir );
315 unless ( scalar run( command => [$perl, BUILD_PL->($dir), $buildflags],
316 buffer => \$prep_output,
317 verbose => $verbose )
319 error( loc( "Build.PL failed: %1", $prep_output ) );
323 msg( $prep_output, 0 );
325 my $prereqs = $self->status->prereqs;
327 $prereqs ||= $dist->_find_prereqs( verbose => $verbose,
330 buildflags => $buildflags );
334 ### send out test report? ###
335 if( $fail and $conf->get_conf('cpantest') ) {
339 buffer => CPANPLUS::Error->stack_as_string,
342 ) or error(loc("Failed to send test report for '%1'",
346 unless( $cb->_chdir( dir => $orig ) ) {
347 error( loc( "Could not chdir back to start dir '%1'", $orig ) );
350 ### save where we wrote this stuff -- same as extract dir in normal
351 ### installer circumstances
352 $dist->status->distdir( $self->status->extract );
354 return $dist->status->prepared( $fail ? 0 : 1 );
359 my $self = $dist->parent;
360 my $cb = $self->parent;
361 my $conf = $cb->configure_object;
364 my ($verbose, $dir, $buildflags, $perl);
366 verbose => { default => $conf->get_conf('verbose'), store => \$verbose },
367 dir => { default => $self->status->extract, store => \$dir },
368 perl => { default => $^X, store => \$perl },
369 buildflags => { default => $conf->get_conf('buildflags'),
370 store => \$buildflags },
373 my $args = check( $tmpl, \%hash ) or return;
377 my $safe_ver = version->new('0.31_03');
381 if ( version->new( $Module::Build::VERSION ) >= $safe_ver and ! ON_WIN32 ) {
382 # Use the new Build action 'prereq_data'
384 unless ( scalar run( command => [$perl, BUILD->($dir), 'prereq_data', $buildflags],
388 error( loc( "Build 'prereq_data' failed: %1 %2", $!, $content ) );
394 my $file = File::Spec->catfile( $dir, '_build', 'prereqs' );
395 return unless -f $file;
397 my $fh = FileHandle->new();
399 unless( $fh->open( $file ) ) {
400 error( loc( "Cannot open '%1': %2", $file, $! ) );
404 $content = do { local $/; <$fh> };
407 return unless $content;
408 my $bphash = eval $content;
409 return unless $bphash and ref $bphash eq 'HASH';
410 foreach my $type ('requires', 'build_requires') {
411 next unless $bphash->{$type} and ref $bphash->{$type} eq 'HASH';
412 $prereqs->{$_} = $bphash->{$type}->{$_} for keys %{ $bphash->{$type} };
416 delete $prereqs->{'perl'};
418 ### allows for a user defined callback to filter the prerequisite
419 ### list as they see fit, to remove (or add) any prereqs they see
420 ### fit. The default installed callback will return the hashref in
421 ### an unmodified form
422 ### this callback got added after cpanplus 0.0562, so use a 'can'
423 ### to find out if it's supported. For older versions, we'll just
424 ### return the hashref as is ourselves.
425 my $href = $cb->_callbacks->can('filter_prereqs')
426 ? $cb->_callbacks->filter_prereqs->( $cb, $prereqs )
429 $self->status->prereqs( $href );
431 ### make sure it's not the same ref
437 =head2 $dist->create([perl => '/path/to/perl', buildflags => 'EXTRA=FLAGS', prereq_target => TARGET, force => BOOL, verbose => BOOL, skiptest => BOOL])
439 C<create> preps a distribution for installation. This means it will
440 run C<Build> and C<Build test>.
441 This will also satisfy any prerequisites the module may have.
443 If you set C<skiptest> to true, it will skip the C<Build test> stage.
444 If you set C<force> to true, it will go over all the stages of the
445 C<Build> process again, ignoring any previously cached results. It
446 will also ignore a bad return value from C<Build test> and still allow
447 the operation to return true.
449 Returns true on success and false on failure.
451 You may then call C<< $dist->install >> on the object to actually
457 ### just in case you already did a create call for this module object
458 ### just via a different dist object
460 my $self = $dist->parent;
462 ### we're also the cpan_dist, since we don't need to have anything
463 ### prepared from another installer
464 $dist = $self->status->dist_cpan if $self->status->dist_cpan;
465 $self->status->dist_cpan( $dist ) unless $self->status->dist_cpan;
467 my $cb = $self->parent;
468 my $conf = $cb->configure_object;
472 unless( $dir = $self->status->extract ) {
473 error( loc( "No dir found to operate on!" ) );
478 my( $force, $verbose, $buildflags, $skiptest, $prereq_target,
479 $perl, $prereq_format, $prereq_build);
480 { local $Params::Check::ALLOW_UNKNOWN = 1;
482 force => { default => $conf->get_conf('force'),
484 verbose => { default => $conf->get_conf('verbose'),
485 store => \$verbose },
486 perl => { default => $^X, store => \$perl },
487 buildflags => { default => $conf->get_conf('buildflags'),
488 store => \$buildflags },
489 skiptest => { default => $conf->get_conf('skiptest'),
490 store => \$skiptest },
491 prereq_target => { default => '', store => \$prereq_target },
492 ### don't set the default format to 'build' -- that is wrong!
493 prereq_format => { #default => $self->status->installer_type,
495 store => \$prereq_format },
496 prereq_build => { default => 0, store => \$prereq_build },
499 $args = check( $tmpl, \%hash ) or return;
502 return 1 if $dist->status->created && !$force;
504 $dist->status->_create_args( $args );
506 ### is this dist prepared?
507 unless( $dist->status->prepared ) {
508 error( loc( "You have not successfully prepared a '%2' distribution ".
509 "yet -- cannot create yet", __PACKAGE__ ) );
513 ### chdir to work directory ###
515 unless( $cb->_chdir( dir => $dir ) ) {
516 error( loc( "Could not chdir to build directory '%1'", $dir ) );
520 ### by now we've loaded module::build, and we're using the API, so
521 ### it's safe to remove CPANPLUS::inc from our inc path, especially
522 ### because it can trip up tests run under taint (just like EU::MM).
523 ### turn off our PERL5OPT so no modules from CPANPLUS::inc get
524 ### included in make test -- it should build without.
525 ### also, modules that run in taint mode break if we leave
526 ### our code ref in perl5opt
527 ### XXX we've removed the ENV settings from cp::inc, so only need
528 ### to reset the @INC
529 #local $ENV{PERL5OPT} = CPANPLUS::inc->original_perl5opt;
530 #local $ENV{PERL5LIB} = CPANPLUS::inc->original_perl5lib;
531 local @INC = CPANPLUS::inc->original_inc;
533 ### but do it *before* the new_from_context, as M::B seems
534 ### to be actually running the file...
535 ### an unshift in the block seems to be ignored.. somehow...
536 #{ my $lib = $self->best_path_to_module_build;
537 # unshift @INC, $lib if $lib;
539 unshift @INC, $self->best_path_to_module_build
540 if $self->best_path_to_module_build;
542 ### this will generate warnings under anything lower than M::B 0.2606
543 my %buildflags = $dist->_buildflags_as_hash( $buildflags );
544 $dist->status->_buildflags( $buildflags );
546 my $fail; my $prereq_fail; my $test_fail;
549 ### this will set the directory back to the start
550 ### dir, so we must chdir /again/
551 my $ok = $dist->_resolve_prereqs(
553 format => $prereq_format,
555 prereqs => $self->status->prereqs,
556 target => $prereq_target,
557 prereq_build => $prereq_build,
560 unless( $cb->_chdir( dir => $dir ) ) {
561 error( loc( "Could not chdir to build directory '%1'", $dir ) );
566 #### use $dist->flush to reset the cache ###
567 error( loc( "Unable to satisfy prerequisites for '%1' " .
568 "-- aborting install", $self->module ) );
569 $dist->status->build(0);
570 $fail++; $prereq_fail++;
576 unless ( scalar run( command => [$perl, BUILD->($dir), $buildflags],
577 buffer => \$captured,
578 verbose => $verbose )
580 error( loc( "MAKE failed:\n%1", $captured ) );
581 $dist->status->build(0);
587 $dist->status->build(1);
589 ### add this directory to your lib ###
590 $self->add_to_includepath();
592 ### this buffer will not include what tests failed due to a
593 ### M::B/Test::Harness bug. Reported as #9793 with patch
594 ### against 0.2607 on 26/1/2005
595 unless( $skiptest ) {
597 my $flag = ON_VMS ? '"test"' : 'test';
598 my $cmd = [$perl, BUILD->($dir), $flag, $buildflags];
599 unless ( scalar run( command => $cmd,
600 buffer => \$test_output,
601 verbose => $verbose )
603 error( loc( "MAKE TEST failed:\n%1 ", $test_output ) );
605 ### mark specifically *test* failure.. so we dont
606 ### send success on force...
609 if( !$force and !$cb->_callbacks->proceed_on_test_failure->(
612 $dist->status->test(0);
618 msg( $test_output, 0 );
619 $dist->status->test(1);
623 msg(loc("Tests skipped"), $verbose);
627 unless( $cb->_chdir( dir => $orig ) ) {
628 error( loc( "Could not chdir back to start dir '%1'", $orig ) );
631 ### send out test report? ###
632 if( $conf->get_conf('cpantest') and not $prereq_fail ) {
635 failed => $test_fail || $fail,
636 buffer => CPANPLUS::Error->stack_as_string,
639 tests_skipped => $skiptest,
640 ) or error(loc("Failed to send test report for '%1'",
644 return $dist->status->created( $fail ? 0 : 1 );
647 =head2 $dist->install([verbose => BOOL, perl => /path/to/perl])
649 Actually installs the created dist.
651 Returns true on success and false on failure.
656 ### just in case you already did a create call for this module object
657 ### just via a different dist object
659 my $self = $dist->parent;
661 ### we're also the cpan_dist, since we don't need to have anything
662 ### prepared from another installer
663 $dist = $self->status->dist_cpan if $self->status->dist_cpan;
665 my $cb = $self->parent;
666 my $conf = $cb->configure_object;
670 my $verbose; my $perl; my $force;
671 { local $Params::Check::ALLOW_UNKNOWN = 1;
673 verbose => { default => $conf->get_conf('verbose'),
674 store => \$verbose },
675 force => { default => $conf->get_conf('force'),
677 perl => { default => $^X, store => \$perl },
680 my $args = check( $tmpl, \%hash ) or return;
681 $dist->status->_install_args( $args );
685 unless( $dir = $self->status->extract ) {
686 error( loc( "No dir found to operate on!" ) );
692 unless( $cb->_chdir( dir => $dir ) ) {
693 error( loc( "Could not chdir to build directory '%1'", $dir ) );
697 ### value set and false -- means failure ###
698 if( defined $self->status->installed &&
699 !$self->status->installed && !$force
701 error( loc( "Module '%1' has failed to install before this session " .
702 "-- aborting install", $self->module ) );
707 my $buildflags = $dist->status->_buildflags;
708 ### hmm, how is this going to deal with sudo?
709 ### for now, check effective uid, if it's not root,
710 ### shell out, otherwise use the method
713 ### don't worry about loading the right version of M::B anymore
714 ### the 'new_from_context' already added the 'right' path to
715 ### M::B at the top of the build.pl
716 ### On VMS, flags need to be quoted
717 my $flag = ON_VMS ? '"install"' : 'install';
718 my $cmd = [$perl, BUILD->($dir), $flag, $buildflags];
719 my $sudo = $conf->get_program('sudo');
720 unshift @$cmd, $sudo if $sudo;
724 unless( scalar run( command => $cmd,
726 verbose => $verbose )
728 error(loc("Could not run '%1': %2", 'Build install', $buffer));
732 my %buildflags = $dist->_buildflags_as_hash($buildflags);
735 my $flag = ON_VMS ? '"install"' : 'install';
736 my $cmd = [$perl, BUILD->($dir), $flag, $buildflags];
737 unless( scalar run( command => $cmd,
738 buffer => \$install_output,
739 verbose => $verbose )
741 error(loc("Could not run '%1': %2", 'Build install', $install_output));
745 msg( $install_output, 0 );
750 unless( $cb->_chdir( dir => $orig ) ) {
751 error( loc( "Could not chdir back to start dir '%1'", $orig ) );
754 return $dist->status->installed( $fail ? 0 : 1 );
757 ### returns the string 'foo=bar zot=quux' as (foo => bar, zot => quux)
758 sub _buildflags_as_hash {
760 my $flags = shift or return;
762 my @argv = Module::Build->split_like_shell($flags);
763 my ($argv) = Module::Build->read_args(@argv);
770 Originally by Jos Boumans E<lt>kane@cpan.orgE<gt>. Brought to working
771 condition by Ken Williams E<lt>kwilliams@cpan.orgE<gt>.
773 Other hackery and currently maintained by Chris 'BinGOs' Williams ( no relation ). E<lt>bingos@cpan.orgE<gt>.
777 The CPAN++ interface (of which this module is a part of) is
778 copyright (c) 2001, 2002, 2003, 2004, 2005 Jos Boumans E<lt>kane@cpan.orgE<gt>.
781 This library is free software;
782 you may redistribute and/or modify it under the same
783 terms as Perl itself.
791 # c-indentation-style: bsd
793 # indent-tabs-mode: nil
795 # vim: expandtab shiftwidth=4: