diff options
author | Karl Berry <karl@freefriends.org> | 2012-05-21 00:15:27 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2012-05-21 00:15:27 +0000 |
commit | a4c42bfb2337d37da89d789cb8cc226367994e32 (patch) | |
tree | c3eabdef5d565a4e515d2be0d9d4d0540bde0250 /Master/tlpkg/tlperl/lib/Module/Build/Base.pm | |
parent | 8274475057f024d35332ac47c2e2f23ea156e6ed (diff) |
perl 5.14.2 from siep
git-svn-id: svn://tug.org/texlive/trunk@26525 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/tlpkg/tlperl/lib/Module/Build/Base.pm')
-rw-r--r-- | Master/tlpkg/tlperl/lib/Module/Build/Base.pm | 797 |
1 files changed, 601 insertions, 196 deletions
diff --git a/Master/tlpkg/tlperl/lib/Module/Build/Base.pm b/Master/tlpkg/tlperl/lib/Module/Build/Base.pm index 5bd8ec72a55..fba916ad267 100644 --- a/Master/tlpkg/tlperl/lib/Module/Build/Base.pm +++ b/Master/tlpkg/tlperl/lib/Module/Build/Base.pm @@ -4,7 +4,7 @@ package Module::Build::Base; use strict; use vars qw($VERSION); -$VERSION = '0.3603'; +$VERSION = '0.3800'; $VERSION = eval $VERSION; BEGIN { require 5.00503 } @@ -62,6 +62,7 @@ EOF $self->dist_name; $self->dist_version; + $self->release_status; $self->_guess_module_name unless $self->module_name; $self->_find_nested_builds; @@ -95,8 +96,12 @@ sub resume { unless ($self->_perl_is_same($self->{properties}{perl})) { my $perl = $self->find_perl_interpreter; - $self->log_warn(" * WARNING: Configuration was initially created with '$self->{properties}{perl}',\n". - " but we are now using '$perl'.\n"); + die(<<"DIEFATAL"); +* FATAL ERROR: Perl interpreter mismatch. Configuration was initially + created with '$self->{properties}{perl}' + but we are now using '$perl'. You must + run 'Build realclean' or 'make realclean' and re-configure. +DIEFATAL } $self->cull_args(@ARGV); @@ -105,7 +110,7 @@ sub resume { my $mb_version = $Module::Build::VERSION; if ( $mb_version ne $self->{properties}{mb_version} ) { $self->log_warn(<<"MISMATCH"); -* WARNING: Configuration was initially created with Module::Build +* WARNING: Configuration was initially created with Module::Build version '$self->{properties}{mb_version}' but we are now using version '$mb_version'. If errors occur, you must re-run the Build.PL or Makefile.PL script. MISMATCH @@ -201,15 +206,15 @@ sub _construct { sub log_info { my $self = shift; - print @_ unless(ref($self) and $self->quiet); + print @_ if ref($self) && ( $self->verbose || ! $self->quiet ); } sub log_verbose { my $self = shift; - $self->log_info(@_) if(ref($self) and $self->verbose); + print @_ if ref($self) && $self->verbose; } sub log_debug { my $self = shift; - print @_ if ref $self && $self->debug; + print @_ if ref($self) && $self->debug; } sub log_warn { @@ -897,10 +902,13 @@ __PACKAGE__->add_property(build_bat => 0); __PACKAGE__->add_property(bundle_inc => []); __PACKAGE__->add_property(bundle_inc_preload => []); __PACKAGE__->add_property(config_dir => '_build'); +__PACKAGE__->add_property(dynamic_config => 1); __PACKAGE__->add_property(include_dirs => []); __PACKAGE__->add_property(license => 'unknown'); __PACKAGE__->add_property(metafile => 'META.yml'); __PACKAGE__->add_property(mymetafile => 'MYMETA.yml'); +__PACKAGE__->add_property(metafile2 => 'META.json'); +__PACKAGE__->add_property(mymetafile2 => 'MYMETA.json'); __PACKAGE__->add_property(recurse_into => []); __PACKAGE__->add_property(use_rcfile => 1); __PACKAGE__->add_property(create_packlist => 1); @@ -926,8 +934,7 @@ __PACKAGE__->add_property( ); { - my $Is_ActivePerl = eval {require ActivePerl::DocTools}; - __PACKAGE__->add_property(html_css => $Is_ActivePerl ? 'Active.css' : ''); + __PACKAGE__->add_property(html_css => ''); } { @@ -964,6 +971,7 @@ __PACKAGE__->add_property($_) for qw( dist_abstract dist_author dist_name + dist_suffix dist_version dist_version_from extra_compiler_flags @@ -984,6 +992,7 @@ __PACKAGE__->add_property($_) for qw( program_name quiet recursive_test_files + release_status script_files scripts share_dir @@ -1124,6 +1133,41 @@ sub dist_name { return $p->{dist_name}; } +sub release_status { + my ($self) = @_; + my $p = $self->{properties}; + + if ( ! defined $p->{release_status} ) { + $p->{release_status} = $self->_is_dev_version ? 'testing' : 'stable'; + } + + unless ( $p->{release_status} =~ qr/\A(?:stable|testing|unstable)\z/ ) { + die "Illegal value '$p->{release_status}' for release_status\n"; + } + + if ( $p->{release_status} eq 'stable' && $self->_is_dev_version ) { + my $version = $self->dist_version; + die "Illegal value '$p->{release_status}' with version '$version'\n"; + } + return $p->{release_status}; +} + +sub dist_suffix { + my ($self) = @_; + my $p = $self->{properties}; + return $p->{dist_suffix} if defined $p->{dist_suffix}; + + if ( $self->release_status eq 'stable' ) { + $p->{dist_suffix} = ""; + } + else { + # non-stable release but non-dev version number needs '-TRIAL' appended + $p->{dist_suffix} = $self->_is_dev_version ? "" : "TRIAL" ; + } + + return $p->{dist_suffix}; +} + sub dist_version_from { my ($self) = @_; my $p = $self->{properties}; @@ -1144,7 +1188,11 @@ sub dist_version { my $version_from = File::Spec->catfile( split( qr{/}, $dist_version_from ) ); my $pm_info = Module::Build::ModuleInfo->new_from_file( $version_from ) or die "Can't find file $version_from to determine version"; + #$p->{dist_version} is undef here $p->{dist_version} = $self->normalize_version( $pm_info->version() ); + unless (defined $p->{dist_version}) { + die "Can't determine distribution version from $version_from"; + } } die ("Can't determine distribution version, must supply either 'dist_version',\n". @@ -1154,6 +1202,16 @@ sub dist_version { return $p->{dist_version}; } +sub _is_dev_version { + my ($self) = @_; + my $dist_version = $self->dist_version; + my $version_obj = eval { Module::Build::Version->new( $dist_version ) }; + # assume it's normal if the version string is fatal -- in this case + # the author might be doing something weird so should play along and + # assume they'll specify all necessary behavior + return $@ ? 0 : $version_obj->is_alpha; +} + sub dist_author { shift->_pod_parse('author') } sub dist_abstract { shift->_pod_parse('abstract') } @@ -1182,6 +1240,26 @@ sub find_module_by_name { # Method provided for backwards compatibility return Module::Build::ModuleInfo->find_module_by_name(@_[1,2]); } +{ + # $unlink_list_for_pid{$$} = [ ... ] + my %unlink_list_for_pid; + + sub _unlink_on_exit { + my $self = shift; + for my $f ( @_ ) { + push @{$unlink_list_for_pid{$$}}, $f if -f $f; + } + return 1; + } + + END { + for my $f ( map glob($_), @{ $unlink_list_for_pid{$$} || [] } ) { + next unless -e $f; + File::Path::rmtree($f, 0, 0); + } + } +} + sub add_to_cleanup { my $self = shift; my %files = map {$self->localize_file_path($_), 1} @_; @@ -1389,8 +1467,8 @@ sub _feature_deps_msg { return $log_text; } -# Automatically detect and add prerequisites based on configuration -sub auto_require { +# Automatically detect configure_requires prereqs +sub auto_config_requires { my ($self) = @_; my $p = $self->{properties}; @@ -1401,6 +1479,10 @@ sub auto_require { && ! exists $p->{configure_requires}{'Module::Build'} ) { (my $ver = $VERSION) =~ s/^(\d+\.\d\d).*$/$1/; # last major release only + $self->log_warn(<<EOM); +Module::Build was not found in configure_requires! Adding it now +automatically as: configure_requires => { 'Module::Build' => $ver } +EOM $self->_add_prereq('configure_requires', 'Module::Build', $ver); } @@ -1415,6 +1497,14 @@ sub auto_require { } } + return; +} + +# Automatically detect and add prerequisites based on configuration +sub auto_require { + my ($self) = @_; + my $p = $self->{properties}; + # If needs_compiler is not explictly set, automatically set it # If set, we need ExtUtils::CBuilder (and a compiler) my $xs_files = $self->find_xs_files; @@ -1555,6 +1645,24 @@ sub _parse_conditions { } } +sub try_require { + my ($self, $modname, $spec) = @_; + my $status = $self->check_installed_status($modname, defined($spec) ? $spec : 0); + return unless $status->{ok}; + my $path = $modname; + $path =~ s{::}{/}g; + $path .= ".pm"; + if ( defined $INC{$path} ) { + return 1; + } + elsif ( exists $INC{$path} ) { # failed before, don't try again + return; + } + else { + return eval "require $modname"; + } +} + sub check_installed_status { my ($self, $modname, $spec) = @_; my %status = (need => $spec); @@ -1572,7 +1680,7 @@ sub check_installed_status { return \%status; } - $status{have} = $pm_info->version(); + $status{have} = eval { $pm_info->version() }; if ($spec and !defined($status{have})) { @status{ qw(have message) } = (undef, "Couldn't find a \$VERSION in prerequisite $modname"); return \%status; @@ -1684,11 +1792,15 @@ sub print_build_script { my $closedata=""; + my $config_requires; + if ( -f $self->metafile ) { + my $meta = eval { $self->read_metafile( $self->metafile ) }; + $config_requires = $meta && $meta->{configure_requires}{'Module::Build'}; + } + $config_requires ||= 0; + my %q = map {$_, $self->$_()} qw(config_dir base_dir); - my $case_tolerant = 0+(File::Spec->can('case_tolerant') - && File::Spec->case_tolerant); - $q{base_dir} = uc $q{base_dir} if $case_tolerant; $q{base_dir} = Win32::GetShortPathName($q{base_dir}) if $self->is_windowsish; $q{magic_numfile} = $self->config_file('magicnum'); @@ -1744,6 +1856,7 @@ $quoted_INC close(*DATA) unless eof(*DATA); # ensure no open handles to this script use $build_package; +Module::Build->VERSION(q{$config_requires}); # Some platforms have problems setting \$^X in shebang contexts, fix it up here \$^X = Module::Build->find_perl_interpreter; @@ -1766,38 +1879,72 @@ EOF sub create_mymeta { my ($self) = @_; - my $mymetafile = $self->mymetafile; - my $metafile = $self->metafile; - # cleanup - if ( $self->delete_filetree($mymetafile) ) { - $self->log_verbose("Removed previous '$mymetafile'\n"); + my ($meta_obj, $mymeta); + my @metafiles = ( $self->metafile, $self->metafile2 ); + my @mymetafiles = ( $self->mymetafile, $self->mymetafile2 ); + + # cleanup old MYMETA + for my $f ( @mymetafiles ) { + if ( $self->delete_filetree($f) ) { + $self->log_verbose("Removed previous '$f'\n"); + } } - $self->log_info("Creating new '$mymetafile' with configuration results\n"); - # use old meta and update prereqs, if possible - my $mymeta; - if ( -f $metafile ) { - $mymeta = eval { $self->read_metafile( $self->metafile ) }; + # Try loading META.json or META.yml + if ( $self->try_require("CPAN::Meta", "2.110420") ) { + for my $file ( @metafiles ) { + next unless -f $file; + $meta_obj = eval { CPAN::Meta->load_file($file) }; + last if $meta_obj; + } } - # if we read META OK, just update it + + # maybe get a copy in spec v2 format (regardless of original source) + $mymeta = $meta_obj->as_struct + if $meta_obj; + + # if we have metadata, just update it if ( defined $mymeta ) { my $prereqs = $self->_normalize_prereqs; - for my $t ( keys %$prereqs ) { - $mymeta->{$t} = $prereqs->{$t}; + # XXX refactor this mapping somewhere + $mymeta->{prereqs}{runtime}{requires} = $prereqs->{requires}; + $mymeta->{prereqs}{build}{requires} = $prereqs->{build_requires}; + $mymeta->{prereqs}{runtime}{recommends} = $prereqs->{recommends}; + $mymeta->{prereqs}{runtime}{conflicts} = $prereqs->{conflicts}; + # delete empty entries + for my $phase ( keys %{$mymeta->{prereqs}} ) { + if ( ref $mymeta->{prereqs}{$phase} eq 'HASH' ) { + for my $type ( keys %{$mymeta->{prereqs}{$phase}} ) { + if ( ! defined $mymeta->{prereqs}{$phase}{$type} + || ! keys %{$mymeta->{prereqs}{$phase}{$type}} + ) { + delete $mymeta->{prereqs}{$phase}{$type}; + } + } + } + if ( ! defined $mymeta->{prereqs}{$phase} + || ! keys %{$mymeta->{prereqs}{$phase}} + ) { + delete $mymeta->{prereqs}{$phase}; + } } + $mymeta->{dynamic_config} = 0; + $mymeta->{generated_by} = "Module::Build version $Module::Build::VERSION"; + eval { $meta_obj = CPAN::Meta->new( $mymeta, { lazy_validation => 1 } ) } } - # but generate from scratch, ignoring errors if META doesn't exist + # or generate from scratch, ignoring errors if META doesn't exist else { - $mymeta = $self->get_metadata( fatal => 0 ); + $meta_obj = $self->_get_meta_object( + quiet => 0, dynamic => 0, fatal => 0, auto => 0 + ); } - # MYMETA is always static - $mymeta->{dynamic_config} = 0; - # Note which M::B created it - $mymeta->{generated_by} = "Module::Build version $Module::Build::VERSION"; + my @created = $self->_write_meta_files( $meta_obj, 'MYMETA' ); + + $self->log_warn("Could not create MYMETA files\n") + unless @created; - $self->write_metafile( $mymetafile, $mymeta ); return 1; } @@ -1958,7 +2105,6 @@ sub _translate_option { create_readme extra_compiler_flags extra_linker_flags - html_css install_base install_path meta_add @@ -2068,7 +2214,7 @@ sub read_args { $args{$_} ||= []; $args{$_} = [ $args{$_} ] unless ref $args{$_}; foreach my $arg ( @{$args{$_}} ) { - $arg =~ /(\w+)=(.*)/ + $arg =~ /($opt_re)=(.*)/ or die "Malformed '$_' argument: '$arg' should be something like 'foo=bar'"; $hash{$1} = $2; } @@ -2208,6 +2354,11 @@ sub read_modulebuildrc { my ($global_opts) = $self->read_args( $self->split_like_shell( $options{'*'} || '' ) ); + + # let fakeinstall act like install if not provided + if ( $action eq 'fakeinstall' && ! exists $options{fakeinstall} ) { + $action = 'install'; + } my ($action_opts) = $self->read_args( $self->split_like_shell( $options{$action} || '' ) ); @@ -2552,6 +2703,8 @@ sub do_tests { my $tests = $self->find_test_files; + local $ENV{PERL_DL_NONLAZY} = 1; + if(@$tests) { my $args = $self->tap_harness_args; if($self->use_tap_harness or ($args and %$args)) { @@ -2667,7 +2820,7 @@ sub ACTION_testcover { # testcover was run. If so, start over. if (-e 'cover_db') { my $pm_files = $self->rscan_dir - (File::Spec->catdir($self->blib, 'lib'), file_qr('\.pm$') ); + (File::Spec->catdir($self->blib, 'lib'), $self->file_qr('\.pm$') ); my $cover_files = $self->rscan_dir('cover_db', sub {-f $_ and not /\.html$/}); $self->do_system(qw(cover -delete)) @@ -2728,11 +2881,19 @@ sub process_support_files { my $p = $self->{properties}; return unless $p->{c_source}; - push @{$p->{include_dirs}}, $p->{c_source}; + my $files; + if (ref($p->{c_source}) eq "ARRAY") { + push @{$p->{include_dirs}}, @{$p->{c_source}}; + for my $path (@{$p->{c_source}}) { + push @$files, @{ $self->rscan_dir($path, $self->file_qr('\.c(c|p|pp|xx|\+\+)?$')) }; + } + } else { + push @{$p->{include_dirs}}, $p->{c_source}; + $files = $self->rscan_dir($p->{c_source}, $self->file_qr('\.c(c|p|pp|xx|\+\+)?$')); + } - my $files = $self->rscan_dir($p->{c_source}, file_qr('\.c(c|p|pp|xx|\+\+)?$')); foreach my $file (@$files) { - push @{$p->{objects}}, $self->compile_c($file); + push @{$p->{objects}}, $self->compile_c($file); } } @@ -2852,8 +3013,10 @@ sub find_PL_files { } return unless -d 'lib'; - return { map {$_, [/^(.*)\.PL$/i ]} @{ $self->rscan_dir('lib', - file_qr('\.PL$')) } }; + return { + map {$_, [/^(.*)\.PL$/i ]} + @{ $self->rscan_dir('lib', $self->file_qr('\.PL$')) } + }; } sub find_pm_files { shift->_find_file_by_type('pm', 'lib') } @@ -2906,7 +3069,7 @@ sub _find_file_by_type { return { map {$_, $_} map $self->localize_file_path($_), grep !/\.\#/, - @{ $self->rscan_dir($dir, file_qr("\\.$type\$")) } }; + @{ $self->rscan_dir($dir, $self->file_qr("\\.$type\$")) } }; } sub localize_file_path { @@ -2934,7 +3097,7 @@ sub fix_shebang_line { # Adapted from fixin() in ExtUtils::MM_Unix 1.35 next unless $cmd =~ /perl/i; my $interpreter = $self->{properties}{perl}; - $self->log_verbose("Changing sharpbang in $file to $interpreter"); + $self->log_verbose("Changing sharpbang in $file to $interpreter\n"); my $shb = ''; $shb .= $c->get('sharpbang')."$interpreter $arg\n" if $does_shbang; @@ -2979,7 +3142,7 @@ sub ACTION_testpod { my @files = sort keys %{$self->_find_pods($self->libdoc_dirs)}, keys %{$self->_find_pods ($self->bindoc_dirs, - exclude => [ file_qr('\.bat$') ])} + exclude => [ $self->file_qr('\.bat$') ])} or die "Couldn't find any POD files to test\n"; { package # hide from PAUSE @@ -3033,6 +3196,24 @@ sub _is_default_installable { ) ? 1 : 0; } +sub _is_ActivePerl { +# return 0; + my $self = shift; + unless (exists($self->{_is_ActivePerl})) { + $self->{_is_ActivePerl} = (eval { require ActivePerl::DocTools; } || 0); + } + return $self->{_is_ActivePerl}; +} + +sub _is_ActivePPM { +# return 0; + my $self = shift; + unless (exists($self->{_is_ActivePPM})) { + $self->{_is_ActivePPM} = (eval { require ActivePerl::PPM; } || 0); + } + return $self->{_is_ActivePPM}; +} + sub ACTION_manpages { my $self = shift; @@ -3041,27 +3222,21 @@ sub ACTION_manpages { $self->depends_on('code'); foreach my $type ( qw(bin lib) ) { + next unless ( $self->invoked_action eq 'manpages' || $self->_is_default_installable("${type}doc")); my $files = $self->_find_pods( $self->{properties}{"${type}doc_dirs"}, - exclude => [ file_qr('\.bat$') ] ); + exclude => [ $self->file_qr('\.bat$') ] ); next unless %$files; my $sub = $self->can("manify_${type}_pods"); - next unless defined( $sub ); - - if ( $self->invoked_action eq 'manpages' ) { - $self->$sub(); - } elsif ( $self->_is_default_installable("${type}doc") ) { - $self->$sub(); - } + $self->$sub() if defined( $sub ); } - } sub manify_bin_pods { my $self = shift; my $files = $self->_find_pods( $self->{properties}{bindoc_dirs}, - exclude => [ file_qr('\.bat$') ] ); + exclude => [ $self->file_qr('\.bat$') ] ); return unless keys %$files; my $mandir = File::Spec->catdir( $self->blib, 'bindoc' ); @@ -3145,21 +3320,11 @@ sub ACTION_html { $self->depends_on('code'); foreach my $type ( qw(bin lib) ) { - my $files = $self->_find_pods( $self->{properties}{"${type}doc_dirs"}, - exclude => - [ file_qr('\.(?:bat|com|html)$') ] ); - next unless %$files; - - if ( $self->invoked_action eq 'html' ) { - $self->htmlify_pods( $type ); - } elsif ( $self->_is_default_installable("${type}html") ) { - $self->htmlify_pods( $type ); - } + next unless ( $self->invoked_action eq 'html' || $self->_is_default_installable("${type}html")); + $self->htmlify_pods( $type ); } - } - # 1) If it's an ActiveState perl install, we need to run # ActivePerl::DocTools->UpdateTOC; # 2) Links to other modules are not being generated @@ -3168,13 +3333,10 @@ sub htmlify_pods { my $type = shift; my $htmldir = shift || File::Spec->catdir($self->blib, "${type}html"); - require Module::Build::PodParser; - require Pod::Html; - $self->add_to_cleanup('pod2htm*'); my $pods = $self->_find_pods( $self->{properties}{"${type}doc_dirs"}, - exclude => [ file_qr('\.(?:bat|com|html)$') ] ); + exclude => [ $self->file_qr('\.(?:bat|com|html)$') ] ); return unless %$pods; # nothing to do unless ( -d $htmldir ) { @@ -3184,21 +3346,54 @@ sub htmlify_pods { my @rootdirs = ($type eq 'bin') ? qw(bin) : $self->installdirs eq 'core' ? qw(lib) : qw(site lib); + my $podroot = $ENV{PERL_CORE} + ? File::Basename::dirname($ENV{PERL_CORE}) + : $self->original_prefix('core'); + + my $htmlroot = $self->install_sets('core')->{libhtml}; + my @podpath = (map { File::Spec->abs2rel($_ ,$podroot) } grep { -d } + ( $self->install_sets('core', 'lib'), # lib + $self->install_sets('core', 'bin'), # bin + $self->install_sets('site', 'lib'), # site/lib + ) ), File::Spec->rel2abs($self->blib); + + my $podpath = $ENV{PERL_CORE} + ? File::Spec->catdir($podroot, 'lib') + : join(":", map { tr,:\\,|/,; $_ } @podpath); + + my $blibdir = join('/', File::Spec->splitdir( + (File::Spec->splitpath(File::Spec->rel2abs($htmldir),1))[1]),'' + ); - my $podpath = join ':', - map $_->[1], - grep -e $_->[0], - map [File::Spec->catdir($self->blib, $_), $_], - qw( script lib ); + my ($with_ActiveState, $htmltool); + if ( $with_ActiveState = $self->_is_ActivePerl + && eval { require ActivePerl::DocTools::Pod; 1 } + ) { + my $tool_v = ActiveState::DocTools::Pod->VERSION; + $htmltool = "ActiveState::DocTools::Pod"; + $htmltool .= " $tool_v" if $tool_v && length $tool_v; + } + else { + require Module::Build::PodParser; + require Pod::Html; + $htmltool = "Pod::Html " . Pod::Html->VERSION; + } + $self->log_verbose("Converting Pod to HTML with $htmltool\n"); + + my $errors = 0; + + POD: foreach my $pod ( keys %$pods ) { my ($name, $path) = File::Basename::fileparse($pods->{$pod}, - file_qr('\.(?:pm|plx?|pod)$')); + $self->file_qr('\.(?:pm|plx?|pod)$') + ); my @dirs = File::Spec->splitdir( File::Spec->canonpath( $path ) ); pop( @dirs ) if scalar(@dirs) && $dirs[-1] eq File::Spec->curdir; - my $fulldir = File::Spec->catfile($htmldir, @rootdirs, @dirs); + my $fulldir = File::Spec->catdir($htmldir, @rootdirs, @dirs); + my $tmpfile = File::Spec->catfile($fulldir, "${name}.tmp"); my $outfile = File::Spec->catfile($fulldir, "${name}.html"); my $infile = File::Spec->abs2rel($pod); @@ -3209,38 +3404,82 @@ sub htmlify_pods { or die "Couldn't mkdir $fulldir: $!"; } - my $path2root = join( '/', ('..') x (@rootdirs+@dirs) ); - my $htmlroot = join( '/', - ($path2root, - $self->installdirs eq 'core' ? () : qw(site) ) ); - - my $fh = IO::File->new($infile) or die "Can't read $infile: $!"; - my $abstract = Module::Build::PodParser->new(fh => $fh)->get_abstract(); - - my $title = join( '::', (@dirs, $name) ); - $title .= " - $abstract" if $abstract; - - my @opts = ( - '--flush', - "--title=$title", - "--podpath=$podpath", - "--infile=$infile", - "--outfile=$outfile", - '--podroot=' . $self->blib, - "--htmlroot=$htmlroot", - ); - - if ( eval{Pod::Html->VERSION(1.03)} ) { - push( @opts, ('--header', '--backlink=Back to Top') ); - push( @opts, "--css=$path2root/" . $self->html_css) if $self->html_css; + $self->log_verbose("HTMLifying $infile -> $outfile\n"); + if ( $with_ActiveState ) { + my $depth = @rootdirs + @dirs; + my %opts = ( infile => $infile, + outfile => $tmpfile, + podpath => $podpath, + podroot => $podroot, + index => 1, + depth => $depth, + ); + eval { + ActivePerl::DocTools::Pod::pod2html(%opts); + 1; + } or $self->log_warn("[$htmltool] pod2html (" . + join(", ", map { "q{$_} => q{$opts{$_}}" } (keys %opts)) . ") failed: $@"); + } else { + my $path2root = join( '/', ('..') x (@rootdirs+@dirs) ); + my $fh = IO::File->new($infile) or die "Can't read $infile: $!"; + my $abstract = Module::Build::PodParser->new(fh => $fh)->get_abstract(); + + my $title = join( '::', (@dirs, $name) ); + $title .= " - $abstract" if $abstract; + + my @opts = ( + '--flush', + "--title=$title", + "--podpath=$podpath", + "--infile=$infile", + "--outfile=$tmpfile", + "--podroot=$podroot", + "--htmlroot=$path2root", + ); + + if ( eval{Pod::Html->VERSION(1.03)} ) { + push( @opts, ('--header', '--backlink=Back to Top') ); + } + + $self->log_verbose("P::H::pod2html @opts\n"); + { + my $orig = Cwd::getcwd(); + eval { Pod::Html::pod2html(@opts); 1 } + or $self->log_warn("[$htmltool] pod2html( " . + join(", ", map { "q{$_}" } @opts) . ") failed: $@"); + chdir($orig); + } + } + # We now have to cleanup the resulting html file + if ( ! -r $tmpfile ) { + $errors++; + next POD; } + my $fh = IO::File->new($tmpfile) or die "Can't read $tmpfile: $!"; + my $html = join('',<$fh>); + $fh->close; + if (!$self->_is_ActivePerl) { + # These fixups are already done by AP::DT:P:pod2html + # The output from pod2html is NOT XHTML! + # IE6+ will display content that is not valid for DOCTYPE + $html =~ s#^<!DOCTYPE .*?>#<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">#im; + $html =~ s#<html xmlns="http://www.w3.org/1999/xhtml">#<html>#i; + + # IE6+ will not display local HTML files with strict + # security without this comment + $html =~ s#<head>#<head>\n<!-- saved from url=(0017)http://localhost/ -->#i; + } + # Fixup links that point to our temp blib + $html =~ s/\Q$blibdir\E//g; - $self->log_verbose("HTMLifying $infile -> $outfile\n"); - $self->log_verbose("pod2html @opts\n"); - eval { Pod::Html::pod2html(@opts); 1 } - or $self->log_warn("pod2html @opts failed: $@"); + $fh = IO::File->new(">$outfile") or die "Can't write $outfile: $!"; + print $fh $html; + $fh->close; + unlink($tmpfile); } + return ! $errors; + } # Adapted from ExtUtils::MM_Unix @@ -3285,7 +3524,7 @@ sub ACTION_diff { delete $installmap->{read}; delete $installmap->{write}; - my $text_suffix = file_qr('\.(pm|pod)$'); + my $text_suffix = $self->file_qr('\.(pm|pod)$'); while (my $localdir = each %$installmap) { my @localparts = File::Spec->splitdir($localdir); @@ -3323,7 +3562,35 @@ sub ACTION_install { my ($self) = @_; require ExtUtils::Install; $self->depends_on('build'); - ExtUtils::Install::install($self->install_map, $self->verbose, 0, $self->{args}{uninst}||0); + # RT#63003 suggest that odd cirmstances that we might wind up + # in a different directory than we started, so wrap with _do_in_dir to + # ensure we get back to where we started; hope this fixes it! + $self->_do_in_dir( ".", sub { + ExtUtils::Install::install( + $self->install_map, $self->verbose, 0, $self->{args}{uninst}||0 + ); + }); + if ($self->_is_ActivePerl && $self->{_completed_actions}{html}) { + $self->log_info("Building ActivePerl Table of Contents\n"); + eval { ActivePerl::DocTools::WriteTOC(verbose => $self->verbose ? 1 : 0); 1; } + or $self->log_warn("AP::DT:: WriteTOC() failed: $@"); + } + if ($self->_is_ActivePPM) { + # We touch 'lib/perllocal.pod'. There is an existing logic in subroutine _init_db() + # of 'ActivePerl/PPM/InstallArea.pm' that says that if 'lib/perllocal.pod' has a 'date-last-touched' + # greater than that of the PPM SQLite databases ('etc/ppm-perl-area.db' and/or + # 'site/etc/ppm-site-area.db') then the PPM SQLite databases are rebuilt from scratch. + + # in the following line, 'perllocal.pod' this is *always* 'lib/perllocal.pod', never 'site/lib/perllocal.pod' + my $F_perllocal = File::Spec->catfile($self->install_sets('core', 'lib'), 'perllocal.pod'); + my $dt_stamp = time; + + $self->log_info("For ActivePerl's PPM: touch '$F_perllocal'\n"); + + open my $perllocal, ">>", $F_perllocal; + close $perllocal; + utime($dt_stamp, $dt_stamp, $F_perllocal); + } } sub ACTION_fakeinstall { @@ -3390,8 +3657,13 @@ sub ACTION_installdeps { # relative command should be relative to our active Perl # so we need to locate that command if ( ! File::Spec->file_name_is_absolute( $command ) ) { + # prefer site to vendor to core + my @loc = ( 'site', 'vendor', '' ); my @bindirs = File::Basename::dirname($self->perl); - push @bindirs, map {$self->config->{"install${_}bin"}} '','site','vendor'; + push @bindirs, + map { + ($self->config->{"install${_}bin"}, $self->config->{"install${_}script"}) + } @loc; for my $d ( @bindirs ) { my $abs_cmd = $self->find_command(File::Spec->catfile( $d, $command )); if ( defined $abs_cmd ) { @@ -3421,7 +3693,7 @@ sub ACTION_realclean { $self->depends_on('clean'); $self->log_info("Cleaning up configuration files\n"); $self->delete_filetree( - $self->config_dir, $self->mymetafile, $self->build_script + $self->config_dir, $self->mymetafile, $self->mymetafile2, $self->build_script ); } @@ -3476,7 +3748,6 @@ sub ACTION_ppmdist { } foreach my $type ( qw(bin lib) ) { - local $self->{properties}{html_css} = 'Active.css'; $self->htmlify_pods( $type, File::Spec->catdir($ppm, 'blib', 'html') ); } @@ -3513,7 +3784,8 @@ sub ACTION_pardist { sub ACTION_dist { my ($self) = @_; - $self->depends_on('distdir'); + # MUST dispatch() and not depends_ok() so we generate a clean distdir + $self->dispatch('distdir'); my $dist_dir = $self->dist_dir; @@ -3553,9 +3825,12 @@ sub _check_mymeta_skip { my $mymetafile = $self->mymetafile; # we can't check it, just add it anyway to be safe - unless ( $skip_factory && $skip_factory->($maniskip)->($mymetafile) ) { - $self->log_warn("File '$maniskip' does not include '$mymetafile'. Adding it now.\n"); - $self->_append_maniskip("^$mymetafile\$", $maniskip); + for my $file ( $self->mymetafile, $self->mymetafile2 ) { + unless ( $skip_factory && $skip_factory->($maniskip)->($file) ) { + $self->log_warn("File '$maniskip' does not include '$file'. Adding it now.\n"); + my $safe = quotemeta($file); + $self->_append_maniskip("^$safe\$", $maniskip); + } } } @@ -3609,7 +3884,7 @@ sub _sign_dir { sub _do_in_dir { my ($self, $dir, $do) = @_; - my $start_dir = $self->cwd; + my $start_dir = File::Spec->rel2abs($self->cwd); chdir $dir or die "Can't chdir() to $dir: $!"; eval {$do->()}; my @err = $@ ? ($@) : (); @@ -3645,7 +3920,11 @@ sub do_create_makefile_pl { my $self = shift; require Module::Build::Compat; $self->log_info("Creating Makefile.PL\n"); - Module::Build::Compat->create_makefile_pl($self->create_makefile_pl, $self, @_); + eval { Module::Build::Compat->create_makefile_pl($self->create_makefile_pl, $self, @_) }; + if ( $@ ) { + 1 while unlink 'Makefile.PL'; + die "$@\n"; + } $self->_add_to_manifest('MANIFEST', 'Makefile.PL'); } @@ -3659,19 +3938,16 @@ sub do_create_license { } my $l = $self->license - or die "No license specified"; - - my $key = $self->valid_licenses->{$l} - or die "'$l' isn't a license key we know about"; - my $class = "Software::License::$key"; + or die "Can't create LICENSE file: No license specified\n"; - eval "use $class; 1" - or die "Can't load Software::License::$key to create LICENSE file: $@"; + my $license = $self->_software_license_object + or die << "HERE"; +Can't create LICENSE file: '$l' is not a valid license key +or Software::License subclass; +HERE $self->delete_filetree('LICENSE'); - my $author = join " & ", @{ $self->dist_author }; - my $license = $class->new({holder => $author}); my $fh = IO::File->new('> LICENSE') or die "Can't write LICENSE file: $!"; print $fh $license->fulltext; @@ -3770,9 +4046,6 @@ sub ACTION_distdir { $self->depends_on('distmeta'); - # Must not include MYMETA - $self->_check_mymeta_skip('MANIFEST.SKIP'); - my $dist_files = $self->_read_manifest('MANIFEST') or die "Can't create distdir without a MANIFEST file - run 'manifest' action first.\n"; delete $dist_files->{SIGNATURE}; # Don't copy, create a fresh one @@ -3788,6 +4061,7 @@ sub ACTION_distdir { $self->add_to_cleanup($dist_dir); foreach my $file (keys %$dist_files) { + next if $file =~ m{^MYMETA\.}; # Double check that we skip MYMETA.* my $new = $self->copy_if_modified(from => $file, to_dir => $dist_dir, verbose => 0); } @@ -3815,6 +4089,22 @@ sub ACTION_disttest { }); } +sub ACTION_distinstall { + my ($self, @args) = @_; + + $self->depends_on('distdir'); + + $self->_do_in_dir ( $self->dist_dir, + sub { + $self->run_perl_script('Build.PL') + or die "Error executing 'Build.PL' in dist directory: $!"; + $self->run_perl_script('Build') + or die "Error executing 'Build' in dist directory: $!"; + $self->run_perl_script('Build', [], ['install']) + or die "Error executing 'Build install' in dist directory"; + } + ); +} =begin private @@ -3873,12 +4163,33 @@ Reads $file and returns the $content. sub _slurp { my $self = shift; my $file = shift; - open my $fh, "<", $file or croak "Can't open $file: $!"; + my $mode = shift || ""; + open my $fh, "<$mode", $file or croak "Can't open $file for reading: $!"; local $/; return <$fh>; } +sub _spew { + my $self = shift; + my $file = shift; + my $content = shift || ""; + my $mode = shift || ""; + open my $fh, ">$mode", $file or croak "Can't open $file for writing: $!"; + print {$fh} $content; + close $fh; +} +sub _case_tolerant { + my $self = shift; + if ( ref $self ) { + $self->{_case_tolerant} = File::Spec->case_tolerant + unless defined($self->{_case_tolerant}); + return $self->{_case_tolerant}; + } + else { + return File::Spec->case_tolerant; + } +} sub _append_maniskip { my $self = shift; @@ -3933,7 +4244,7 @@ sub _check_manifest_skip { if ( ! -e $maniskip ) { $self->log_warn("File '$maniskip' does not exist: Creating a temporary '$maniskip'\n"); $self->_write_default_maniskip($maniskip); - $self->add_to_cleanup($maniskip); + $self->_unlink_on_exit($maniskip); } else { # MYMETA must not be added to MANIFEST, so always confirm the skip @@ -3953,14 +4264,28 @@ sub ACTION_manifest { ExtUtils::Manifest::mkmanifest(); } +sub ACTION_manifest_skip { + my ($self) = @_; + + if ( -e 'MANIFEST.SKIP' ) { + $self->log_warn("MANIFEST.SKIP already exists.\n"); + return 0; + } + $self->log_info("Creating a new MANIFEST.SKIP file\n"); + return $self->_write_default_maniskip; + return -e 'MANIFEST.SKIP' +} + # Case insensitive regex for files sub file_qr { - return File::Spec->case_tolerant ? qr($_[0])i : qr($_[0]); + return shift->{_case_tolerant} ? qr($_[0])i : qr($_[0]); } sub dist_dir { my ($self) = @_; - return join "-", $self->dist_name, $self->dist_version; + my $dir = join "-", $self->dist_name, $self->dist_version; + $dir .= "-" . $self->dist_suffix if $self->dist_suffix; + return $dir; } sub ppm_name { @@ -4058,13 +4383,13 @@ sub script_files { } my %pl_files = map { - File::Spec->canonpath( File::Spec->case_tolerant ? uc $_ : $_ ) => 1 + File::Spec->canonpath( $_ ) => 1 } keys %{ $self->PL_files || {} }; my @bin_files = $self->_files_in('bin'); my %bin_map = map { - $_ => File::Spec->canonpath( File::Spec->case_tolerant ? uc $_ : $_ ) + $_ => File::Spec->canonpath( $_ ) } @bin_files; return $_ = { map {$_ => 1} grep !$pl_files{$bin_map{$_}}, @bin_files }; @@ -4123,6 +4448,32 @@ BEGIN { *scripts = \&script_files; } } } +# use mapping or license name directly +sub _software_license_object { + my ($self) = @_; + return unless defined( my $license = $self->license ); + + my $class; + LICENSE: for my $l ( $self->valid_licenses->{ $license }, $license ) { + next unless defined $l; + my $trial = "Software::License::" . $l; + if ( eval "require Software::License; Software::License->VERSION(0.014); require $trial; 1" ) { + $class = $trial; + last LICENSE; + } + } + return unless defined $class; + + # Software::License requires a 'holder' argument + my $author = join( " & ", @{ $self->dist_author }) || 'unknown'; + my $sl = eval { $class->new({holder=>$author}) }; + if ( $@ ) { + $self->log_warn( "Error getting '$class' object: $@" ); + } + + return $sl; +} + sub _hash_merge { my ($self, $h, $k, $v) = @_; if (ref $h->{$k} eq 'ARRAY') { @@ -4136,7 +4487,6 @@ sub _hash_merge { sub ACTION_distmeta { my ($self) = @_; - $self->do_create_makefile_pl if $self->create_makefile_pl; $self->do_create_readme if $self->create_readme; $self->do_create_license if $self->create_license; @@ -4148,19 +4498,15 @@ sub do_create_metafile { return if $self->{wrote_metadata}; my $p = $self->{properties}; - my $metafile = $self->metafile; unless ($p->{license}) { $self->log_warn("No license specified, setting license = 'unknown'\n"); $p->{license} = 'unknown'; } - unless (exists $self->valid_licenses->{ $p->{license} }) { - die "Unknown license type '$p->{license}'"; - } + my @metafiles = ( $self->metafile, $self->metafile2 ); # If we're in the distdir, the metafile may exist and be non-writable. - $self->delete_filetree($metafile); - $self->log_info("Creating $metafile\n"); + $self->delete_filetree($_) for @metafiles; # Since we're building ourself, we have to do some special stuff # here: the ConfigData module is found in blib/lib. @@ -4170,49 +4516,85 @@ sub do_create_metafile { push @INC, File::Spec->catdir($self->blib, 'lib'); } - if ($self->write_metafile($self->metafile,$self->get_metadata(fatal=>1))){ + my $meta_obj = $self->_get_meta_object( + quiet => 1, fatal => 1, auto => 1 + ); + my @created = $self->_write_meta_files( $meta_obj, 'META' ); + if ( @created ) { $self->{wrote_metadata} = 1; - $self->_add_to_manifest('MANIFEST', $metafile); + $self->_add_to_manifest('MANIFEST', $_) for @created; } - return 1; } -sub read_metafile { +sub _write_meta_files { my $self = shift; - my ($metafile) = @_; - my $yaml; + my ($meta, $file) = @_; + $file =~ s{\.(?:yml|json)$}{}; + + my @created; + push @created, "$file\.yml" + if $meta && $meta->save( "$file\.yml", {version => "1.4"} ); + push @created, "$file\.json" + if $meta && $meta->save( "$file\.json" ); + + if ( @created ) { + $self->log_info("Created " . join(" and ", @created) . "\n"); + } + return @created; +} + +sub _get_meta_object { + my $self = shift; + my %args = @_; + return unless $self->try_require("CPAN::Meta", "2.110420"); + + my $meta; + eval { + my $data = $self->get_metadata( + fatal => $args{fatal}, + auto => $args{auto}, + ); + $data->{dynamic_config} = $args{dynamic} if defined $args{dynamic}; + $meta = CPAN::Meta->create( $data ); + }; + if ($@ && ! $args{quiet}) { + $self->log_warn( + "Could not get valid metadata. Error is: $@\n" + ); + } - my $class = $self->_mb_feature('YAML_support') - ? 'YAML::Tiny' : 'Module::Build::YAML' ; + return $meta; +} - eval "require $class; 1" or die $@; - my $meta = $class->read($metafile) - or $self->log_warn( "Error reading '$metafile': " . $class->errstr . "\n"); +# We return a version 1.4 structure for backwards compatibility +sub read_metafile { + my $self = shift; + my ($metafile) = @_; - return $meta->[0] || {}; + return unless $self->try_require("CPAN::Meta", "2.110420"); + my $meta = CPAN::Meta->load_file($metafile); + return $meta->as_struct( {version => "1.4"} ); } +# For legacy compatibility, we upconvert a 1.4 data structure, ensuring +# validity, and then downconvert it back to save it. +# +# generally, this code should no longer be used sub write_metafile { my $self = shift; - my ($metafile, $node) = @_; - my $yaml; + my ($metafile, $struct) = @_; - if ($self->_mb_feature('YAML_support')) { - # XXX this is probably redundant, but stick with it - require YAML::Tiny; - $yaml = YAML::Tiny->new($node); - } else { - require Module::Build::YAML; - $yaml = Module::Build::YAML->new($node); - } - my $result = $yaml->write($metafile) - or $self->log_warn( "Error writing '$metafile': " . $yaml->errstr . "\n"); - return $result; + return unless $self->try_require("CPAN::Meta", "2.110420"); + + my $meta = CPAN::Meta->new( $struct ); + return $meta->save( $metafile, { version => "1.4" } ); } sub normalize_version { my ($self, $version) = @_; + $version = 0 unless defined $version and length $version; + if ( $version =~ /[=<>!,]/ ) { # logic, not just version # take as is without modification } @@ -4247,7 +4629,6 @@ sub _normalize_prereqs { return \%prereq_types; } - # wrapper around old prepare_metadata API; sub get_metadata { my ($self, %args) = @_; @@ -4270,6 +4651,8 @@ sub prepare_metadata { my $fatal = $args->{fatal} || 0; my $p = $self->{properties}; + $self->auto_config_requires if $args->{auto}; + # A little helper sub my $add_node = sub { my ($name, $val) = @_; @@ -4277,11 +4660,11 @@ sub prepare_metadata { push @$keys, $name if $keys; }; - foreach (qw(dist_name dist_version dist_author dist_abstract license)) { - (my $name = $_) =~ s/^dist_//; - $add_node->($name, $self->$_()); - unless ( defined($node->{$name}) && length($node->{$name}) ) { - my $err = "ERROR: Missing required field '$_' for metafile\n"; + # validate required fields + foreach my $f (qw(dist_name dist_version dist_author dist_abstract license)) { + my $field = $self->$f(); + unless ( defined $field and length $field ) { + my $err = "ERROR: Missing required field '$f' for metafile\n"; if ( $fatal ) { die $err; } @@ -4290,33 +4673,46 @@ sub prepare_metadata { } } } + + + # add dist_* fields + foreach my $f (qw(dist_name dist_version dist_author dist_abstract)) { + (my $name = $f) =~ s/^dist_//; + $add_node->($name, $self->$f()); + } + + # normalize version $node->{version} = $self->normalize_version($node->{version}); - if (defined( my $l = $self->license )) { - unless ( exists $self->valid_licenses->{ $l } ) { - my $err = "Unknown license string '$l'"; - if ( $fatal ) { - die $err; - } - else { - $self->log_warn($err); - } - } + # validate license information + my $license = $self->license; + my ($meta_license, $meta_license_url); - if (my $key = $self->valid_licenses->{ $l }) { - my $class = "Software::License::$key"; - if (eval "require Software::License; require $class; 1") { - # S::L requires a 'holder' key - $node->{resources}{license} = $class->new({holder=>"nobody"})->url; - } - else { - $node->{resources}{license} = $self->_license_url($l); - } - } - # XXX we are silently omitting the url for any unknown license + # XXX this is still meta spec version 1 stuff + + # if Software::License::* exists, then we can use it to get normalized name + # for META files + + if ( my $sl = $self->_software_license_object ) { + $meta_license = $sl->meta_name; + $meta_license_url = $sl->url; + } + elsif ( exists $self->valid_licenses()->{$license} ) { + $meta_license = $license; + $meta_license_url = $self->_license_url( $license ); + } + else { + # if we didn't find a license from a Software::License class, + # then treat it as unknown + $self->log_warn( "Can not determine license type for '" . $self->license + . "'\nSetting META license field to 'unknown'.\n"); + $meta_license = 'unknown'; } + $node->{license} = $meta_license; + $node->{resources}{license} = $meta_license_url if defined $meta_license_url; + # add prerequisite data my $prereqs = $self->_normalize_prereqs; for my $t ( keys %$prereqs ) { $add_node->($t, $prereqs->{$t}); @@ -4385,6 +4781,8 @@ sub find_dist_packages { return $self->find_packages_in_files(\@pm_files, \%dist_files); } +# XXX Do not document this function; mst wrote it and now says the API is +# stupid and needs to be fixed and it shouldn't become a public API until then sub find_packages_in_files { my ($self, $file_list, $filename_map) = @_; @@ -4791,6 +5189,13 @@ sub install_map { my $localdir = File::Spec->catdir( $blib, $type ); next unless -e $localdir; + # the line "...next if (($type eq 'bindoc'..." was one of many changes introduced for + # improving HTML generation on ActivePerl, see https://rt.cpan.org/Public/Bug/Display.html?id=53478 + # Most changes were ok, but this particular line caused test failures in t/manifypods.t on windows, + # therefore it is commented out. + + # ********* next if (($type eq 'bindoc' || $type eq 'libdoc') && not $self->is_unixish); + if (my $dest = $self->install_destination($type)) { $map{$localdir} = $dest; } else { @@ -5234,7 +5639,7 @@ sub dir_contains { return 0 if @second_dirs < @first_dirs; - my $is_same = ( File::Spec->case_tolerant + my $is_same = ( $self->_case_tolerant ? sub {lc(shift()) eq lc(shift())} : sub {shift() eq shift()} ); |