Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

automate perldelta "new modules" section #12720

Closed
p5pRT opened this issue Jan 21, 2013 · 32 comments
Closed

automate perldelta "new modules" section #12720

p5pRT opened this issue Jan 21, 2013 · 32 comments

Comments

@p5pRT
Copy link

p5pRT commented Jan 21, 2013

Migrated from rt.perl.org#116475 (status was 'resolved')

Searchable as RT116475$

@p5pRT
Copy link
Author

p5pRT commented Jan 21, 2013

From @rjbs

Each perldelta contains a section that lists what modules changed. This
could be generated automatically by using Module​::CoreList.

In fact, this has been begun with the program
Porting/corelist-perldelta.pl.

Look at Porting/release_managers_guide.pod for information on writing a
perldelta, as well as Porting/perldelta_template.pod

Determine whether the existing program works. Test it a couple times on
different versions and comare what it says with the output of corelist
--diff. Add documentation of how to use it to the release manager's
guide, where the current section on updating the module-updates delta
is.

@p5pRT
Copy link
Author

p5pRT commented Sep 13, 2013

From abiviq@hushmail.com

I would like to attempt this but I have questions concerning the
intended scope of the automation.

Both the release manager's guide and the notes in "how to write a
perldelta" appear to assume that the list won't be written until near
the end of the release cycle, ideally after Module​::CoreList has been
updated.

However, by looking at the commits to perldelta, it appears that current
practice is that an entry is added for a module when the updates for the
module itself are committed, or very soon after.

In light of this incremental approach, is improving
corelist-perldelta.pl still desired?

On Mon Jan 21 07​:52​:38 2013, rjbs wrote​:

Each perldelta contains a section that lists what modules changed. This
could be generated automatically by using Module​::CoreList.

In fact, this has been begun with the program
Porting/corelist-perldelta.pl.

Look at Porting/release_managers_guide.pod for information on writing a
perldelta, as well as Porting/perldelta_template.pod

Determine whether the existing program works. Test it a couple times on
different versions and comare what it says with the output of corelist
--diff. Add documentation of how to use it to the release manager's
guide, where the current section on updating the module-updates delta
is.

@p5pRT
Copy link
Author

p5pRT commented Sep 13, 2013

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Sep 13, 2013

From @rjbs

* Abir Viqar via RT <perlbug-followup@​perl.org> [2013-09-12T21​:10​:26]

In light of this incremental approach, is improving
corelist-perldelta.pl still desired?

Definitely. The reason that it is done throughout the month is that if it is
not, then it becomes a big boring task at the end of the release cycle. If it
was instead a single program to be run at that time, there'd be no reason to do
it at other times.

Now, that said, there is one useful point. Often, all that is needed is the
note that "X was upgraded from v1 to v2" but sometimes more information is
needed. "X was upgraded from v1 to v2 and now support Foozles." An automated
program should leave those in place. Even better would be to notice them and
then make sure that their claim is accurate!

Let me (or p5p) know if you have any further questions. I'm always very
excited to see more automation fo the release process!

--
rjbs

@p5pRT
Copy link
Author

p5pRT commented Sep 15, 2013

From @steve-m-hay

I think a program to ensure that each module that has been upgraded
since the previous release is listed in perldelta would be useful. (At
the moment (as RM for this month and last) I keep an eye on commits each
day, and also manually diff the previous release with the current
release-to-be just before release to check that nothing has been missed.)

However, there is a limit to what can be automated. Certainly the
"L<Module> has been upgraded from version X to Y." item could be added
if missing, but writing the detail which follows that line, describing
what has been changed, is really a manual process. Not every module has
a Changes file that could be scanned for a list of changes, and even
where such a file exists I do not deem it appropriate to list every
single change in the perldelta file​: that would be too much duplication.
Instead, I try to summarize what the major changes are, and sometimes
refer the reader to the Changes file for more detail if necessary. This
summarizing process will always be a manual task since it requires human
judgement.

The program should be tolerant of upgrade entries which already exist
(i.e. it must not go adding duplicate sections for a given module)
because I personally would still be inclined to perform this process
incrementally through the month to avoid having the whole of that
necessarily manual part still to do at release time.

So really I think the program just needs to add

  =item *

  L<Module> has been upgraded from version X to Y.

  XXX TODO

for each upgraded Module that isn't already listed.

Take care over the name of the Module. The directory names in cpan/ and
dist/ are mostly (if not all?) the names of CPAN *distributions* rather
than *modules*, but perldelta generally lists a single "key" module name
for each one since this is more meaningful to users of the perl core,
who may not even be aware of the dual-lived nature of some modules.
Having said that, if a distribution contains very many modules that have
been upgraded then it might be better to refer to a collection of
modules by their distribution name instead. (Thus, if Scalar-List-Utils
is upgraded, then it is probably best to mention each module
(Scalar​::Util and List​::Util) separately, especially if only one has
been changed. But if IO-Compress is upgraded and lots of modules in it
have been changed, perhaps with similar changes in each file, then it
would be excessive to list them all separately.)

Thank you for your interest in helping to automate parts of the release
process! :-)

@p5pRT
Copy link
Author

p5pRT commented Sep 15, 2013

From [Unknown Contact. See original ticket]

I think a program to ensure that each module that has been upgraded
since the previous release is listed in perldelta would be useful. (At
the moment (as RM for this month and last) I keep an eye on commits each
day, and also manually diff the previous release with the current
release-to-be just before release to check that nothing has been missed.)

However, there is a limit to what can be automated. Certainly the
"L<Module> has been upgraded from version X to Y." item could be added
if missing, but writing the detail which follows that line, describing
what has been changed, is really a manual process. Not every module has
a Changes file that could be scanned for a list of changes, and even
where such a file exists I do not deem it appropriate to list every
single change in the perldelta file​: that would be too much duplication.
Instead, I try to summarize what the major changes are, and sometimes
refer the reader to the Changes file for more detail if necessary. This
summarizing process will always be a manual task since it requires human
judgement.

The program should be tolerant of upgrade entries which already exist
(i.e. it must not go adding duplicate sections for a given module)
because I personally would still be inclined to perform this process
incrementally through the month to avoid having the whole of that
necessarily manual part still to do at release time.

So really I think the program just needs to add

  =item *

  L<Module> has been upgraded from version X to Y.

  XXX TODO

for each upgraded Module that isn't already listed.

Take care over the name of the Module. The directory names in cpan/ and
dist/ are mostly (if not all?) the names of CPAN *distributions* rather
than *modules*, but perldelta generally lists a single "key" module name
for each one since this is more meaningful to users of the perl core,
who may not even be aware of the dual-lived nature of some modules.
Having said that, if a distribution contains very many modules that have
been upgraded then it might be better to refer to a collection of
modules by their distribution name instead. (Thus, if Scalar-List-Utils
is upgraded, then it is probably best to mention each module
(Scalar​::Util and List​::Util) separately, especially if only one has
been changed. But if IO-Compress is upgraded and lots of modules in it
have been changed, perhaps with similar changes in each file, then it
would be excessive to list them all separately.)

Thank you for your interest in helping to automate parts of the release
process! :-)

@p5pRT
Copy link
Author

p5pRT commented Oct 6, 2013

From abiviq@hushmail.com

The patches attached are what I have attempted so far.

Patches 1-6 are mostly improvements to corelist-perldelta.pl's check
mode that are unrelated to the task at hand, but were helpful in
observing past perldelta.

Patch 7 includes fixes for the determination of changed modules that
takes into account the mapping between distribution and key module based
on how their changes have been listed in past perldelta. With
this, the only distributions that are currently unaccounted for are
podlater, libnet, and Win32. However, the determination of removed
modules still includes too many false positives to be useful.

Patch 9 is my initial attempt at being able to update an existing
perldelta file by adding unlisted modules and updating the version
number of listed modules if they differ from Module​::CoreList. I used
the non-core Pod​::Elemental for pod manipulation. The core pod parsing
modules don't seem to allow easy serialization back to pod, especially
without including whitespace differences. Is the use of Pod​::Elemental
acceptable, and if not, what alternate approach should I take?

I have not included documentation changes pending comments to my patches.
On Sun Sep 15 11​:30​:29 2013, shay wrote​:

I think a program to ensure that each module that has been upgraded
since the previous release is listed in perldelta would be useful. (At
the moment (as RM for this month and last) I keep an eye on commits each
day, and also manually diff the previous release with the current
release-to-be just before release to check that nothing has been missed.)

However, there is a limit to what can be automated. Certainly the
"L<Module> has been upgraded from version X to Y." item could be added
if missing, but writing the detail which follows that line, describing
what has been changed, is really a manual process. Not every module has
a Changes file that could be scanned for a list of changes, and even
where such a file exists I do not deem it appropriate to list every
single change in the perldelta file​: that would be too much duplication.
Instead, I try to summarize what the major changes are, and sometimes
refer the reader to the Changes file for more detail if necessary. This
summarizing process will always be a manual task since it requires human
judgement.

The program should be tolerant of upgrade entries which already exist
(i.e. it must not go adding duplicate sections for a given module)
because I personally would still be inclined to perform this process
incrementally through the month to avoid having the whole of that
necessarily manual part still to do at release time.

So really I think the program just needs to add

=item \*

L\<Module> has been upgraded from version X to Y\.

XXX TODO

for each upgraded Module that isn't already listed.

Take care over the name of the Module. The directory names in cpan/ and
dist/ are mostly (if not all?) the names of CPAN *distributions* rather
than *modules*, but perldelta generally lists a single "key" module name
for each one since this is more meaningful to users of the perl core,
who may not even be aware of the dual-lived nature of some modules.
Having said that, if a distribution contains very many modules that have
been upgraded then it might be better to refer to a collection of
modules by their distribution name instead. (Thus, if Scalar-List-Utils
is upgraded, then it is probably best to mention each module
(Scalar​::Util and List​::Util) separately, especially if only one has
been changed. But if IO-Compress is upgraded and lots of modules in it
have been changed, perhaps with similar changes in each file, then it
would be excessive to list them all separately.)

Thank you for your interest in helping to automate parts of the release
process! :-)

@p5pRT
Copy link
Author

p5pRT commented Oct 6, 2013

From abiviq@hushmail.com

0006-Porting-corelist-perldelta.pl-Cleanup-DeltaParser.patch
From 4b8f1631af41a7887bf8c783155dc1e448a902e4 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Thu, 3 Oct 2013 16:48:34 -0400
Subject: [PATCH 6/9] Porting/corelist-perldelta.pl - Cleanup DeltaParser

* Comment generation of accessor methods
* Make _parse_delta() more explicit by calling the section parsing
  methods explicitly
* Ensure that the new_modules, updated_modules, and removed_modules
  attributes exist even if the perldelta file does not contain the
  corresponding sections
