Commit | Line | Data |
---|---|---|
c84bdaf6 LC |
1 | eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}' |
2 | & eval 'exec perl -wS "$0" $argv:q' | |
3 | if 0; | |
4 | # Generate a release announcement message. | |
5 | ||
005de2e8 | 6 | my $VERSION = '2012-06-08 06:53'; # UTC |
c84bdaf6 LC |
7 | # The definition above must lie within the first 8 lines in order |
8 | # for the Emacs time-stamp write hook (at end) to update it. | |
9 | # If you change this file with Emacs, please let the write hook | |
10 | # do its job. Otherwise, update this string manually. | |
11 | ||
5e69ceb7 | 12 | # Copyright (C) 2002-2014 Free Software Foundation, Inc. |
c84bdaf6 LC |
13 | |
14 | # This program is free software: you can redistribute it and/or modify | |
15 | # it under the terms of the GNU General Public License as published by | |
16 | # the Free Software Foundation, either version 3 of the License, or | |
17 | # (at your option) any later version. | |
18 | ||
19 | # This program is distributed in the hope that it will be useful, | |
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | # GNU General Public License for more details. | |
23 | ||
24 | # You should have received a copy of the GNU General Public License | |
25 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
26 | ||
27 | # Written by Jim Meyering | |
28 | ||
29 | use strict; | |
30 | ||
31 | use Getopt::Long; | |
32 | use Digest::MD5; | |
005de2e8 LC |
33 | eval { require Digest::SHA; } |
34 | or eval 'use Digest::SHA1'; | |
c84bdaf6 LC |
35 | use POSIX qw(strftime); |
36 | ||
37 | (my $ME = $0) =~ s|.*/||; | |
38 | ||
39 | my %valid_release_types = map {$_ => 1} qw (alpha beta stable); | |
40 | my @archive_suffixes = ('tar.gz', 'tar.bz2', 'tar.lzma', 'tar.xz'); | |
005de2e8 | 41 | my $srcdir = '.'; |
c84bdaf6 LC |
42 | |
43 | sub usage ($) | |
44 | { | |
45 | my ($exit_code) = @_; | |
46 | my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); | |
47 | if ($exit_code != 0) | |
48 | { | |
f0007cad | 49 | print $STREAM "Try '$ME --help' for more information.\n"; |
c84bdaf6 LC |
50 | } |
51 | else | |
52 | { | |
53 | my @types = sort keys %valid_release_types; | |
54 | print $STREAM <<EOF; | |
55 | Usage: $ME [OPTIONS] | |
005de2e8 | 56 | Generate an announcement message. Run this from builddir. |
c84bdaf6 LC |
57 | |
58 | OPTIONS: | |
59 | ||
60 | These options must be specified: | |
61 | ||
62 | --release-type=TYPE TYPE must be one of @types | |
63 | --package-name=PACKAGE_NAME | |
64 | --previous-version=VER | |
65 | --current-version=VER | |
66 | --gpg-key-id=ID The GnuPG ID of the key used to sign the tarballs | |
67 | --url-directory=URL_DIR | |
68 | ||
69 | The following are optional: | |
70 | ||
005de2e8 LC |
71 | --news=NEWS_FILE include the NEWS section about this release |
72 | from this NEWS_FILE; accumulates. | |
73 | --srcdir=DIR where to find the NEWS_FILEs (default: $srcdir) | |
c84bdaf6 LC |
74 | --bootstrap-tools=TOOL_LIST a comma-separated list of tools, e.g., |
75 | autoconf,automake,bison,gnulib | |
76 | --gnulib-version=VERSION report VERSION as the gnulib version, where | |
77 | VERSION is the result of running git describe | |
78 | in the gnulib source directory. | |
79 | required if gnulib is in TOOL_LIST. | |
80 | --no-print-checksums do not emit MD5 or SHA1 checksums | |
81 | --archive-suffix=SUF add SUF to the list of archive suffixes | |
a927b6c1 LC |
82 | --mail-headers=HEADERS a space-separated list of mail headers, e.g., |
83 | To: x\@example.com Cc: y-announce\@example.com,... | |
c84bdaf6 LC |
84 | |
85 | --help display this help and exit | |
86 | --version output version information and exit | |
87 | ||
88 | EOF | |
89 | } | |
90 | exit $exit_code; | |
91 | } | |
92 | ||
93 | ||
94 | =item C<%size> = C<sizes (@file)> | |
95 | ||
96 | Compute the sizes of the C<@file> and return them as a hash. Return | |
97 | C<undef> if one of the computation failed. | |
98 | ||
99 | =cut | |
100 | ||
101 | sub sizes (@) | |
102 | { | |
103 | my (@file) = @_; | |
104 | ||
105 | my $fail = 0; | |
106 | my %res; | |
107 | foreach my $f (@file) | |
108 | { | |
005de2e8 | 109 | my $cmd = "du -h $f"; |
c84bdaf6 LC |
110 | my $t = `$cmd`; |
111 | # FIXME-someday: give a better diagnostic, a la $PROCESS_STATUS | |
112 | $@ | |
005de2e8 | 113 | and (warn "command failed: '$cmd'\n"), $fail = 1; |
c84bdaf6 | 114 | chomp $t; |
005de2e8 | 115 | $t =~ s/^\s*([\d.]+[MkK]).*/${1}B/; |
c84bdaf6 LC |
116 | $res{$f} = $t; |
117 | } | |
118 | return $fail ? undef : %res; | |
119 | } | |
120 | ||
121 | =item C<print_locations ($title, \@url, \%size, @file) | |
122 | ||
123 | Print a section C<$title> dedicated to the list of <@file>, which | |
124 | sizes are stored in C<%size>, and which are available from the C<@url>. | |
125 | ||
126 | =cut | |
127 | ||
128 | sub print_locations ($\@\%@) | |
129 | { | |
130 | my ($title, $url, $size, @file) = @_; | |
131 | print "Here are the $title:\n"; | |
132 | foreach my $url (@{$url}) | |
133 | { | |
134 | for my $file (@file) | |
3d458a81 AW |
135 | { |
136 | print " $url/$file"; | |
137 | print " (", $$size{$file}, ")" | |
138 | if exists $$size{$file}; | |
139 | print "\n"; | |
140 | } | |
c84bdaf6 LC |
141 | } |
142 | print "\n"; | |
143 | } | |
144 | ||
145 | =item C<print_checksums (@file) | |
146 | ||
147 | Print the MD5 and SHA1 signature section for each C<@file>. | |
148 | ||
149 | =cut | |
150 | ||
151 | sub print_checksums (@) | |
152 | { | |
153 | my (@file) = @_; | |
154 | ||
155 | print "Here are the MD5 and SHA1 checksums:\n"; | |
156 | print "\n"; | |
157 | ||
158 | foreach my $meth (qw (md5 sha1)) | |
159 | { | |
160 | foreach my $f (@file) | |
3d458a81 AW |
161 | { |
162 | open IN, '<', $f | |
163 | or die "$ME: $f: cannot open for reading: $!\n"; | |
164 | binmode IN; | |
165 | my $dig = | |
166 | ($meth eq 'md5' | |
167 | ? Digest::MD5->new->addfile(*IN)->hexdigest | |
168 | : Digest::SHA1->new->addfile(*IN)->hexdigest); | |
169 | close IN; | |
170 | print "$dig $f\n"; | |
171 | } | |
c84bdaf6 LC |
172 | } |
173 | print "\n"; | |
174 | } | |
175 | ||
176 | =item C<print_news_deltas ($news_file, $prev_version, $curr_version) | |
177 | ||
178 | Print the section of the NEWS file C<$news_file> addressing changes | |
179 | between versions C<$prev_version> and C<$curr_version>. | |
180 | ||
181 | =cut | |
182 | ||
183 | sub print_news_deltas ($$$) | |
184 | { | |
185 | my ($news_file, $prev_version, $curr_version) = @_; | |
186 | ||
3d458a81 | 187 | my $news_name = $news_file; |
005de2e8 | 188 | $news_name =~ s|^\Q$srcdir\E/||; |
3d458a81 AW |
189 | |
190 | print "\n$news_name\n\n"; | |
c84bdaf6 LC |
191 | |
192 | # Print all lines from $news_file, starting with the first one | |
193 | # that mentions $curr_version up to but not including | |
194 | # the first occurrence of $prev_version. | |
195 | my $in_items; | |
196 | ||
197 | my $re_prefix = qr/(?:\* )?(?:Noteworthy c|Major c|C)(?i:hanges)/; | |
198 | ||
3d458a81 | 199 | my $found_news; |
c84bdaf6 LC |
200 | open NEWS, '<', $news_file |
201 | or die "$ME: $news_file: cannot open for reading: $!\n"; | |
202 | while (defined (my $line = <NEWS>)) | |
203 | { | |
204 | if ( ! $in_items) | |
3d458a81 AW |
205 | { |
206 | # Match lines like these: | |
207 | # * Major changes in release 5.0.1: | |
208 | # * Noteworthy changes in release 6.6 (2006-11-22) [stable] | |
209 | $line =~ /^$re_prefix.*(?:[^\d.]|$)\Q$curr_version\E(?:[^\d.]|$)/o | |
210 | or next; | |
211 | $in_items = 1; | |
212 | print $line; | |
213 | } | |
c84bdaf6 | 214 | else |
3d458a81 AW |
215 | { |
216 | # This regexp must not match version numbers in NEWS items. | |
f0007cad | 217 | # For example, they might well say "introduced in 4.5.5", |
3d458a81 AW |
218 | # and we don't want that to match. |
219 | $line =~ /^$re_prefix.*(?:[^\d.]|$)\Q$prev_version\E(?:[^\d.]|$)/o | |
220 | and last; | |
221 | print $line; | |
222 | $line =~ /\S/ | |
223 | and $found_news = 1; | |
224 | } | |
c84bdaf6 LC |
225 | } |
226 | close NEWS; | |
227 | ||
228 | $in_items | |
f0007cad | 229 | or die "$ME: $news_file: no matching lines for '$curr_version'\n"; |
3d458a81 | 230 | $found_news |
f0007cad | 231 | or die "$ME: $news_file: no news item found for '$curr_version'\n"; |
c84bdaf6 LC |
232 | } |
233 | ||
234 | sub print_changelog_deltas ($$) | |
235 | { | |
236 | my ($package_name, $prev_version) = @_; | |
237 | ||
238 | # Print new ChangeLog entries. | |
239 | ||
240 | # First find all CVS-controlled ChangeLog files. | |
241 | use File::Find; | |
242 | my @changelog; | |
243 | find ({wanted => sub {$_ eq 'ChangeLog' && -d 'CVS' | |
3d458a81 AW |
244 | and push @changelog, $File::Find::name}}, |
245 | '.'); | |
c84bdaf6 LC |
246 | |
247 | # If there are no ChangeLog files, we're done. | |
248 | @changelog | |
249 | or return; | |
250 | my %changelog = map {$_ => 1} @changelog; | |
251 | ||
252 | # Reorder the list of files so that if there are ChangeLog | |
253 | # files in the specified directories, they're listed first, | |
254 | # in this order: | |
255 | my @dir = qw ( . src lib m4 config doc ); | |
256 | ||
257 | # A typical @changelog array might look like this: | |
258 | # ./ChangeLog | |
259 | # ./po/ChangeLog | |
260 | # ./m4/ChangeLog | |
261 | # ./lib/ChangeLog | |
262 | # ./doc/ChangeLog | |
263 | # ./config/ChangeLog | |
264 | my @reordered; | |
265 | foreach my $d (@dir) | |
266 | { | |
267 | my $dot_slash = $d eq '.' ? $d : "./$d"; | |
268 | my $target = "$dot_slash/ChangeLog"; | |
269 | delete $changelog{$target} | |
3d458a81 | 270 | and push @reordered, $target; |
c84bdaf6 LC |
271 | } |
272 | ||
273 | # Append any remaining ChangeLog files. | |
274 | push @reordered, sort keys %changelog; | |
275 | ||
f0007cad | 276 | # Remove leading './'. |
c84bdaf6 LC |
277 | @reordered = map { s!^\./!!; $_ } @reordered; |
278 | ||
279 | print "\nChangeLog entries:\n\n"; | |
280 | # print join ("\n", @reordered), "\n"; | |
281 | ||
282 | $prev_version =~ s/\./_/g; | |
283 | my $prev_cvs_tag = "\U$package_name\E-$prev_version"; | |
284 | ||
285 | my $cmd = "cvs -n diff -u -r$prev_cvs_tag -rHEAD @reordered"; | |
286 | open DIFF, '-|', $cmd | |
f0007cad | 287 | or die "$ME: cannot run '$cmd': $!\n"; |
c84bdaf6 | 288 | # Print two types of lines, making minor changes: |
f0007cad | 289 | # Lines starting with '+++ ', e.g., |
c84bdaf6 | 290 | # +++ ChangeLog 22 Feb 2003 16:52:51 -0000 1.247 |
f0007cad | 291 | # and those starting with '+'. |
c84bdaf6 LC |
292 | # Don't print the others. |
293 | my $prev_printed_line_empty = 1; | |
294 | while (defined (my $line = <DIFF>)) | |
295 | { | |
296 | if ($line =~ /^\+\+\+ /) | |
3d458a81 AW |
297 | { |
298 | my $separator = "*"x70 ."\n"; | |
299 | $line =~ s///; | |
300 | $line =~ s/\s.*//; | |
301 | $prev_printed_line_empty | |
302 | or print "\n"; | |
303 | print $separator, $line, $separator; | |
304 | } | |
c84bdaf6 | 305 | elsif ($line =~ /^\+/) |
3d458a81 AW |
306 | { |
307 | $line =~ s///; | |
308 | print $line; | |
309 | $prev_printed_line_empty = ($line =~ /^$/); | |
310 | } | |
c84bdaf6 LC |
311 | } |
312 | close DIFF; | |
313 | ||
314 | # The exit code should be 1. | |
315 | # Allow in case there are no modified ChangeLog entries. | |
316 | $? == 256 || $? == 128 | |
005de2e8 | 317 | or warn "warning: '$cmd' had unexpected exit code or signal ($?)\n"; |
c84bdaf6 LC |
318 | } |
319 | ||
320 | sub get_tool_versions ($$) | |
321 | { | |
322 | my ($tool_list, $gnulib_version) = @_; | |
323 | @$tool_list | |
324 | or return (); | |
325 | ||
326 | my $fail; | |
327 | my @tool_version_pair; | |
328 | foreach my $t (@$tool_list) | |
329 | { | |
330 | if ($t eq 'gnulib') | |
3d458a81 AW |
331 | { |
332 | push @tool_version_pair, ucfirst $t . ' ' . $gnulib_version; | |
333 | next; | |
334 | } | |
c84bdaf6 | 335 | # Assume that the last "word" on the first line of |
f0007cad | 336 | # 'tool --version' output is the version string. |
c84bdaf6 LC |
337 | my ($first_line, undef) = split ("\n", `$t --version`); |
338 | if ($first_line =~ /.* (\d[\w.-]+)$/) | |
3d458a81 AW |
339 | { |
340 | $t = ucfirst $t; | |
341 | push @tool_version_pair, "$t $1"; | |
342 | } | |
c84bdaf6 | 343 | else |
3d458a81 AW |
344 | { |
345 | defined $first_line | |
346 | and $first_line = ''; | |
005de2e8 | 347 | warn "$t: unexpected --version output\n:$first_line"; |
3d458a81 AW |
348 | $fail = 1; |
349 | } | |
c84bdaf6 LC |
350 | } |
351 | ||
352 | $fail | |
353 | and exit 1; | |
354 | ||
355 | return @tool_version_pair; | |
356 | } | |
357 | ||
358 | { | |
359 | # Neutralize the locale, so that, for instance, "du" does not | |
360 | # issue "1,2" instead of "1.2", what confuses our regexps. | |
361 | $ENV{LC_ALL} = "C"; | |
362 | ||
a927b6c1 | 363 | my $mail_headers; |
c84bdaf6 LC |
364 | my $release_type; |
365 | my $package_name; | |
366 | my $prev_version; | |
367 | my $curr_version; | |
368 | my $gpg_key_id; | |
369 | my @url_dir_list; | |
370 | my @news_file; | |
371 | my $bootstrap_tools; | |
372 | my $gnulib_version; | |
373 | my $print_checksums_p = 1; | |
374 | ||
005de2e8 LC |
375 | # Reformat the warnings before displaying them. |
376 | local $SIG{__WARN__} = sub | |
377 | { | |
378 | my ($msg) = @_; | |
379 | # Warnings from GetOptions. | |
380 | $msg =~ s/Option (\w)/option --$1/; | |
381 | warn "$ME: $msg"; | |
382 | }; | |
383 | ||
c84bdaf6 LC |
384 | GetOptions |
385 | ( | |
a927b6c1 | 386 | 'mail-headers=s' => \$mail_headers, |
c84bdaf6 LC |
387 | 'release-type=s' => \$release_type, |
388 | 'package-name=s' => \$package_name, | |
389 | 'previous-version=s' => \$prev_version, | |
390 | 'current-version=s' => \$curr_version, | |
391 | 'gpg-key-id=s' => \$gpg_key_id, | |
392 | 'url-directory=s' => \@url_dir_list, | |
393 | 'news=s' => \@news_file, | |
005de2e8 | 394 | 'srcdir=s' => \$srcdir, |
c84bdaf6 LC |
395 | 'bootstrap-tools=s' => \$bootstrap_tools, |
396 | 'gnulib-version=s' => \$gnulib_version, | |
397 | 'print-checksums!' => \$print_checksums_p, | |
398 | 'archive-suffix=s' => \@archive_suffixes, | |
399 | ||
400 | help => sub { usage 0 }, | |
401 | version => sub { print "$ME version $VERSION\n"; exit }, | |
402 | ) or usage 1; | |
403 | ||
404 | my $fail = 0; | |
005de2e8 | 405 | # Ensure that each required option is specified. |
c84bdaf6 | 406 | $release_type |
005de2e8 | 407 | or (warn "release type not specified\n"), $fail = 1; |
c84bdaf6 | 408 | $package_name |
005de2e8 | 409 | or (warn "package name not specified\n"), $fail = 1; |
c84bdaf6 | 410 | $prev_version |
005de2e8 | 411 | or (warn "previous version string not specified\n"), $fail = 1; |
c84bdaf6 | 412 | $curr_version |
005de2e8 | 413 | or (warn "current version string not specified\n"), $fail = 1; |
c84bdaf6 | 414 | $gpg_key_id |
005de2e8 | 415 | or (warn "GnuPG key ID not specified\n"), $fail = 1; |
c84bdaf6 | 416 | @url_dir_list |
005de2e8 | 417 | or (warn "URL directory name(s) not specified\n"), $fail = 1; |
c84bdaf6 LC |
418 | |
419 | my @tool_list = split ',', $bootstrap_tools; | |
420 | ||
421 | grep (/^gnulib$/, @tool_list) ^ defined $gnulib_version | |
005de2e8 | 422 | and (warn "when specifying gnulib as a tool, you must also specify\n" |
3d458a81 AW |
423 | . "--gnulib-version=V, where V is the result of running git describe\n" |
424 | . "in the gnulib source directory.\n"), $fail = 1; | |
c84bdaf6 LC |
425 | |
426 | exists $valid_release_types{$release_type} | |
005de2e8 | 427 | or (warn "'$release_type': invalid release type\n"), $fail = 1; |
c84bdaf6 LC |
428 | |
429 | @ARGV | |
005de2e8 | 430 | and (warn "too many arguments:\n", join ("\n", @ARGV), "\n"), |
c84bdaf6 LC |
431 | $fail = 1; |
432 | $fail | |
433 | and usage 1; | |
434 | ||
435 | my $my_distdir = "$package_name-$curr_version"; | |
436 | ||
437 | my $xd = "$package_name-$prev_version-$curr_version.xdelta"; | |
438 | ||
439 | my @candidates = map { "$my_distdir.$_" } @archive_suffixes; | |
440 | my @tarballs = grep {-f $_} @candidates; | |
441 | ||
442 | @tarballs | |
443 | or die "$ME: none of " . join(', ', @candidates) . " were found\n"; | |
444 | my @sizable = @tarballs; | |
445 | -f $xd | |
446 | and push @sizable, $xd; | |
447 | my %size = sizes (@sizable); | |
448 | %size | |
449 | or exit 1; | |
450 | ||
a927b6c1 LC |
451 | my $headers = ''; |
452 | if (defined $mail_headers) | |
453 | { | |
454 | ($headers = $mail_headers) =~ s/\s+(\S+:)/\n$1/g; | |
455 | $headers .= "\n"; | |
456 | } | |
457 | ||
c84bdaf6 LC |
458 | # The markup is escaped as <\# so that when this script is sent by |
459 | # mail (or part of a diff), Gnus is not triggered. | |
460 | print <<EOF; | |
461 | ||
a927b6c1 | 462 | ${headers}Subject: $my_distdir released [$release_type] |
c84bdaf6 LC |
463 | |
464 | <\#secure method=pgpmime mode=sign> | |
465 | ||
466 | FIXME: put comments here | |
467 | ||
468 | EOF | |
469 | ||
7f1ea859 LC |
470 | if (@url_dir_list == 1 && @tarballs == 1) |
471 | { | |
472 | # When there's only one tarball and one URL, use a more concise form. | |
473 | my $m = "$url_dir_list[0]/$tarballs[0]"; | |
474 | print "Here are the compressed sources and a GPG detached signature[*]:\n" | |
475 | . " $m\n" | |
476 | . " $m.sig\n\n"; | |
477 | } | |
478 | else | |
479 | { | |
480 | print_locations ("compressed sources", @url_dir_list, %size, @tarballs); | |
481 | -f $xd | |
482 | and print_locations ("xdelta diffs (useful? if so, " | |
483 | . "please tell bug-gnulib\@gnu.org)", | |
484 | @url_dir_list, %size, $xd); | |
485 | my @sig_files = map { "$_.sig" } @tarballs; | |
486 | print_locations ("GPG detached signatures[*]", @url_dir_list, %size, | |
487 | @sig_files); | |
488 | } | |
489 | ||
414e4441 LC |
490 | if ($url_dir_list[0] =~ "gnu\.org") |
491 | { | |
7f1ea859 LC |
492 | print "Use a mirror for higher download bandwidth:\n"; |
493 | if (@tarballs == 1 && $url_dir_list[0] =~ m!http://ftp\.gnu\.org/gnu/!) | |
494 | { | |
495 | (my $m = "$url_dir_list[0]/$tarballs[0]") | |
496 | =~ s!http://ftp\.gnu\.org/gnu/!http://ftpmirror\.gnu\.org/!; | |
497 | print " $m\n" | |
498 | . " $m.sig\n\n"; | |
499 | ||
500 | } | |
501 | else | |
502 | { | |
503 | print " http://www.gnu.org/order/ftp.html\n\n"; | |
504 | } | |
414e4441 | 505 | } |
c84bdaf6 LC |
506 | |
507 | $print_checksums_p | |
508 | and print_checksums (@sizable); | |
509 | ||
510 | print <<EOF; | |
7f1ea859 LC |
511 | [*] Use a .sig file to verify that the corresponding file (without the |
512 | .sig suffix) is intact. First, be sure to download both the .sig file | |
513 | and the corresponding tarball. Then, run a command like this: | |
c84bdaf6 LC |
514 | |
515 | gpg --verify $tarballs[0].sig | |
516 | ||
517 | If that command fails because you don't have the required public key, | |
518 | then run this command to import it: | |
519 | ||
520 | gpg --keyserver keys.gnupg.net --recv-keys $gpg_key_id | |
521 | ||
f0007cad | 522 | and rerun the 'gpg --verify' command. |
c84bdaf6 LC |
523 | EOF |
524 | ||
525 | my @tool_versions = get_tool_versions (\@tool_list, $gnulib_version); | |
526 | @tool_versions | |
527 | and print "\nThis release was bootstrapped with the following tools:", | |
528 | join ('', map {"\n $_"} @tool_versions), "\n"; | |
529 | ||
530 | print_news_deltas ($_, $prev_version, $curr_version) | |
531 | foreach @news_file; | |
532 | ||
533 | $release_type eq 'stable' | |
534 | or print_changelog_deltas ($package_name, $prev_version); | |
535 | ||
536 | exit 0; | |
537 | } | |
538 | ||
539 | ### Setup "GNU" style for perl-mode and cperl-mode. | |
540 | ## Local Variables: | |
541 | ## mode: perl | |
542 | ## perl-indent-level: 2 | |
543 | ## perl-continued-statement-offset: 2 | |
544 | ## perl-continued-brace-offset: 0 | |
545 | ## perl-brace-offset: 0 | |
546 | ## perl-brace-imaginary-offset: 0 | |
547 | ## perl-label-offset: -2 | |
548 | ## perl-extra-newline-before-brace: t | |
549 | ## perl-merge-trailing-else: nil | |
550 | ## eval: (add-hook 'write-file-hooks 'time-stamp) | |
551 | ## time-stamp-start: "my $VERSION = '" | |
552 | ## time-stamp-format: "%:y-%02m-%02d %02H:%02M" | |
553 | ## time-stamp-time-zone: "UTC" | |
554 | ## time-stamp-end: "'; # UTC" | |
555 | ## End: |