Skip to content

Commit 3b8063f

Browse files
bookericherman
authored andcommitted
add a script that merges all devel deltas into the final one
For now, the script simply copies the content of each relevant section into the same one in the master document (pod/perldelta.pod). It will die when encountering an unexpected =head1 header.
1 parent 3f46175 commit 3b8063f

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed

Porting/merge-deltas.pl

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#!perl
2+
use v5.36;
3+
use Pod::Simple::SimpleTree;
4+
5+
# POD NAVIGATION SUBROUTINES
6+
7+
sub header_pos ( $tree, $level, $title, $pos = 0 ) {
8+
while ( $pos < @$tree ) {
9+
next
10+
unless ref( $tree->[$pos] ) eq 'ARRAY'
11+
&& $tree->[$pos][0] eq "head$level";
12+
return $pos if $tree->[$pos][2] eq $title;
13+
}
14+
continue { $pos++ }
15+
return; # not found
16+
}
17+
18+
sub next_header_pos ( $tree, $level, $pos = 0 ) {
19+
$pos++;
20+
while ( $pos < @$tree ) {
21+
next
22+
unless ref( $tree->[$pos] ) eq 'ARRAY';
23+
next unless $tree->[$pos][0] =~ /\Ahead([1-4])\z/;
24+
next if $1 > $level;
25+
last if $1 < $level;
26+
return $pos;
27+
}
28+
continue { $pos++ }
29+
return; # not found
30+
}
31+
32+
sub find_pos_in ( $master, $delta, $title ) {
33+
return
34+
map header_pos( $_, 1, $title ),
35+
$master, $delta;
36+
}
37+
38+
# POD GENERATION SUBROUTINES
39+
40+
# NOTE: A Pod::Simple::SimpleTree "tree" is really just a list of
41+
# directives. The only parts that are really tree-like / recursive are
42+
# the list directives, and pod formatting codes.
43+
44+
sub as_pod ( $tree ) {
45+
return $tree unless ref $tree; # simple string
46+
state $handler = {
47+
Document => sub ( $name, $attr, @nodes ) {
48+
return map( as_pod($_), @nodes), "=cut\n";
49+
},
50+
Para => sub ( $name, $attr, @nodes ) {
51+
return map( as_pod($_), @nodes ), "\n\n";
52+
},
53+
Verbatim => sub ( $name, $attr, @nodes ) {
54+
return map( as_pod($_), @nodes ), "\n\n";
55+
},
56+
X => sub ( $name, $attr, @nodes ) {
57+
my ( $open, $spacer, $close ) =
58+
$attr->{'~bracket_count'}
59+
? (
60+
'<' x $attr->{'~bracket_count'},
61+
' ',
62+
'>' x $attr->{'~bracket_count'}
63+
)
64+
: ( '<', '', '>' );
65+
return "$name$open$spacer",
66+
map( as_pod($_), @nodes ),
67+
"$spacer$close";
68+
},
69+
L => sub ( $name, $attr, @nodes ) {
70+
return "$name<$attr->{raw}>";
71+
},
72+
# TODO: =begin / =for
73+
over => sub ( $name, $attr, @nodes ) {
74+
return "=over",
75+
$attr->{'~orig_content'} && " $attr->{'~orig_content'}", "\n\n",
76+
map( as_pod($_), @nodes ), "=back\n\n";
77+
},
78+
item => sub ( $name, $attr, @nodes ) {
79+
return "=item ",
80+
$attr->{'~orig_content'} ? "$attr->{'~orig_content'}\n\n" : '',
81+
map( as_pod($_), @nodes ), "\n\n";
82+
},
83+
'' => sub ( $name, $attr, @nodes ) {
84+
return "=$name", @nodes && ' ', map( as_pod($_), @nodes ), "\n\n";
85+
},
86+
};
87+
my ( $directive, $attr, @nodes ) = @$tree;
88+
my $name =
89+
exists $handler->{$directive} ? $directive
90+
: $directive =~ /\Aover-/ ? 'over'
91+
: $directive =~ /\Aitem-/ ? 'item'
92+
: length($directive) == 1 ? 'X'
93+
: '';
94+
return join '', $handler->{$name}->( $directive, $attr, @nodes );
95+
}
96+
97+
# CONTENT MANIPULATION SUBROUTINES
98+
99+
# copy the whole section content
100+
sub copy_section ( $master, $title, $delta ) {
101+
my ( $master_pos, $delta_pos ) = find_pos_in( $master, $delta, $title );
102+
103+
# find the end of the section in the delta
104+
my $end_pos = next_header_pos( $delta, 1, $delta_pos ) - 1;
105+
106+
# inject the whole section from the delta
107+
splice @$master, $master_pos + 1,
108+
0, $delta->@[ $delta_pos + 1 .. $end_pos ];
109+
}
110+
111+
# map each section to an action
112+
my %ACTION_FOR = (
113+
'NAME' => 'skip',
114+
'DESCRIPTION' => 'skip',
115+
'Notice' => 'copy',
116+
'Core Enhancements' => 'copy',
117+
'Security' => 'copy',
118+
'Incompatible Changes' => 'copy',
119+
'Deprecations' => 'copy',
120+
'Performance Enhancements' => 'copy',
121+
'Modules and Pragmata' => 'skip',
122+
'Documentation' => 'copy',
123+
'Diagnostics' => 'copy',
124+
'Utility Changes' => 'copy',
125+
'Configuration and Compilation' => 'copy',
126+
'Testing' => 'copy',
127+
'Platform Support' => 'copy',
128+
'Internal Changes' => 'copy',
129+
'Selected Bug Fixes' => 'copy',
130+
'Known Problems' => 'copy',
131+
'Errata From Previous Releases' => 'copy',
132+
'Obituary' => 'copy',
133+
'Acknowledgements' => 'skip',
134+
'Reporting Bugs' => 'skip',
135+
'Give Thanks' => 'skip',
136+
'SEE ALSO' => 'skip',
137+
);
138+
139+
# HELPER SUBROUTINES
140+
141+
# Note: the parser can only be used *once* per file
142+
sub tree_for ($string) {
143+
my $parser = Pod::Simple::SimpleTree->new;
144+
$parser->keep_encoding_directive(1);
145+
$parser->preserve_whitespace(1);
146+
$parser->accept_targets('*'); # for & begin/end
147+
$parser->_output_is_for_JustPod(1); # for ~bracket_count
148+
$parser->parse_string_document($string)->root;
149+
}
150+
151+
sub merge_into ( $master, $delta, $file ) {
152+
153+
# loop over the =head1 sections
154+
for my $title (
155+
map $_->[2], # grab the title
156+
grep ref eq 'ARRAY' && $_->[0] eq 'head1', # of the =head1
157+
@$delta # of the delta
158+
)
159+
{
160+
die "Unexpected section '=head1 $title' in $file\n"
161+
unless exists $ACTION_FOR{$title};
162+
next if $ACTION_FOR{$title} eq 'skip';
163+
copy_section( $master, $title, $delta );
164+
}
165+
}
166+
167+
sub slurp { return do { local @ARGV = @_; local $/; <> } }
168+
169+
# MAIN PROGRAM
170+
171+
sub main (@argv) {
172+
173+
# compute the version
174+
my ($version) = `git describe` =~ /\Av(5\.[0-9]+)/g;
175+
die "$version does not look like a devel Perl version\n"
176+
unless $version =~ /\A5\.[0-9]{1,2}[13579]\z/;
177+
178+
# the current, unfinished, delta will be used
179+
# as the master to produce the final document
180+
my $final_delta = 'pod/perldelta.pod';
181+
my $master = tree_for( slurp($final_delta) );
182+
183+
# loop over all the development deltas
184+
my $tag_devel = $version =~ tr/.//dr;
185+
for my $file_tree (
186+
map [ $_->[0], tree_for( slurp( $_->[0] ) ) ],
187+
sort { $b->[1] <=> $a->[1] }
188+
map [ $_, m{pod/perl$tag_devel([0-9]+)delta\.pod}g ],
189+
glob "pod/perl$tag_devel*delta.pod"
190+
)
191+
{
192+
my ( $file, $delta ) = @$file_tree;
193+
merge_into( $master, $delta, $file );
194+
}
195+
196+
# save the result
197+
open my $fh, '>', $final_delta
198+
or die "Can't open $final_delta for writing: $!";
199+
print $fh as_pod($master);
200+
201+
return 0;
202+
}
203+
204+
# make it easier to test
205+
exit main( @ARGV ) unless caller;

0 commit comments

Comments
 (0)