---
 Porting/corelist-perldelta.pl |   43 +++++++++++++++++++----------------------
 1 file changed, 20 insertions(+), 23 deletions(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 6b8ebba..19ab0d3 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -224,6 +224,10 @@ sub do_check {
     return $self;
   }
 
+  # creates the accessor methods:
+  #   new_modules
+  #   updated_modules
+  #   removed_modules
   for my $k (keys %sections) {
     no strict 'refs';
     my $m = "${k}_modules";
@@ -233,29 +237,17 @@ sub do_check {
   sub _parse_delta {
     my ($self, $pod) = @_;
 
-    map {
-        my ($t, $s) = @{ $_ };
-        
-        # Keep the section title if it has one:
-        if( $s->[0]->[0] eq 'head2' ) {
-          #warn "Keeping section title '$s->[0]->[2]'";
-          $titles{ $t } = $s->[0]->[2]
-              if $s->[0]->[2];
-        };
-
-        $self->${\"_parse_${t}_section"}($s)
-    } map {
-        my $s = $self->_look_for_section($pod => $sections{$_})
-            or die "failed to parse $_ section";
-        [$_, $s];
-    } keys %sections;
-
-    for my $s (keys %sections) {
-      my $m = "${s}_modules";
-
-      $self->{$m} = [sort {
-        lc $a->[0] cmp lc $b->[0]
-      } @{ $self->{$m} }];
+    my $new_section     = $self->_look_for_section( $pod, $sections{new} );
+    my $updated_section = $self->_look_for_section( $pod, $sections{updated} );
+    my $removed_section = $self->_look_for_section( $pod, $sections{removed} );
+
+    $self->_parse_new_section($new_section);
+    $self->_parse_updated_section($updated_section);
+    $self->_parse_removed_section($removed_section);
+
+    for (qw/new_modules updated_modules removed_modules/) {
+      $self->{$_} =
+        [ sort { lc $a->[0] cmp lc $b->[0] } @{ $self->{$_} } ];
     }
 
     return;
@@ -264,6 +256,8 @@ sub do_check {
   sub _parse_new_section {
     my ($self, $section) = @_;
 
+    $self->{new_modules} = [];
+    return unless $section;
     $self->{new_modules} = $self->_parse_section($section => sub {
       my ($el) = @_;
 
@@ -310,6 +304,9 @@ sub do_check {
 
   sub _parse_removed_section {
     my ($self, $section) = @_;
+
+    $self->{removed_modules} = [];
+    return unless $section;
     $self->{removed_modules} = $self->_parse_section($section => sub {
       my ($el) = @_;
 
-- 
1.7.10.4

@p5pRT
Copy link
Author

p5pRT commented Oct 6, 2013

From abiviq@hushmail.com

0001-Porting-corelist-perldelta.pl-Use-Module-CoreList-in.patch
From 4bfe4129f84aec05e0d4c53e06690aed5992b948 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Thu, 3 Oct 2013 16:32:42 -0400
Subject: [PATCH 1/9] Porting/corelist-perldelta.pl - Use Module::CoreList in
 dist/

Ensure that the latest version of Module::CoreList is used when
running the script
---
 Porting/corelist-perldelta.pl |    1 +
 1 file changed, 1 insertion(+)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index e6fb582..2947923 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -4,6 +4,7 @@ use strict;
 use warnings;
 use lib 'Porting';
 use Maintainers qw/%Modules/;
+use lib 'dist/Module-CoreList/lib';
 use Module::CoreList;
 use Getopt::Long;
 
-- 
1.7.10.4

@p5pRT
Copy link
Author

p5pRT commented Oct 6, 2013

From abiviq@hushmail.com

0003-Porting-corelist-perldelta.pl-Remove-trailing-whites.patch
From a85ccb51b18397662da5943d3eafd28a302c58d4 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Thu, 3 Oct 2013 16:36:26 -0400
Subject: [PATCH 3/9] Porting/corelist-perldelta.pl - Remove trailing
 whitespace

---
 Porting/corelist-perldelta.pl |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 022b427..b22c75a 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -26,7 +26,7 @@ The part to check the diff wants to be run with a Perl that has an up-to-date
 L<Module::CoreList>, but needs the outside L<Algorithm::Diff>.
 
 Ideally, the program will be split into two separate programs, one
-to generate the text and one to show the diff between the 
+to generate the text and one to show the diff between the
 corelist sections of the last perldelta and the next perldelta.
 
 =cut
@@ -350,7 +350,7 @@ sub do_check {
       sub {
         my ($el) = @_;
         my ($heading) = $el->[0] =~ /^head(\d)$/;
-        my $f = $heading && $el->[2] =~ /^$section/;        
+        my $f = $heading && $el->[2] =~ /^$section/;
         $level = $heading if $f && !$level;
         return $f;
       },
-- 
1.7.10.4

@p5pRT
Copy link
Author

p5pRT commented Oct 6, 2013

From abiviq@hushmail.com

0009-Porting-corelist-perldelta.pl-Add-a-new-mode-update.patch
From 5ec4e7daddbb412e7110c520b9063bf2c0bb5a02 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Sun, 6 Oct 2013 10:58:27 -0400
Subject: [PATCH 9/9] Porting/corelist-perldelta.pl - Add a new mode update

This adds a new mode, update, which given an the path to an existing
perldelta file, will add missing entries and update incorrect version
information.

This commit introduces a new module, DeltaTransformer, which is used
for pod manipulation. This module introduces a dependency on
the CPAN distribution Pod::Elemental.
---
 Porting/corelist-perldelta.pl |  415 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 413 insertions(+), 2 deletions(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 23071a5..f90fe12 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -7,12 +7,16 @@ use Maintainers qw/%Modules/;
 use lib 'dist/Module-CoreList/lib';
 use Module::CoreList;
 use Getopt::Long;
+use List::Util 'first';
 
 =head1 USAGE
 
   # generate the module changes for the Perl you are currently building
   ./perl -Ilib Porting/corelist-perldelta.pl
-  
+
+  # update the module changes for the Perl you are currently building
+  perl Porting/corelist-perldelta.pl --mode=update Porting/perldelta.pod
+
   # generate a diff between the corelist sections of two perldelta* files:
   perl Porting/corelist-perldelta.pl --mode=check 5.017001 5.017002 <perl5172delta.pod
 
@@ -91,7 +95,7 @@ sub run {
   my %opt = (mode => 'generate');
 
   GetOptions(\%opt,
-    'mode|m:s', # 'generate', 'check'
+    'mode|m:s', # 'generate', 'check', 'update'
   );
 
   # by default, compare latest two version in CoreList;
@@ -106,6 +110,9 @@ sub run {
   elsif ( $opt{mode} eq 'check' ) {
     do_check(\*ARGV, $old => $new);
   }
+  elsif ( $opt{mode} eq 'update' ) {
+    do_update_existing(shift @ARGV, $old => $new);
+  }
   else {
     die "Unrecognized mode '$opt{mode}'\n";
   }
@@ -284,6 +291,41 @@ sub corelist_delta {
   );
 }
 
+# currently does not update the Removed Module section
+sub do_update_existing {
+  my ( $existing, $old, $new ) = @_;
+
+  my $document = DeltaTransformer::transform_pod($existing);
+
+  my $topSectionIndex = first { DeltaTransformer::make_selector( -command => 'head1', -content => qr/^Modules and Pragmata$/)->($document->children->[$_]) } 0..$#{ $document->children };
+  die 'No existing Modules and Pragmata section, please create and run again' unless $topSectionIndex != -1;
+  my $topSection = $document->children->[$topSectionIndex];
+
+  my ( $added, $removed, $updated, $manuallyCheck ) = corelist_delta( $old => $new );
+  if ($manuallyCheck) {
+    say "Please check whether the following distributions have been modified and list accordingly";
+    say "\t* $_" for sort @{$manuallyCheck};
+  }
+
+  my $data = {
+    new => $added,
+    updated => $updated,
+    #removed => $removed, ignore removed for now
+  };
+
+  for my $title ( keys %sections ) {
+    my $selector = DeltaTransformer::get_head_selector($title);
+    my $section = ( grep { $selector->($_) } @{ $topSection->children } )[0];
+    DeltaTransformer::update_section($section, $data, $title);
+    DeltaTransformer::add_to_section($section, $data, $title);
+    DeltaTransformer::sort_section($section);
+  }
+  DeltaTransformer::remove_empty_sections($topSection);
+  open my $out, '>', $existing;
+  print $out $document->as_pod_string;
+  close $out;
+}
+
 sub do_generate {
   my ($old, $new) = @_;
   my ($added, $removed, $updated, $manuallyCheck) = corelist_delta($old => $new);
@@ -340,6 +382,375 @@ sub do_check {
 }
 
 {
+  package DeltaTransformer;
+  use subs qw(s_command s_flat);
+  use List::Util 'first';
+
+  sub init {
+    require Pod::Elemental;
+    require Pod::Elemental::Transformer::Pod5;
+    require Pod::Elemental::Transformer::Nester;
+    require Pod::Elemental::Element::Nested;
+    require Pod::Elemental::Element::Pod5::Command;
+    require Pod::Elemental::Element::Pod5::Ordinary;
+    require Pod::Elemental::Selectors;
+
+    no warnings 'once';
+
+    *DeltaTransformer::s_command = *Pod::Elemental::Selectors::s_command;
+    *DeltaTransformer::s_flat = *Pod::Elemental::Selectors::s_flat;
+  }
+
+  sub make_selector {
+    my %params = @_;
+    my $ret = sub {
+      return s_command($params{'-command'}, $_[0]) && $_[0]->content =~ /$params{'-content'}/;
+    };
+    return $ret;
+  }
+
+
+  sub get_head_selector {
+    my $title = shift;
+    return make_selector(
+      -command => 'head2',
+      -content => $sections{$title},
+    );
+  }
+
+
+  sub nest_section {
+    my($document, $selector) = @_;
+    my @documentChildren     = @{ $document->children };
+    my $sectionIndex         = first { $selector->($documentChildren[$_]) } 0..$#documentChildren;
+    my @openNodes            = ($documentChildren[$sectionIndex]);
+
+    my @children = @{$openNodes[0]->children};
+    $openNodes[0]->children([]);
+
+    for my $node (@children){
+      my $currentNode = $openNodes[-1];
+
+      if(s_command('item',$currentNode)){
+        if(s_command('item', $node)){
+          pop @openNodes;
+          $openNodes[-1]->children([@{$openNodes[-1]->children}, $currentNode]);
+          push @openNodes, Pod::Elemental::Element::Nested->new( command => 'item',  content => $node->content );
+          next;
+        }
+        elsif(s_command('over', $node)) {
+          push @openNodes, Pod::Elemental::Element::Nested->new( command => 'over',  content => $node->content );
+          next;
+        }
+        elsif(s_command('back', $node)) {
+          die 'unexpected back, not currently in over' if $openNodes[-2]->command ne 'over';
+          pop @openNodes;
+          my $over = pop @openNodes;
+          $over->children([@{$over->children}, $currentNode]);
+          $openNodes[-1]->children([@{$openNodes[-1]->children}, $over,$node]);
+          next;
+        }
+        elsif(s_flat($node)) {
+          $openNodes[-1]->children([@{$openNodes[-1]->children}, $node]);
+          next;
+        }
+        else {
+          die 'undexpeced element in item: ' . $node->as_debug_string;
+        }
+      }
+
+      elsif(s_command('over',$currentNode)){
+        if(s_command('item', $node)){
+          push @openNodes, Pod::Elemental::Element::Nested->new( command => 'item',  content => $node->content );
+          next;
+        }
+        elsif(s_command('over', $node)) {
+          push @openNodes, Pod::Elemental::Element::Nested->new( command => 'over',  content => $node->content );
+          next;
+        }
+        elsif(s_command('back', $node)) {
+          pop @openNodes;
+          $openNodes[-1]->children([@{$openNodes[-1]->children}, $currentNode, $node]);
+          next;
+        }
+        elsif(s_flat($node)) {
+          $openNodes[-1]->children([@{$openNodes[-1]->children}, $node]);
+          next;
+        }
+        else {
+          die 'undexpeced element in over: ' . $node->as_debug_string;
+        }
+      }
+
+      elsif(s_command('head2', $currentNode)) {
+        if(s_command('item', $node)){
+          push @openNodes, Pod::Elemental::Element::Nested->new( command => 'item',  content => $node->content );
+          next;
+        }
+        elsif(s_command('over', $node)) {
+          push @openNodes, Pod::Elemental::Element::Nested->new( command => 'over',  content => $node->content );
+          next;
+        }
+        elsif(s_command('back', $node)) {
+          die 'unexpected back in head';
+        }
+        elsif(s_flat($node)) {
+          $openNodes[-1]->children([@{$openNodes[-1]->children}, $node]);
+          next;
+        }
+        else {
+          die 'undexpeced element in head2: ' . $node->as_debug_string;
+        }
+      }
+
+      else {
+        die 'unexpected element ' . $node->as_debug_string;
+      }
+    }
+
+    $documentChildren[$sectionIndex ] = $openNodes[0];
+    $document->children( \@documentChildren );
+    return $document;
+  }
+
+  # currently just ensures that there is only one over region in each section
+  sub validate_section {
+    my ($node, $title, $selector) = @_;
+    my $section = ( grep { $selector->($_) } @{ $node->children } )[0];
+
+    my @overRegion = grep { s_command( 'over', $_ ) } @{ $section->children };
+    die "$title should have only one over region\n" . $section->as_debug_string
+      unless @overRegion and scalar @overRegion == 1;
+  }
+
+  # transforms the node by
+  # * applying Pod::Elemental::Transformer::Pod5
+  # * create the new, updated and removed modules sections if necessary
+  # * nest the nodes in the sections appropriately
+  # * ensure that the new, updated, and removed sections occur in this order under the top module changes section
+  sub transform_pod {
+    init;
+    my $pod      = shift;
+    my $document = Pod::Elemental->read_file($pod);
+
+    Pod::Elemental::Transformer::Pod5->new->transform_node($document);
+
+    my $topSectionSelector = make_selector( -command => 'head1', -content => qr/^Modules and Pragmata$/);
+
+    # create the Module subsections if necessary
+    # and nest over and back sections under them
+    for my $title (keys %sections) {
+      my $selector = get_head_selector($title);
+      my @section = grep { $selector->($_) } @{ $document->children };
+
+      if (not @section) {
+        my $section = Pod::Elemental::Element::Nested->new( command => 'head2', content => $titles{$title} );
+        my $over    = Pod::Elemental::Element::Nested->new( command => 'over',  content => '4' );
+        my $back    = Pod::Elemental::Element::Pod5::Command->new( command => 'back', content => '' );
+        $section->children([ $over, $back ]);
+
+        # place section as first child of the top Module Changes section
+        my @documentChildren    = @{ $document->children };
+        my $topSectionIndex = first { $topSectionSelector->($documentChildren[$_]) } 0..$#documentChildren;
+        splice @documentChildren, $topSectionIndex + 1, 0, $section;
+        $document->children( \@documentChildren );
+      }
+      else {
+        die "More than one $title section" if $#section > 0;
+        my $nester = Pod::Elemental::Transformer::Nester->new({
+          top_selector      => $selector,
+          content_selectors => [ s_command( [qw(over back item)] ), s_flat, ],
+        });
+        $nester->transform_node($document);
+        nest_section($document, $selector);
+      }
+
+      validate_section( $document, $title, $selector );
+    }
+
+    my @sectionSelectors;
+    for ( keys %sections ) {
+      push @sectionSelectors, get_head_selector($_);
+    }
+
+    my $sectionNester = Pod::Elemental::Transformer::Nester->new({
+      top_selector      => $topSectionSelector,
+      content_selectors => [ s_command( [qw(over back head2 item)] ), s_flat ]
+    });
+    # nest the Module subsections under the top Module section
+    $sectionNester->transform_node($document);
+
+    # sorts the the Module subsections into the desired
+    # new, updated, removed order
+    my $topSection = ( grep { $topSectionSelector->($_) } @{ $document->children } )[0];
+    my @nodeChildren = @{ $topSection->children };
+    my %subsection;
+    my $minIndex;
+
+    # first find the position of each subsection and keep track of the index of the subsection that comes first
+    for my $selector (@sectionSelectors) {
+      my $index = first { $selector->($nodeChildren[$_]) } 0..$#nodeChildren;
+      die "Could not find subsection nested under section, ensure the document only contains the new, updated, and removed =head2 sections under the main section\n".$document->as_debug_string if !defined $index or $index == -1;
+      my $section = $nodeChildren[$index];
+      $subsection{ $section->content } = [ $index, $section, $selector ];
+      $minIndex //= $index;
+      $minIndex = $index if $index < $minIndex;
+    }
+
+    # then remove the subsections that are not first
+    while ( my ( $section, $data ) = each %subsection ) {
+      my $index    = $data->[0];
+      my $selector = $data->[2];
+      next if $index == $minIndex;
+
+      $index = first { $selector->($nodeChildren[$_]) } 0..$#nodeChildren;
+      splice @nodeChildren, $index, 1;
+    }
+
+    # splice the subsections at the index of the subsection that comes first so they end up in the right order
+    # doing it this way ensures that ordering of other nodes are not disturbed
+    splice @nodeChildren, $minIndex, 1, $subsection{ $titles{new} }->[1], $subsection{ $titles{updated} }->[1], $subsection{ $titles{removed} }->[1];
+    $topSection->children( \@nodeChildren );
+
+    return $document;
+  }
+
+  # remove the new, updated, and removed sections if they contain no items
+  sub remove_empty_sections {
+    my ($topSection) = @_;
+    my @sections = @{ $topSection->children };
+    for my $title ( keys %sections ) {
+      my $selector = get_head_selector($title);
+
+      my $sectionIndex = first { $selector->($sections[$_]) } 0..$#sections;
+      my $over         = ( grep { s_command( 'over', $_) } @{$sections[$sectionIndex]->children} )[0];
+      my @items        = grep { s_command( 'item', $_ ) } @{ $over->children };
+
+      # no items means that this section doesn't list modules
+      if ( scalar @items == 0 ) {
+        splice @sections, $sectionIndex, 1;
+      }
+    }
+    $topSection->children( \@sections );
+  }
+
+  # sort the modules and pragmata in the section
+  sub sort_section {
+    my ($section) = @_;
+    my $over = ( grep { s_command( 'over', $_) } @{$section->children} )[0];
+
+    # if we can't parse the module name, it will be uninitalized
+    # in sort. This is not a problem as it will just result in these
+    # sections being placed near the begining of the section
+    no warnings 'uninitialized';
+    $over->children(
+      [
+        map { $_->[0] } sort { lc $a->[1] cmp lc $b->[1] } map {
+          my $item   = $_;
+          my $text   = $item->children->[0];
+          my $module = '';
+          ($module)  = $text->content =~ /^(?:L|C)<(.+?)>/ if $text;
+          [ $item, $module ];
+        } @{ $over->children }
+      ]
+    );
+  }
+
+  # add modules and pragmata in $data to the section
+  sub add_to_section {
+    my ($section, $data, $title) = @_;
+    my $over = ( grep { s_command( 'over', $_) } @{$section->children} )[0];
+
+    #undef is a valid version name in Module::CoreList so supress warnings about concatenating undef values
+    no warnings 'uninitialized';
+    for ( values %{ $data->{$title} } ) {
+      my ($mod, $old_v, $new_v)  = @{$_};
+      my ($item, $text);
+
+      if ($title eq 'new') {
+        $item = Pod::Elemental::Element::Nested->new( command => 'item', content => '*' );
+        $text = Pod::Elemental::Element::Pod5::Ordinary->new( content => "L<$mod> $new_v has been added to the Perl core." );
+      }
+
+      elsif ($title eq 'updated') {
+        $item = Pod::Elemental::Element::Nested->new( command => 'item', content => '*' );
+        my $content = "L<$mod> has been upgraded from version $old_v to $new_v.\n";
+        if($deprecated->{$mod}) {
+          $content .= "NOTE: L<$mod> is deprecated and may be removed from a future version of Perl.";
+        }
+        $text = Pod::Elemental::Element::Pod5::Ordinary->new( content => $content );
+      }
+
+      elsif ($title eq 'removed') {
+          $item = Pod::Elemental::Element::Nested->new( command => 'item', content => '*' );
+          $text = Pod::Elemental::Element::Pod5::Ordinary->new( content => "C<$mod> has been removed from the Perl core.  Prior version was $old_v." );
+      }
+
+      $item->children( [$text] );
+      $over->children( [@{$over->children}, $item] );
+    }
+  }
+
+  # verify the module and pragmata in the section, changing the stated version if necessary
+  # this subroutine warns if the module name cannot be parsed or if it is not listed in
+  # the results returned from corelist_delta()
+  sub update_section {
+    my ($section, $data, $title) = @_;
+    my $over = ( grep { s_command( 'over', $_) } @{$section->children} )[0];
+    my @items = grep { s_command( 'item', $_ ) } @{ $over->children };
+
+    # verify existing items, correcting errors as necessary
+    # XXXthe regex for parsing the version have been copied from DeltaParser,
+    for my $item (@items) {
+      next unless $item->content eq '*';
+      next unless defined $item->children->[0] and $item->children->[0]->isa('Pod::Elemental::Element::Pod5::Ordinary');
+
+      my $content = $item->children->[0]->content;
+      my ($module) = $content =~ /^(?:L|C)<(.+?)>/;
+
+      say "Could not parse module name; line is:\n\t$content" and next unless $module;
+      say "$module is not in Module::CoreList; check to see that it is not covered by another section" and next
+        unless $data->{$title}{$module};
+
+      if ( $title eq 'new' ) {
+        my ($new) = $content =~ /(\d[^\s]+)\s+has\s+been.*$/;
+        say "Could not parse new version for $module; line is:\n\t$content" and next unless $new;
+        if ( $data->{$title}{$module}[2] ne $new ) {
+          say "$module: new version differs; version in pod: $new; version in corelist: " . $data->{$title}{$module}[2];
+        }
+        $content =~ s/\d[^\s]+(\s+has\s+been.*$)/$data->{$title}{$module}[2].$1/e;
+      }
+
+      elsif ( $title eq 'updated' ) {
+        my ( $prev, $new ) = $content =~ /from\s+(?:version\s+)?(\d[^\s]+)\s+to\s+(?:version\s+)?(\d[^\s,]+?)(?=[\s,]|\.\s|\.$|$).*/s;
+        say "Could not parse old and new version for $module; line is:\n\t$content" and next
+          unless $prev and $new;
+        if ( $data->{$title}{$module}[1] ne $prev ) {
+          say "$module: previous version differs; version in pod: $prev; version in corelist: " . $data->{$title}{$module}[1];
+        }
+        if ( $data->{$title}{$module}[2] ne $new ) {
+          say "$module: new version differs; version in pod: $new; version in corelist: " . $data->{$title}{$module}[2];
+        }
+        $content =~
+          s/(from\s+(?:version\s+)?)\d[^\s]+(\s+to\s+(?:version\s+)?)\d[^\s,]+?(?=[\s,]|\.\s|\.$|$)(.*)/$1.$data->{$title}{$module}[1].$2.$data->{$title}{$module}[2].$3/se;
+      }
+
+      elsif ( $title eq 'removed' ) {
+        my ($prev) = $content =~ /^.*?was\s+(\d[^\s]+?)/;
+        say "Could not parse old version for $module; line is:\n\t$content" and next unless $prev;
+        if ( $data->{$title}{$module}[1] ne $prev ) {
+          say "$module: previous version differs; $prev " . $data->{$title}{$module}[1];
+        }
+        $content =~ s/(^.*?was\s+)\d[^\s]+?/$1.$data->{$title}{$module}[1]/e;
+      }
+
+      delete $data->{$title}{$module};
+      $item->children->[0]->content($content);
+    }
+  }
+}
+
+{
   package DeltaParser;
   use Pod::Simple::SimpleTree;
 
-- 
1.7.10.4

@p5pRT
Copy link
Author

p5pRT commented Oct 6, 2013

From abiviq@hushmail.com

0004-Porting-corelist.pl-DeltaParser-match-modules-in-L-a.patch
From e90b98ab520310d17dca9e5af4c39e10206a2e2a Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Thu, 3 Oct 2013 16:40:13 -0400
Subject: [PATCH 4/9] Porting/corelist.pl - DeltaParser: match modules in L<>
 and C<>

Module names have been listed in both
---
 Porting/corelist-perldelta.pl |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index b22c75a..cf34bdd 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -317,7 +317,7 @@ sub do_check {
       my ($el) = @_;
       return unless ref $el && $el->[0] =~ /^item-/
           && @{ $el } > 2 && ref $el->[2];
-      return unless $el->[2]->[0] eq 'C';
+      return unless $el->[2]->[0] =~ /C|L/;
 
       return 1;
     });
-- 
1.7.10.4

@p5pRT
Copy link
Author

p5pRT commented Oct 6, 2013

From abiviq@hushmail.com

0005-Porting-corelist-perldelta.pl-Improve-version-parsin.patch
From bc3bde3f0f83d380a8f793ee0eae199a3d94e1a7 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Thu, 3 Oct 2013 16:44:38 -0400
Subject: [PATCH 5/9] Porting/corelist-perldelta.pl - Improve version parsing

Catch more forms of how updated modules have been listed in past
perldelta
---
 Porting/corelist-perldelta.pl |   14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index cf34bdd..6b8ebba 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -279,13 +279,25 @@ sub do_check {
   sub _parse_updated_section {
     my ($self, $section) = @_;
 
+    $self->{updated_modules} = [];
+    return unless $section;
     $self->{updated_modules} = $self->_parse_section($section => sub {
       my ($el) = @_;
 
       my ($first, $second) = @{ $el }[2, 3];
       my $module = $first->[2];
+
+      # the regular expression matches the following:
+      #   from VERSION_NUMBER to VERSION_NUMBER
+      #   from VERSION_NUMBER to VERSION_NUMBER.
+      #   from version VERSION_NUMBER to version VERSION_NUMBER.
+      #   from VERSION_NUMBER to VERSION_NUMBER and MODULE from VERSION_NUMBER to VERSION_NUMBER
+      #   from VERSION_NUMBER to VERSION_NUMBER, and MODULE from VERSION_NUMBER to VERSION_NUMBER
+      #
+      # some perldelta contain more than one module listed in an entry, this only attempts to match the
+      # first module
       my ($old, $new) = $second =~
-          /from\s+(?:version\s+)?(\d[^\s]+)\s+to\s+(\d[^\s]+?)\.?$/;
+          /from\s+(?:version\s+)?(\d[^\s]+)\s+to\s+(?:version\s+)?(\d[^\s,]+?)(?=[\s,]|\.\s|\.$|$).*/s;
 
       warn "Unable to extract old or new version of $module from perldelta"
         if !defined $old || !defined $new;
-- 
1.7.10.4

@p5pRT
Copy link
Author

p5pRT commented Oct 6, 2013

From abiviq@hushmail.com

0007-Porting-corelist-perldelta.pl-Improve-corelist_delta.patch
From b1c1e49debda01b26675c59a4b0f4da4647e104a Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Thu, 3 Oct 2013 17:01:54 -0400
Subject: [PATCH 7/9] Porting/corelist-perldelta.pl - Improve corelist_delta

corelist_delta now goes through almost all of the core distributions.
The problem with the previous approach was that the keys of %Modules in
Porting/Maintainers.pl do not all correspond to a valid distribution or
do not correspond to a module as listed in Module::CoreList.
This commit also updates the callers of the function.
---
 Porting/corelist-perldelta.pl |  191 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 161 insertions(+), 30 deletions(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 19ab0d3..303a3cb 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -113,35 +113,156 @@ sub run {
   exit 0;
 }
 
+# Given two perl versions, it returns a list describing the core distributions that have changed.
+# The first three elements are hashrefs corresponding to new, updated, and removed modules
+# and are of the form (mostly, see the special remarks about removed):
+#   'Distribution Name' => ['Distribution Name', previous version number, current version number]
+# where the version number is undef if the distribution did not exist
+# the fourth element is an arrayref of core distribution names of those distribution for which it is
+# unknown whether they have changed and therefore need to be manually checked.
+#
+# A simple implementation would be to iterate over the keys of the %Modules hash in Porting/Maintainer.pl
+# checking to see if the distribution is listed in the hash returned Module::CoreList::changes_between,
+# and then adding the distribution to the appropriate hashref to be returned.
+#
+# The problem with this approach is that the %Modules hash is keyed by distribution but Module::CoreList
+# only contains information about modules. In most cases, the distribution name corresponds to the module
+# that is representative of the distribution, so this discrepency between distribution and module does not
+# matter. However, there are a few distribution names that do not correspond to a module. In this case a
+# hash, %distToModules, has been created which maps the distribution name to a representative module.
+# The representative module was chosen by either looking at the Makefile of the distribution or
+# by seeing which module the distribution has been traditionally listed under in past perldelta.
+#
+# There are a few distributions for which there is no single representative module (e.g. libnet). These distributions
+# are returned as the last element of the list.
+#
+# There are also a few distribution in %Modules which do not contain any modules. These mostly contain platform-specific code
+# and docs. These can be ignored by seeing where in the filesystem they are located.
+#
+# %Modules contains a final key, _PERLLIB, which contains a list of modules that have no owner. This list contains
+# modules and pragmata that may also be present in Module::CoreList. A list of modules are in the array @unclaimedModules,
+# which were manually listed based on whether they were independent modules and whether they have been listed in past
+# perldelta. The pragmata were found by doing something like
+#   say for sort grep { $_ eq lc $_ and !exists $Modules{$_}} keys %{$Module::CoreList::version{'5.019003'}}
+# and manually filtering out pragamata that were already covered (e.g. warnings::register is covered under warnings)
+#
+# It is currently not possible to differentiate between a removed module and a removed distribution. Therefore,
+# the removed hashref contains every module that has been removed, even if the module's corresponding distribution
+# has not been removed.
+
 sub corelist_delta {
   my ($old, $new) = @_;
   my $corelist = \%Module::CoreList::version;
-
+  my %changes = Module::CoreList::changes_between( $old, $new );
   $deprecated = $Module::CoreList::deprecated{$new};
 
-  my (@new,@deprecated,@removed,@pragmas,@modules);
+  my $getModifyType = sub {
+    my $data = shift;
+    if ( exists $data->{left} and exists $data->{right} ) {
+      return 'updated';
+    }
+    elsif ( !exists $data->{left} and exists $data->{right} ) {
+      return 'new';
+    }
+    elsif ( exists $data->{left} and !exists $data->{right} ) {
+      return 'removed';
+    }
+    return undef;
+  };
+
+  my @unclaimedModules = qw/Config::Extensions DB ExtUtils::Embed ExtUtils::Miniperl ExtUtils::Typemaps ExtUtils::XSSymSet SelectSaver Symbol Thread Tie::Array Tie::Handle Tie::Scalar Tie::StdHandle Tie::SubstrHash Time::gmtime Time::localtime Time::tm UNIVERSAL User::grent User::pwent/;
+  my @unclaimedPragmata = qw/_charnames arybase blib bytes charnames deprecate feature filetest integer less locale open overloading sort strict subs utf8 vars vmsish/;
+  my @unclaimed = (@unclaimedModules, @unclaimedPragmata);
+
+  my %distToModules = (
+    'IO-Compress' => [
+      {
+        'name' => 'IO-Compress',
+        'modification' => $getModifyType->( $changes{'IO::Compress::Base'} ),
+        'data' => $changes{'IO::Compress::Base'}
+      }
+    ],
+    'Locale-Codes' => [
+      {
+        'name'         => 'Locale::Codes',
+        'modification' => $getModifyType->( $changes{'Locale::Codes'} ),
+        'data'         => $changes{'Locale::Codes'}
+      }
+    ],
+    'PathTools' => [
+      {
+        'name'         => 'File::Spec',
+        'modification' => $getModifyType->( $changes{'Cwd'} ),
+        'data'         => $changes{'Cwd'}
+      }
+    ],
+    'Scalar-List-Utils' => [
+      {
+        'name'         => 'List::Util',
+        'modification' => $getModifyType->( $changes{'List::Util'} ),
+        'data'         => $changes{'List::Util'}
+      },
+      {
+        'name'         => 'Scalar::Util',
+        'modification' => $getModifyType->( $changes{'Scalar::Util'} ),
+        'data'         => $changes{'Scalar::Util'}
+      }
+    ],
+    'Text-Tabs+Wrap' => [
+      {
+        'name'         => 'Text::Tabs',
+        'modification' => $getModifyType->( $changes{'Text::Tabs'} ),
+        'data'         => $changes{'Text::Tabs'}
+      },
+      {
+        'name'         => 'Text::Wrap',
+        'modification' => $getModifyType->( $changes{'Text::Wrap'} ),
+        'data'         => $changes{'Text::Wrap'}
+      }
+    ],
+  );
+
+  # structure is (new|removed|updated) => [ [ModuleName, previousVersion, newVersion] ]
+  my $deltaGrouping = {};
+
+  # list of distributions listed in %Modules that need to be manually checked because there is no module that represents it
+  my @manuallyCheck;
 
   # %Modules defines what is currently in core
   for my $k ( keys %Modules ) {
-    next unless exists $corelist->{$new}{$k};
-    my $old_ver = $corelist->{$old}{$k};
-    my $new_ver = $corelist->{$new}{$k};
-    # in core but not in last corelist
-    if ( ! exists $corelist->{$old}{$k} ) {
-      push @new, [$k, undef, $new_ver];
+    next if $k eq '_PERLLIB'; #these are taken care of by being listed in @unclaimed
+    next if Module::CoreList::is_core($k) and !exists $changes{$k}; #modules that have not changed
+
+    my ( $distName, $modifyType, $data );
+
+    if ( exists $changes{$k} ) {
+      $distName   = $k;
+      $modifyType = $getModifyType->( $changes{$k} );
+      $data       = $changes{$k};
     }
-    # otherwise just pragmas or modules
-    else {
-      my $old_ver = $corelist->{$old}{$k};
-      my $new_ver = $corelist->{$new}{$k};
-      next unless defined $old_ver && defined $new_ver && $old_ver ne $new_ver;
-      my $tuple = [ $k, $old_ver, $new_ver ];
-      if ( $k eq lc $k ) {
-        push @pragmas, $tuple;
-      }
-      else {
-        push @modules, $tuple;
+    elsif ( exists $distToModules{$k} ) {
+      # modification will be undef if the distribution has not changed
+      my @modules = grep { $_->{modification} } @{ $distToModules{$k} };
+      for (@modules) {
+        $deltaGrouping->{ $_->{modification} }->{ $_->{name} } = [ $_->{name}, $_->{data}->{left}, $_->{data}->{right} ];
       }
+      next;
+    }
+    else {
+      # "modules" that are not listed in Module::CoreList
+      # mostly platform-specific code and docs
+      next unless grep { /^cpan|dist|lib|ext/ } split '\s', $Modules{$k}->{FILES};
+
+      push @manuallyCheck, $k and next;
+    }
+
+    $deltaGrouping->{$modifyType}->{$distName} = [ $distName, $data->{left}, $data->{right} ];
+  }
+
+  for my $k (@unclaimed) {
+    if ( exists $changes{$k} ) {
+      $deltaGrouping->{ $getModifyType->( $changes{$k} ) }->{$k} =
+        [ $k, $changes{$k}->{left}, $changes{$k}->{right} ];
     }
   }
 
@@ -151,33 +272,43 @@ sub corelist_delta {
   # important. That's the best we can do without a historical Maintainers.pl
   for my $k ( keys %{ $corelist->{$old} } ) {
     if ( ! exists $corelist->{$new}{$k} ) {
-      push @removed, [$k, $corelist->{$old}{$k}, undef];
+      $deltaGrouping->{'removed'}->{$k} = [ $k, $corelist->{$old}{$k}, undef ];
     }
   }
 
-  return (\@new, \@removed, \@pragmas, \@modules);
+  return (
+    \%{ $deltaGrouping->{'new'} },
+    \%{ $deltaGrouping->{'removed'} },
+    \%{ $deltaGrouping->{'updated'} },
+    \@manuallyCheck
+  );
 }
 
 sub do_generate {
   my ($old, $new) = @_;
-  my ($added, $removed, $pragmas, $modules) = corelist_delta($old => $new);
+  my ($added, $removed, $updated, $manuallyCheck) = corelist_delta($old => $new);
+
+  if ($manuallyCheck) {
+    say "\nXXXPlease check whether the following distributions have been modified and list accordingly";
+    say "\t$_" for @{$manuallyCheck};
+  }
 
-  generate_section($titles{new}, \&added, @{ $added });
-  generate_section($titles{updated}, \&updated, @{ $pragmas }, @{ $modules });
-  generate_section($titles{removed}, \&removed, @{ $removed });
+  generate_section( $titles{new},     \&added,   values %{$added} );
+  generate_section( $titles{updated}, \&updated, values %{$updated} );
+  generate_section( $titles{removed}, \&removed, values %{$removed} );
 }
 
 sub do_check {
   my ($in, $old, $new) = @_;
 
   my $delta = DeltaParser->new($in);
-  my ($added, $removed, $pragmas, $modules) = corelist_delta($old => $new);
+  my ($added, $removed, $updated) = corelist_delta($old => $new);
 
-  for my $ck (['new',     $delta->new_modules, $added],
-              ['removed', $delta->removed_modules, $removed],
-              ['updated', $delta->updated_modules, [@{ $modules }, @{ $pragmas }]]) {
+  for my $ck ([ 'new', $delta->new_modules, $added ],
+              [ 'removed', $delta->removed_modules, $removed ],
+              [ 'updated', $delta->updated_modules, $updated ] ) {
     my @delta = @{ $ck->[1] };
-    my @corelist = sort { lc $a->[0] cmp lc $b->[0] } @{ $ck->[2] };
+    my @corelist = sort { lc $a->[0] cmp lc $b->[0] } values %{ $ck->[2] };
 
     printf $ck->[0] . ":\n";
 
-- 
1.7.10.4

@p5pRT
Copy link
Author

p5pRT commented Oct 6, 2013

From abiviq@hushmail.com

0008-Porting-corelist-perldelta.pl-Make-do_check-less-noi.patch
From f9bf448d5c965b278d15de227b7c258788d767e3 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Thu, 3 Oct 2013 17:14:34 -0400
Subject: [PATCH 8/9] Porting/corelist-perldelta.pl - Make do_check less noisy

The removed modules as returned by corelist_perldelta contains
too many false positives to be useful. Also, explictly state
whether the difference is from the pod or the results returned from
corelist_perldelta().
---
 Porting/corelist-perldelta.pl |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 303a3cb..23071a5 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -304,8 +304,10 @@ sub do_check {
   my $delta = DeltaParser->new($in);
   my ($added, $removed, $updated) = corelist_delta($old => $new);
 
+  # because of the difficulty in identifying the distribution for removed modules
+  # don't bother checking them
   for my $ck ([ 'new', $delta->new_modules, $added ],
-              [ 'removed', $delta->removed_modules, $removed ],
+              #[ 'removed', $delta->removed_modules, $removed ],
               [ 'updated', $delta->updated_modules, $updated ] ) {
     my @delta = @{ $ck->[1] };
     my @corelist = sort { lc $a->[0] cmp lc $b->[0] } values %{ $ck->[2] };
@@ -328,9 +330,9 @@ sub do_check {
         $sep = "---\n";
         printf "%d,%dc%d,%d\n", $diff->Get(qw( Min1 Max1 Min2 Max2 ));
       }
-      print "< $_\n" for $diff->Items(1);
+      print "Delta< $_\n" for $diff->Items(1);
       print $sep;
-      print "> $_\n" for $diff->Items(2);
+      print "Corelist> $_\n" for $diff->Items(2);
     }
 
     print "\n";
-- 
1.7.10.4

@p5pRT
Copy link
Author

p5pRT commented Oct 6, 2013

From abiviq@hushmail.com

0002-Porting-corelist-perldelta.pl-fix-usage-example.patch
From 7becb16fdfd364e2417211dba00487174ad21952 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Sun, 6 Oct 2013 10:52:21 -0400
Subject: [PATCH 2/9] Porting/corelist-perldelta.pl - fix usage example

lib needs to be included when generating the module changes
with the currently build Perl
---
 Porting/corelist-perldelta.pl |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 2947923..022b427 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -11,7 +11,7 @@ use Getopt::Long;
 =head1 USAGE
 
   # generate the module changes for the Perl you are currently building
-  ./perl Porting/corelist-perldelta.pl
+  ./perl -Ilib Porting/corelist-perldelta.pl
   
   # generate a diff between the corelist sections of two perldelta* files:
   perl Porting/corelist-perldelta.pl --mode=check 5.017001 5.017002 <perl5172delta.pod
-- 
1.7.10.4

@p5pRT
Copy link
Author

p5pRT commented Oct 14, 2013

From @steve-m-hay

On Sun Oct 06 09​:10​:27 2013, abirv wrote​:

The patches attached are what I have attempted so far.

Patches 1-6 are mostly improvements to corelist-perldelta.pl's check
mode that are unrelated to the task at hand, but were helpful in
observing past perldelta.

Patch 7 includes fixes for the determination of changed modules that
takes into account the mapping between distribution and key module based
on how their changes have been listed in past perldelta. With
this, the only distributions that are currently unaccounted for are
podlater, libnet, and Win32. However, the determination of removed
modules still includes too many false positives to be useful.

Patch 9 is my initial attempt at being able to update an existing
perldelta file by adding unlisted modules and updating the version
number of listed modules if they differ from Module​::CoreList. I used
the non-core Pod​::Elemental for pod manipulation. The core pod parsing
modules don't seem to allow easy serialization back to pod, especially
without including whitespace differences. Is the use of Pod​::Elemental
acceptable, and if not, what alternate approach should I take?

Thank you for your work on this.

I have not had time to examine the patches in detail yet, but an initial
reaction is that I would much prefer the script not to rely on non-core
modules.

Therefore, unless there is agreement by p5p and the current pumpking
(Ricardo -- also, by coincidence, the author of the CPAN module in
question (Pod​::Elemental)!) to include Pod​::Elemental in the core then I
would rather patch 9 was restructured in some way.

If this is not possible to do using existing core modules then you could
either copy sections of Pod​::Elemental for use directly in the script
(unless this would require copying too much code for such an approach to
be sensible) or else consider manipulating the POD more directly as
plain text -- perldelta is a fairly simply structured file after all.

@p5pRT
Copy link
Author

p5pRT commented Oct 22, 2013

From abiviq@hushmail.com

Thank you for your comments.

I have rewritten patch 9 so that it does not rely on non-core
distributions.

Recent changes to Maintainers.pl has meant that patch 7, which fixed how
changed modules were determined, also needed to be updated.

A new patch, patch 10 removes the now redundant generate mode functions
as that functionality is also present in the pod manipulator.
On Mon Oct 14 06​:15​:51 2013, shay wrote​:

On Sun Oct 06 09​:10​:27 2013, abirv wrote​:

The patches attached are what I have attempted so far.

Patches 1-6 are mostly improvements to corelist-perldelta.pl's check
mode that are unrelated to the task at hand, but were helpful in
observing past perldelta.

Patch 7 includes fixes for the determination of changed modules that
takes into account the mapping between distribution and key module based
on how their changes have been listed in past perldelta. With
this, the only distributions that are currently unaccounted for are
podlater, libnet, and Win32. However, the determination of removed
modules still includes too many false positives to be useful.

Patch 9 is my initial attempt at being able to update an existing
perldelta file by adding unlisted modules and updating the version
number of listed modules if they differ from Module​::CoreList. I used
the non-core Pod​::Elemental for pod manipulation. The core pod parsing
modules don't seem to allow easy serialization back to pod, especially
without including whitespace differences. Is the use of Pod​::Elemental
acceptable, and if not, what alternate approach should I take?

Thank you for your work on this.

I have not had time to examine the patches in detail yet, but an initial
reaction is that I would much prefer the script not to rely on non-core
modules.

Therefore, unless there is agreement by p5p and the current pumpking
(Ricardo -- also, by coincidence, the author of the CPAN module in
question (Pod​::Elemental)!) to include Pod​::Elemental in the core then I
would rather patch 9 was restructured in some way.

If this is not possible to do using existing core modules then you could
either copy sections of Pod​::Elemental for use directly in the script
(unless this would require copying too much code for such an approach to
be sensible) or else consider manipulating the POD more directly as
plain text -- perldelta is a fairly simply structured file after all.

@p5pRT
Copy link
Author

p5pRT commented Oct 22, 2013

From abiviq@hushmail.com

2-0009-Porting-corelist-perldelta.pl-Add-a-new-mode-update.patch
From 7516335797d7bdf495e6d7277f7edd08d63f57e0 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Sun, 6 Oct 2013 10:58:27 -0400
Subject: [PATCH 09/10] Porting/corelist-perldelta.pl - Add a new mode update

This adds a new mode, update, which given an the path to an existing
perldelta file, will add missing entries and update incorrect version
information.

This commit introduces a new module, DeltaUpdater, which is used
for pod manipulation.
---
 Porting/corelist-perldelta.pl | 417 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 415 insertions(+), 2 deletions(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 4e21622..de76149 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -12,7 +12,10 @@ use Getopt::Long;
 
   # generate the module changes for the Perl you are currently building
   ./perl -Ilib Porting/corelist-perldelta.pl
-  
+
+  # update the module changes for the Perl you are currently building
+  perl Porting/corelist-perldelta.pl --mode=update Porting/perldelta.pod
+
   # generate a diff between the corelist sections of two perldelta* files:
   perl Porting/corelist-perldelta.pl --mode=check 5.017001 5.017002 <perl5172delta.pod
 
@@ -91,7 +94,7 @@ sub run {
   my %opt = (mode => 'generate');
 
   GetOptions(\%opt,
-    'mode|m:s', # 'generate', 'check'
+    'mode|m:s', # 'generate', 'check', 'update'
   );
 
   # by default, compare latest two version in CoreList;
@@ -106,6 +109,9 @@ sub run {
   elsif ( $opt{mode} eq 'check' ) {
     do_check(\*ARGV, $old => $new);
   }
+  elsif ( $opt{mode} eq 'update' ) {
+    do_update_existing(shift @ARGV, $old => $new);
+  }
   else {
     die "Unrecognized mode '$opt{mode}'\n";
   }
@@ -274,6 +280,28 @@ sub corelist_delta {
   );
 }
 
+# currently does not update the Removed Module section
+sub do_update_existing {
+  my ( $existing, $old, $new ) = @_;
+
+  my ( $added, $removed, $updated, $manuallyCheck ) = corelist_delta( $old => $new );
+  if ($manuallyCheck) {
+    say "Please check whether the following distributions have been modified and list accordingly";
+    say "\t* $_" for sort @{$manuallyCheck};
+  }
+
+  my $data = {
+    new      => $added,
+    updated  => $updated,
+    #removed => $removed, ignore removed for now
+  };
+
+  my $text = DeltaUpdater::transform_pod( $existing, $data );
+  open my $out, '>', $existing or die "can't open perldelta file $existing: $!";
+  print $out $text;
+  close $out;
+}
+
 sub do_generate {
   my ($old, $new) = @_;
   my ($added, $removed, $updated, $manuallyCheck) = corelist_delta($old => $new);
@@ -330,6 +358,391 @@ sub do_check {
 }
 
 {
+
+  package DeltaUpdater;
+  use List::Util 'reduce';
+
+  sub get_section_name_from_heading {
+    my $heading = shift;
+    while (my ($key, $expression) = each %sections) {
+      if ($heading =~ $expression) {
+        return $titles{$key};
+      }
+    }
+    die "$heading did not match any section";
+  }
+
+  sub is_desired_section_name {
+    for (values %sections) {
+      return 1 if $_[0] =~ $_;
+    }
+    return 0;
+  }
+
+  # verify the module and pragmata in the section, changing the stated version if necessary
+  # this subroutine warns if the module name cannot be parsed or if it is not listed in
+  # the results returned from corelist_delta()
+  #
+  # a side-effect of calling this function is that modules present in the section are
+  # removed from $data, resulting in $data containing only those modules and pragmata
+  # that were not listed in the perldelta file. This means we can then pass $data to
+  # add_to_section() without worrying about filtering out duplicates
+  sub update_section {
+    my ( $section, $data, $title ) = @_;
+    my @items = @{ $section->{items} };
+
+    for my $item (@items) {
+
+      my $content = $item->{text};
+      my $module  = $item->{name};
+
+      say "Could not parse module name; line is:\n\t$content" and next unless $module;
+      say "$module is not in Module::CoreList; check to see that it is not covered by another section" and next
+        unless $data->{$title}{$module};
+
+      if ( $title eq 'new' ) {
+        my ($new) = $content =~ /(\d[^\s]+)\s+has\s+been.*$/m;
+        say "Could not parse new version for $module; line is:\n\t$content" and next unless $new;
+        if ( $data->{$title}{$module}[2] ne $new ) {
+            say "$module: new version differs; version in pod: $new; version in corelist: " . $data->{$title}{$module}[2];
+        }
+        $content =~ s/\d[^\s]+(\s+has\s+been.*$)/$data->{$title}{$module}[2].$1/me;
+      }
+
+      elsif ( $title eq 'updated' ) {
+        my ( $prev, $new ) = $content =~ /from\s+(?:version\s+)?(\d[^\s]+)\s+to\s+(?:version\s+)?(\d[^\s,]+?)(?=[\s,]|\.\s|\.$|$).*/s;
+        say "Could not parse old and new version for $module; line is:\n\t$content" and next
+          unless $prev and $new;
+        if ( $data->{$title}{$module}[1] ne $prev ) {
+          say "$module: previous version differs; version in pod: $prev; version in corelist: " . $data->{$title}{$module}[1];
+        }
+        if ( $data->{$title}{$module}[2] ne $new ) {
+          say "$module: new version differs; version in pod: $new; version in corelist: " . $data->{$title}{$module}[2];
+        }
+        $content =~
+          s/(from\s+(?:version\s+)?)\d[^\s]+(\s+to\s+(?:version\s+)?)\d[^\s,]+?(?=[\s,]|\.\s|\.$|$)(.*)/$1.$data->{$title}{$module}[1].$2.$data->{$title}{$module}[2].$3/se;
+      }
+
+      elsif ( $title eq 'removed' ) {
+        my ($prev) = $content =~ /^.*?was\s+(\d[^\s]+?)/m;
+        say "Could not parse old version for $module; line is:\n\t$content" and next unless $prev;
+        if ( $data->{$title}{$module}[1] ne $prev ) {
+          say "$module: previous version differs; $prev " . $data->{$title}{$module}[1];
+        }
+        $content =~ s/(^.*?was\s+)\d[^\s]+?/$1.$data->{$title}{$module}[1]/me;
+      }
+
+      delete $data->{$title}{$module};
+      $item->{text} = $content;
+    }
+    return $section;
+  }
+
+  # add modules and pragmata present in $data to the section
+  sub add_to_section {
+    my ( $section, $data, $title ) = @_;
+
+    #undef is a valid version name in Module::CoreList so supress warnings about concatenating undef values
+    no warnings 'uninitialized';
+    for ( values %{ $data->{$title} } ) {
+      my ( $mod, $old_v, $new_v ) = @{$_};
+      my ( $item, $text );
+
+      $item = { name => $mod, text => "=item *\n" };
+      if ( $title eq 'new' ) {
+        $text = "L<$mod> $new_v has been added to the Perl core.\n";
+      }
+
+      elsif ( $title eq 'updated' ) {
+        $text = "L<$mod> has been upgraded from version $old_v to $new_v.\n";
+        if ( $deprecated->{$mod} ) {
+          $text .= "NOTE: L<$mod> is deprecated and may be removed from a future version of Perl.\n";
+        }
+      }
+
+      elsif ( $title eq 'removed' ) {
+        $text = "C<$mod> has been removed from the Perl core.  Prior version was $old_v.\n";
+      }
+
+      $item->{text} .= "\n$text\n";
+      push @{ $section->{items} }, $item;
+    }
+    return $section;
+  }
+
+  sub sort_items_in_section {
+    my ($section) = @_;
+
+    # if we could not parse the module name, it will be uninitalized
+    # in sort. This is not a problem as it will just result in these
+    # sections being placed near the begining of the section
+    no warnings 'uninitialized';
+    $section->{items} =
+      [ sort { lc $a->{name} cmp lc $b->{name} } @{ $section->{items} } ];
+    return $section;
+  }
+
+  # given a hashref of the form returned by corelist_delta()
+  # and a hash structured as documented in transform_pod(), it returns
+  # a pod string representation of the sections, creating sections
+  # if necessary
+  sub sections_to_pod {
+    my ( $data, %sections ) = @_;
+    my $out = '';
+
+    for (
+        (
+          [ 'New Modules and Pragmata',     'new' ],
+          [ 'Updated Modules and Pragmata', 'updated' ],
+          [ 'Removed Modules and Pragmata', 'removed' ]
+        )
+      )
+    {
+      my ( $section_name, $title ) = @{$_};
+
+      my $section = $sections{$section_name} // {
+          name            => $section_name,
+          preceeding_text => "=head2 $_->[0]\n=over 4\n",
+          following_text  => "=back\n",
+          items           => [],
+          manual          => 1
+      };
+
+      $section = update_section( $section, $data, $title );
+      $section = add_to_section( $section, $data, $title );
+      $section = sort_items_in_section( $section );
+
+      next if $section->{manual} and scalar @{ $section->{items} } == 0;
+
+      my $items = reduce { no warnings 'once'; $a . $b->{text} }
+        ( '', @{ $section->{items} } );
+      $out .=
+        ( $section->{preceeding_text} // '' )
+        . $items
+        . ( $section->{following_text} // '' );
+    }
+    return $out;
+  }
+
+  # given a filename corresponding to an existing perldelta file
+  # and a hashref of the form returned by corelist_delta(), it
+  # returns a string of the resulting file after the module
+  # information has been added.
+  sub transform_pod {
+    my ( $existing, $data ) = @_;
+
+    # will contain hashrefs corresponding to new, updated and removed
+    # modules and pragmata keyed by section name
+    # each section is hashref of the structure
+    #   preceeding_text => Text occuring before and including the over
+    #                      region containing the list of modules,
+    #   items           => [Arrayref of hashrefs corresponding to a module
+    #                       entry],
+    #     an entry has the form:
+    #       name => Module name or undef if the name could not be determined
+    #       text => The text of the entry, including the item heading
+    #
+    #   following_text  => Any text not corresponding to a module
+    #                      that occurs after the first module
+    #
+    # the sections are converted to a pod string by calling sections_to_pod()
+    my %sections;
+
+    # we are in the Modules_and_Pragmata's section
+    my $in_Modules_and_Pragmata;
+    # we are the Modules_and_Pragmata's section but have not
+    # encountered any of the desired sections. We use this
+    # flag to determine whether we should append the text to $out
+    # or we need to delay appending until the module listings are
+    # processed and instead append to $append_to_out
+    my $in_Modules_and_Pragmata_preamble;
+    my $done_processing_Modules_and_Pragmata;
+
+    my $current_section;
+    # $nested_element_level == 0 : not in an over region, treat lines as text
+    # $nested_element_level == 1 : presumably in the top over region that
+    #                              corresponds to the module listing. Treat
+    #                              each item as a module
+    # $nested_element_level > 1  : we only consider these values when we are in an item
+    #                              We treat lines as the text of the current item.
+    my $nested_element_level = 0;
+    my $current_item;
+    my $need_to_parse_module_name;
+
+    my $out = '';
+    my $append_to_out = '';
+
+    open my $fh, '<', $existing or die "can't open perldelta file $existing: $!";
+
+    while (<$fh>) {
+      # treat the rest of the file as plain text
+      if ($done_processing_Modules_and_Pragmata) {
+        $out .= $_;
+        next;
+      }
+
+      elsif ( !$in_Modules_and_Pragmata ) {
+        # entering Modules and Pragmata
+        if (/^=head1 Modules and Pragmata/) {
+          $in_Modules_and_Pragmata          = 1;
+          $in_Modules_and_Pragmata_preamble = 1;
+        }
+        $out .= $_;
+        next;
+      }
+
+      # leaving Modules and Pragmata
+      elsif (/^=head1/) {
+        if ($current_section) {
+          push @{ $current_section->{items} }, $current_item
+            if $current_item;
+          $sections{ $current_section->{name} } = $current_section;
+        }
+        $done_processing_Modules_and_Pragmata = 1;
+        $out .=
+          sections_to_pod( $data, %sections ) . $append_to_out . $_;
+        next;
+      }
+
+      # new section in Modules and Pragmata
+      elsif (/^=head2 (.*?)$/) {
+        my $name = $1;
+        if ($current_section) {
+          push @{ $current_section->{items} }, $current_item
+            if $current_item;
+          $sections{ $current_section->{name} } = $current_section;
+          undef $current_section;
+        }
+
+        if ( is_desired_section_name($name) ) {
+          undef $in_Modules_and_Pragmata_preamble;
+          if ( $nested_element_level > 0 ) {
+            die "Unexpected head2 at line no. $.";
+          }
+          my $title = get_section_name_from_heading($name);
+          if ( exists $sections{$title} ) {
+            die "$name occured twice at line no. $.";
+          }
+          $current_section                    = {};
+          $current_section->{name}            = $title;
+          $current_section->{preceeding_text} = $_;
+          $current_section->{items}           = [];
+          $nested_element_level               = 0;
+          next;
+        }
+
+        # otherwise treat section as plain text
+        else {
+          if ($in_Modules_and_Pragmata_preamble) {
+            $out .= $_;
+          }
+          else {
+            $append_to_out .= $_;
+          }
+          next;
+        }
+      }
+
+      elsif ($current_section) {
+
+        # not in an over region
+        if ( $nested_element_level == 0 ) {
+          if (/^=over/) {
+            $nested_element_level++;
+          }
+          if ( scalar @{ $current_section->{items} } > 0 ) {
+            $current_section->{following_text} .= $_;
+          }
+          else {
+            $current_section->{preceeding_text} .= $_;
+          }
+          next;
+        }
+
+        if ($current_item) {
+          if ($need_to_parse_module_name) {
+            # the item may not have a parsable module name, which means that
+            # $current_item->{name} will never be defined.
+            if (/^(?:L|C)<(.+?)>/) {
+              $current_item->{name} = $1;
+              undef $need_to_parse_module_name;
+            }
+            # =item or =back signals the end of an item
+            # block, which we handle below
+            if ( !/^=(?:item|back)/ ) {
+              $current_item->{text} .= $_;
+              next;
+            }
+          }
+          # currently in an over region
+          # treat text inside region as plain text
+          if ( $nested_element_level > 1 ) {
+            if (/^=back/) {
+              $nested_element_level--;
+            }
+            elsif (/^=over/) {
+              $nested_element_level++;
+            }
+            $current_item->{text} .= $_;
+            next;
+          }
+          # entering over region
+          if (/^=over/) {
+            $nested_element_level++;
+            $current_item->{text} .= $_;
+            next;
+          }
+          # =item or =back signals the end of an item
+          # block, which we handle below
+          if ( !/^=(?:item|back)/ ) {
+            $current_item->{text} .= $_;
+            next;
+          }
+        }
+
+        if (/^=item \*/) {
+          push @{ $current_section->{items} }, $current_item
+            if $current_item;
+          $current_item = { text => $_ };
+          $need_to_parse_module_name = 1;
+          next;
+        }
+
+        if (/^=back/) {
+          push @{ $current_section->{items} }, $current_item
+            if $current_item;
+          undef $current_item;
+          $nested_element_level--;
+        }
+
+        if ( scalar @{ $current_section->{items} } == 0 ) {
+          $current_section->{preceeding_text} .= $_;
+        }
+        else {
+          $current_section->{following_text} .= $_;
+        }
+        next;
+      }
+
+      # text in Modules and Pragmata not in a head2 region
+      else {
+        if ($in_Modules_and_Pragmata_preamble) {
+          $out .= $_;
+        }
+        else {
+          $append_to_out .= $_;
+        }
+        next;
+      }
+    }
+    close $fh;
+    die 'Never saw Modules and Pragmata section' unless $in_Modules_and_Pragmata;
+    return $out;
+  }
+
+}
+
+{
   package DeltaParser;
   use Pod::Simple::SimpleTree;
 
-- 
1.8.4.rc3

@p5pRT
Copy link
Author

p5pRT commented Oct 22, 2013

From abiviq@hushmail.com

2-0010-Porting-corelist-perldelta.pl-Make-do_generate-use-D.patch
From acd15655aa7713c4393c0f456ae410401a39c1e1 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Tue, 22 Oct 2013 13:31:18 -0400
Subject: [PATCH 10/10] Porting/corelist-perldelta.pl - Make do_generate use
 DeltaUpdater

DeltaUpdater::sections_to_pod() makes generate_section() redundant
---
 Porting/corelist-perldelta.pl | 52 ++++++-------------------------------------
 1 file changed, 7 insertions(+), 45 deletions(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index de76149..a91d27e 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -48,48 +48,6 @@ my %titles = (
 
 my $deprecated;
 
-#--------------------------------------------------------------------------#
-
-sub added {
-  my ($mod, $old_v, $new_v) = @_;
-  say "=item *\n";
-  say "L<$mod> $new_v has been added to the Perl core.\n";
-}
-
-sub updated {
-  my ($mod, $old_v, $new_v) = @_;
-  say "=item *\n";
-  say "L<$mod> has been upgraded from version $old_v to $new_v.\n";
-  if ( $deprecated->{$mod} ) {
-    say "NOTE: L<$mod> is deprecated and may be removed from a future version of Perl.\n";
-  }
-}
-
-sub removed {
-  my ($mod, $old_v, $new_v) = @_;
-  say "=item *\n";
-  say "C<$mod> has been removed from the Perl core.  Prior version was $old_v.\n";
-}
-
-sub generate_section {
-  my ($title, $item_sub, @mods ) = @_;
-  return unless @mods;
-
-  say "=head2 $title\n";
-  say "=over 4\n";
-
-  for my $tuple ( sort { lc($a->[0]) cmp lc($b->[0]) } @mods ) {
-    my ($mod,$old_v,$new_v) = @$tuple;
-    $old_v //= q('undef');
-    $new_v //= q('undef');
-    $item_sub->($mod, $old_v, $new_v);
-  }
-
-  say "=back\n";
-}
-
-#--------------------------------------------------------------------------#
-
 sub run {
   my %opt = (mode => 'generate');
 
@@ -311,9 +269,13 @@ sub do_generate {
     say "\t$_" for @{$manuallyCheck};
   }
 
-  generate_section( $titles{new},     \&added,   values %{$added} );
-  generate_section( $titles{updated}, \&updated, values %{$updated} );
-  generate_section( $titles{removed}, \&removed, values %{$removed} );
+  my $data = {
+    new      => $added,
+    updated  => $updated,
+    #removed => $removed, ignore removed for now
+  };
+
+  say DeltaUpdater::sections_to_pod($data)
 }
 
 sub do_check {
-- 
1.8.4.rc3

@p5pRT
Copy link
Author

p5pRT commented Oct 22, 2013

From abiviq@hushmail.com

2-0007-Porting-corelist-perldelta.pl-Improve-corelist_delta.patch
From 687e1ef85fdca46e9191d48b6cdc61c7a281da7b Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Thu, 3 Oct 2013 17:01:54 -0400
Subject: [PATCH 07/10] Porting/corelist-perldelta.pl - Improve corelist_delta

corelist_delta now goes through almost all of the core distributions.
The problem with the previous approach was that the keys of %Modules in
Porting/Maintainers.pl do not all correspond to a valid distribution or
do not correspond to a module as listed in Module::CoreList.
This commit also updates the callers of the function.
---
 Porting/corelist-perldelta.pl | 181 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 151 insertions(+), 30 deletions(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 19ab0d3..94ec500 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -113,35 +113,146 @@ sub run {
   exit 0;
 }
 
+# Given two perl versions, it returns a list describing the core distributions that have changed.
+# The first three elements are hashrefs corresponding to new, updated, and removed modules
+# and are of the form (mostly, see the special remarks about removed):
+#   'Distribution Name' => ['Distribution Name', previous version number, current version number]
+# where the version number is undef if the distribution did not exist the fourth element is
+# an arrayref of core distribution names of those distribution for which it is unknown whether
+# they have changed and therefore need to be manually checked.
+#
+# In most cases, the distribution name in %Modules corresponds to the module that is representative
+# of the distribution as listed in Module::CoreList. However, there are a few distribution names
+# that do not correspond to a module. %distToModules, has been created which maps the distribution
+# name to a representative module. The representative module was chosen by either looking at the
+# Makefile of the distribution or by seeing which module the distribution has been traditionally
+# listed under in past perldelta.
+#
+# There are a few distributions for which there is no single representative module (e.g. libnet).
+# These distributions are returned as the last element of the list.
+#
+# %Modules contains a final key, _PERLLIB, which contains a list of modules that are owned by p5p.
+# This list contains modules and pragmata that may also be present in Module::CoreList.
+# A list of modules are in the list @unclaimedModules, which were manually listed based on whether
+# they were independent modules and whether they have been listed in past perldelta.
+# The pragmata were found by doing something like:
+#   say for sort grep { $_ eq lc $_ and !exists $Modules{$_}}
+#     keys %{$Module::CoreList::version{'5.019003'}}
+# and manually filtering out pragamata that were already covered.
+#
+# It is currently not possible to differentiate between a removed module and a removed
+# distribution. Therefore, the removed hashref contains every module that has been removed, even if
+# the module's corresponding distribution has not been removed.
+
 sub corelist_delta {
   my ($old, $new) = @_;
   my $corelist = \%Module::CoreList::version;
-
+  my %changes = Module::CoreList::changes_between( $old, $new );
   $deprecated = $Module::CoreList::deprecated{$new};
 
-  my (@new,@deprecated,@removed,@pragmas,@modules);
+  my $getModifyType = sub {
+    my $data = shift;
+    if ( exists $data->{left} and exists $data->{right} ) {
+      return 'updated';
+    }
+    elsif ( !exists $data->{left} and exists $data->{right} ) {
+      return 'new';
+    }
+    elsif ( exists $data->{left} and !exists $data->{right} ) {
+      return 'removed';
+    }
+    return undef;
+  };
+
+  my @unclaimedModules = qw/AnyDBM_File B B::Concise B::Deparse Benchmark Class::Struct Config::Extensions DB DBM_Filter Devel::Peek DirHandle DynaLoader English Errno ExtUtils::Embed ExtUtils::Miniperl ExtUtils::Typemaps ExtUtils::XSSymSet Fcntl File::Basename File::Compare File::Copy File::DosGlob File::Find File::Glob File::stat FileCache FileHandle FindBin GDBM_File Getopt::Std Hash::Util Hash::Util::FieldHash I18N::Langinfo IPC::Open3 NDBM_File ODBM_File Opcode PerlIO PerlIO::encoding PerlIO::mmap PerlIO::scalar PerlIO::via Pod::Functions Pod::Html POSIX SDBM_File SelectSaver Symbol Sys::Hostname Thread Tie::Array Tie::Handle Tie::Hash Tie::Hash::NamedCapture Tie::Memoize Tie::Scalar Tie::StdHandle Tie::SubstrHash Time::gmtime Time::localtime Time::tm Unicode::UCD UNIVERSAL User::grent User::pwent VMS::DCLsym VMS::Filespec VMS::Stdio XS::Typemap Win32CORE/;
+  my @unclaimedPragmata = qw/_charnames arybase attributes blib bytes charnames deprecate diagnostics encoding feature fields filetest inc::latest integer less locale mro open ops overload overloading re sigtrap sort strict subs utf8 vars vmsish/;
+  my @unclaimed = (@unclaimedModules, @unclaimedPragmata);
+
+  my %distToModules = (
+    'IO-Compress' => [
+      {
+        'name' => 'IO-Compress',
+        'modification' => $getModifyType->( $changes{'IO::Compress::Base'} ),
+        'data' => $changes{'IO::Compress::Base'}
+      }
+    ],
+    'Locale-Codes' => [
+      {
+        'name'         => 'Locale::Codes',
+        'modification' => $getModifyType->( $changes{'Locale::Codes'} ),
+        'data'         => $changes{'Locale::Codes'}
+      }
+    ],
+    'PathTools' => [
+      {
+        'name'         => 'File::Spec',
+        'modification' => $getModifyType->( $changes{'Cwd'} ),
+        'data'         => $changes{'Cwd'}
+      }
+    ],
+    'Scalar-List-Utils' => [
+      {
+        'name'         => 'List::Util',
+        'modification' => $getModifyType->( $changes{'List::Util'} ),
+        'data'         => $changes{'List::Util'}
+      },
+      {
+        'name'         => 'Scalar::Util',
+        'modification' => $getModifyType->( $changes{'Scalar::Util'} ),
+        'data'         => $changes{'Scalar::Util'}
+      }
+    ],
+    'Text-Tabs+Wrap' => [
+      {
+        'name'         => 'Text::Tabs',
+        'modification' => $getModifyType->( $changes{'Text::Tabs'} ),
+        'data'         => $changes{'Text::Tabs'}
+      },
+      {
+        'name'         => 'Text::Wrap',
+        'modification' => $getModifyType->( $changes{'Text::Wrap'} ),
+        'data'         => $changes{'Text::Wrap'}
+      }
+    ],
+  );
+
+  # structure is (new|removed|updated) => [ [ModuleName, previousVersion, newVersion] ]
+  my $deltaGrouping = {};
+
+  # list of distributions listed in %Modules that need to be manually checked because there is no module that represents it
+  my @manuallyCheck;
 
   # %Modules defines what is currently in core
   for my $k ( keys %Modules ) {
-    next unless exists $corelist->{$new}{$k};
-    my $old_ver = $corelist->{$old}{$k};
-    my $new_ver = $corelist->{$new}{$k};
-    # in core but not in last corelist
-    if ( ! exists $corelist->{$old}{$k} ) {
-      push @new, [$k, undef, $new_ver];
+    next if $k eq '_PERLLIB'; #these are taken care of by being listed in @unclaimed
+    next if Module::CoreList::is_core($k) and !exists $changes{$k}; #modules that have not changed
+
+    my ( $distName, $modifyType, $data );
+
+    if ( exists $changes{$k} ) {
+      $distName   = $k;
+      $modifyType = $getModifyType->( $changes{$k} );
+      $data       = $changes{$k};
     }
-    # otherwise just pragmas or modules
-    else {
-      my $old_ver = $corelist->{$old}{$k};
-      my $new_ver = $corelist->{$new}{$k};
-      next unless defined $old_ver && defined $new_ver && $old_ver ne $new_ver;
-      my $tuple = [ $k, $old_ver, $new_ver ];
-      if ( $k eq lc $k ) {
-        push @pragmas, $tuple;
-      }
-      else {
-        push @modules, $tuple;
+    elsif ( exists $distToModules{$k} ) {
+      # modification will be undef if the distribution has not changed
+      my @modules = grep { $_->{modification} } @{ $distToModules{$k} };
+      for (@modules) {
+        $deltaGrouping->{ $_->{modification} }->{ $_->{name} } = [ $_->{name}, $_->{data}->{left}, $_->{data}->{right} ];
       }
+      next;
+    }
+    else {
+      push @manuallyCheck, $k and next;
+    }
+
+    $deltaGrouping->{$modifyType}->{$distName} = [ $distName, $data->{left}, $data->{right} ];
+  }
+
+  for my $k (@unclaimed) {
+    if ( exists $changes{$k} ) {
+      $deltaGrouping->{ $getModifyType->( $changes{$k} ) }->{$k} =
+        [ $k, $changes{$k}->{left}, $changes{$k}->{right} ];
     }
   }
 
@@ -151,33 +262,43 @@ sub corelist_delta {
   # important. That's the best we can do without a historical Maintainers.pl
   for my $k ( keys %{ $corelist->{$old} } ) {
     if ( ! exists $corelist->{$new}{$k} ) {
-      push @removed, [$k, $corelist->{$old}{$k}, undef];
+      $deltaGrouping->{'removed'}->{$k} = [ $k, $corelist->{$old}{$k}, undef ];
     }
   }
 
-  return (\@new, \@removed, \@pragmas, \@modules);
+  return (
+    \%{ $deltaGrouping->{'new'} },
+    \%{ $deltaGrouping->{'removed'} },
+    \%{ $deltaGrouping->{'updated'} },
+    \@manuallyCheck
+  );
 }
 
 sub do_generate {
   my ($old, $new) = @_;
-  my ($added, $removed, $pragmas, $modules) = corelist_delta($old => $new);
+  my ($added, $removed, $updated, $manuallyCheck) = corelist_delta($old => $new);
+
+  if ($manuallyCheck) {
+    say "\nXXXPlease check whether the following distributions have been modified and list accordingly";
+    say "\t$_" for @{$manuallyCheck};
+  }
 
-  generate_section($titles{new}, \&added, @{ $added });
-  generate_section($titles{updated}, \&updated, @{ $pragmas }, @{ $modules });
-  generate_section($titles{removed}, \&removed, @{ $removed });
+  generate_section( $titles{new},     \&added,   values %{$added} );
+  generate_section( $titles{updated}, \&updated, values %{$updated} );
+  generate_section( $titles{removed}, \&removed, values %{$removed} );
 }
 
 sub do_check {
   my ($in, $old, $new) = @_;
 
   my $delta = DeltaParser->new($in);
-  my ($added, $removed, $pragmas, $modules) = corelist_delta($old => $new);
+  my ($added, $removed, $updated) = corelist_delta($old => $new);
 
-  for my $ck (['new',     $delta->new_modules, $added],
-              ['removed', $delta->removed_modules, $removed],
-              ['updated', $delta->updated_modules, [@{ $modules }, @{ $pragmas }]]) {
+  for my $ck ([ 'new', $delta->new_modules, $added ],
+              [ 'removed', $delta->removed_modules, $removed ],
+              [ 'updated', $delta->updated_modules, $updated ] ) {
     my @delta = @{ $ck->[1] };
-    my @corelist = sort { lc $a->[0] cmp lc $b->[0] } @{ $ck->[2] };
+    my @corelist = sort { lc $a->[0] cmp lc $b->[0] } values %{ $ck->[2] };
 
     printf $ck->[0] . ":\n";
 
-- 
1.8.4.rc3

@p5pRT
Copy link
Author

p5pRT commented Nov 1, 2013

From @steve-m-hay

On 22 October 2013 21​:40, Abir Viqar via RT <perlbug-followup@​perl.org>wrote​:

Thank you for your comments.

I have rewritten patch 9 so that it does not rely on non-core
distributions.

Recent changes to Maintainers.pl has meant that patch 7, which fixed how
changed modules were determined, also needed to be updated.

A new patch, patch 10 removes the now redundant generate mode functions
as that functionality is also present in the pod manipulator.

Thank you again for this. I'm a little bit behind with other things at the
moment, but will definitely get round to this in the next couple of weeks
unless someone else has done so by then.

@p5pRT
Copy link
Author

p5pRT commented Dec 3, 2013

From @steve-m-hay

On Fri Nov 01 01​:11​:43 2013, shay wrote​:

On 22 October 2013 21​:40, Abir Viqar via RT <perlbug-
followup@​perl.org>wrote​:

Thank you for your comments.

I have rewritten patch 9 so that it does not rely on non-core
distributions.

Recent changes to Maintainers.pl has meant that patch 7, which fixed
how
changed modules were determined, also needed to be updated.

A new patch, patch 10 removes the now redundant generate mode
functions
as that functionality is also present in the pod manipulator.

Thank you again for this. I'm a little bit behind with other things at
the
moment, but will definitely get round to this in the next couple of
weeks
unless someone else has done so by then.

I've finally found some time to look at this. Apologies for taking so long to get back to you.

I have applied your complete series of patches (1 through 6, the second version of 7, then 8, then the second version of 9, and finally 10) locally.

However, when giving it a quick initial test I've run into a few problems. Would you care to look at this before I go through the code changes and test it more thoroughly?

1. The USAGE section of the POD says the update mode is run like this​:

perl Porting/corelist-perldelta.pl --mode=update Porting/perldelta.pod

but when I do that I get an error​:

Can't use an undefined value as a HASH reference at dist/Module-CoreList/lib/Module/CoreList.pm line 120.

It seems to be expecting $old and $new versions in @​ARGV. It would be nice if it defaulted to updating perldelta for the current version in development.

2. If I add the $old and $new arguments like this​:

perl Porting/corelist-perldelta.pl --mode=update 5.019006 5.019007 Porting/perldelta.pod

then I get on obvious error that it can't open perldelta.pod because it's in pod/, not Porting/

3. If I correct this too like this​:

perl Porting/corelist-perldelta.pl --mode=update 5.019006 5.019007 pod/perldelta.pod

then I get this output​:


Please check whether the following distributions have been modified and list acc
ordingly
  * libnet
  * podlators
Could not parse module name; line is​:
  =item *

XXX

CGI is not in Module​::CoreList; check to see that it is not covered by another section
File​::Fetch is not in Module​::CoreList; check to see that it is not covered by another section
Could not parse module name; line is​:
  =item *

XXX


The perldelta.pod file that is in place during the development process prior to a release being made has numerous XXX placeholders in it. It is initially just a template file that gets slowly filled in over time and then finished off, with all such placeholders being removed, very close to release day. It would be helpful if this program coped with the presence of such placeholders without complaining about them.

I don't know why it's generating noise about CGI and File​::Fetch, but they are the only two modules currently listed in perldelta.pod as being updated...

Aside from that, the program successfully inserted a new entry into perldelta.pod (Module​::CoreList is updated from 3.01 to 3.02), but running it on a Windows computer causes it to rewrite the file with DOS line endings. It should open the file in 'binary mode' to avoid doing that.

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2013

From abiviq@hushmail.com

I apologize for the errors and incorrect documentation. I have fixed the usage example, defaulted to comparing the last two versions, fixed the newlines, and now ignore placeholder items. I have clarified the various messages. The reason for the message about CGI and File​::Fetch is that it they were not listed as being changed in Module​::CoreList for 5.919007, likely because you have not updated Module​::CoreList. I have changed the message to make this clearer.

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2013

From abiviq@hushmail.com

0011-Porting-corelist-perldelta.pl-Default-to-comparing-l.patch
From b28d5adbdcd7aec6387aa44e672908a88ae69832 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Sat, 14 Dec 2013 14:03:59 -0500
Subject: [PATCH 11/14] Porting/corelist-perldelta.pl - Default to comparing
 last two versions

do_update_existing() previously required versions to be specified
---
 Porting/corelist-perldelta.pl | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index a91d27e..0a9695e 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -57,9 +57,18 @@ sub run {
 
   # by default, compare latest two version in CoreList;
   my @versions = sort keys %Module::CoreList::version;
-  my ($old, $new) = (shift @ARGV, shift @ARGV);
-  $old ||= $versions[-2];
-  $new ||= $versions[-1];
+  my $old = $versions[-2];
+  my $new = $versions[-1];
+
+  # use the provided versions if present
+  # @ARGV >=2 means [old_version] [new_version] [path/to/file]
+  if ( @ARGV >= 2) {
+    ($old, $new) = (shift @ARGV, shift @ARGV);
+    die "$old is an invalid version\n" if not exists
+      $Module::CoreList::version{$old};
+    die "$new is an invalid verison\n" if not exists
+      $Module::CoreList::version{$new};
+  }
 
   if ( $opt{mode} eq 'generate' ) {
     do_generate($old => $new);
-- 
1.8.4.rc3

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2013

From abiviq@hushmail.com

0012-Porting-corelist-perldelta.pl-Use-Unix-newlines-in-p.patch
From e5eb8e9f7ff22965a9d9a5923230adffa43358ca Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Sat, 14 Dec 2013 14:05:43 -0500
Subject: [PATCH 12/14] Porting/corelist-perldelta.pl - Use Unix newlines in
 perldelta

---
 Porting/corelist-perldelta.pl | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 0a9695e..690d66a 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -265,6 +265,7 @@ sub do_update_existing {
 
   my $text = DeltaUpdater::transform_pod( $existing, $data );
   open my $out, '>', $existing or die "can't open perldelta file $existing: $!";
+  binmode($out);
   print $out $text;
   close $out;
 }
@@ -544,6 +545,7 @@ sub do_check {
     my $append_to_out = '';
 
     open my $fh, '<', $existing or die "can't open perldelta file $existing: $!";
+    binmode($fh);
 
     while (<$fh>) {
       # treat the rest of the file as plain text
-- 
1.8.4.rc3

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2013

From abiviq@hushmail.com

0013-Porting-corelist-perldelta.pl-Skip-dummy-items-durin.patch
From ef2a99c93db5ad733e6bff1dd553abb91c654683 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Sat, 14 Dec 2013 14:06:35 -0500
Subject: [PATCH 13/14] Porting/corelist-perldelta.pl - Skip dummy items during
 update

---
 Porting/corelist-perldelta.pl | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 690d66a..5e9f32c 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -368,6 +368,9 @@ sub do_check {
       my $content = $item->{text};
       my $module  = $item->{name};
 
+      #skip dummy items
+      next if !$module and $content =~ /\s*xx*\s*/i;
+
       say "Could not parse module name; line is:\n\t$content" and next unless $module;
       say "$module is not in Module::CoreList; check to see that it is not covered by another section" and next
         unless $data->{$title}{$module};
-- 
1.8.4.rc3

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2013

From abiviq@hushmail.com

0014-Porting-corelist-perldelta.pl-Make-documentation-cle.patch
From 234c2a16c7da56821998505e82227ce2a50a6ef9 Mon Sep 17 00:00:00 2001
From: Abir Viqar <abiviq@hushmail.com>
Date: Sat, 14 Dec 2013 14:07:28 -0500
Subject: [PATCH 14/14] Porting/corelist-perldelta.pl - Make documentation
 clearer

* Fix incorrect usage example for update
* Document that removed modules are currently ignored
* Clarify why some distributions have to be manually checked
* Clarify why a distribution may not be listed in Module::CoreList
---
 Porting/corelist-perldelta.pl | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/Porting/corelist-perldelta.pl b/Porting/corelist-perldelta.pl
index 5e9f32c..d34766c 100755
--- a/Porting/corelist-perldelta.pl
+++ b/Porting/corelist-perldelta.pl
@@ -14,7 +14,7 @@ use Getopt::Long;
   ./perl -Ilib Porting/corelist-perldelta.pl
 
   # update the module changes for the Perl you are currently building
-  perl Porting/corelist-perldelta.pl --mode=update Porting/perldelta.pod
+  ./perl -Ilib Porting/corelist-perldelta.pl --mode=update pod/perldelta.pod
 
   # generate a diff between the corelist sections of two perldelta* files:
   perl Porting/corelist-perldelta.pl --mode=check 5.017001 5.017002 <perl5172delta.pod
@@ -32,6 +32,8 @@ Ideally, the program will be split into two separate programs, one
 to generate the text and one to show the diff between the
 corelist sections of the last perldelta and the next perldelta.
 
+Currently no information about Removed Modules is displayed in any of the
+modes.
 =cut
 
 my %sections = (
@@ -253,8 +255,10 @@ sub do_update_existing {
 
   my ( $added, $removed, $updated, $manuallyCheck ) = corelist_delta( $old => $new );
   if ($manuallyCheck) {
-    say "Please check whether the following distributions have been modified and list accordingly";
+    print "It cannot be determined whether the following distributions have changed.\n";
+    print "Please check and list accordingly:\n";
     say "\t* $_" for sort @{$manuallyCheck};
+    print "\n";
   }
 
   my $data = {
@@ -268,6 +272,8 @@ sub do_update_existing {
   binmode($out);
   print $out $text;
   close $out;
+  say "The New and Updated Modules and Pragamata sections in $existing have been updated";
+  say "Please ensure the Removed Modules and Pragmata section is up-to-date";
 }
 
 sub do_generate {
@@ -275,8 +281,10 @@ sub do_generate {
   my ($added, $removed, $updated, $manuallyCheck) = corelist_delta($old => $new);
 
   if ($manuallyCheck) {
-    say "\nXXXPlease check whether the following distributions have been modified and list accordingly";
+    print "\nXXXIt cannot be determined whether the following distributions have changed.\n";
+    print "Please check and list accordingly:\n";
     say "\t$_" for @{$manuallyCheck};
+    print "\n";
   }
 
   my $data = {
@@ -372,8 +380,13 @@ sub do_check {
       next if !$module and $content =~ /\s*xx*\s*/i;
 
       say "Could not parse module name; line is:\n\t$content" and next unless $module;
-      say "$module is not in Module::CoreList; check to see that it is not covered by another section" and next
-        unless $data->{$title}{$module};
+
+      if ( !$data->{$title}{$module} ) {
+        print "$module is not listed as being $title in Module::CoreList.\n";
+        print "Ensure Module::CoreList has been updated and\n";
+        print "check to see that the distribution is not listed under another name.\n\n";
+        next;
+      }
 
       if ( $title eq 'new' ) {
         my ($new) = $content =~ /(\d[^\s]+)\s+has\s+been.*$/m;
-- 
1.8.4.rc3

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2013

From [Unknown Contact. See original ticket]

I apologize for the errors and incorrect documentation. I have fixed the usage example, defaulted to comparing the last two versions, fixed the newlines, and now ignore placeholder items. I have clarified the various messages. The reason for the message about CGI and File​::Fetch is that it they were not listed as being changed in Module​::CoreList for 5.919007, likely because you have not updated Module​::CoreList. I have changed the message to make this clearer.

@p5pRT
Copy link
Author

p5pRT commented Dec 19, 2013

From @steve-m-hay

Many thanks again for your work on this. This is now committed in merge commit 49143bd.

@p5pRT p5pRT closed this as completed Dec 19, 2013
@p5pRT
Copy link
Author

p5pRT commented Dec 19, 2013

@steve-m-hay - Status changed from 'open' to 'resolved'

@p5pRT
Copy link
Author

p5pRT commented Dec 19, 2013

From @rjbs

On Thu Dec 19 09​:55​:51 2013, shay wrote​:

Many thanks again for your work on this. This is now committed in
merge commit 49143bd.

Yes, thanks very much Abir (and Steve)! I have no doubt this will help make my life, and other lives, a little easier over the course of future releases!

--
rjbs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant