summaryrefslogtreecommitdiff
path: root/dist/Module-CoreList/corelist
blob: f17d9fff60ae5398fea796aca209f2d62ddd9c96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
#!/usr/bin/perl

=head1 NAME

corelist - a commandline frontend to Module::CoreList

=head1 DESCRIPTION

See L<Module::CoreList> for one.

=head1 SYNOPSIS

   corelist -v
   corelist [-a|-d] <ModuleName> | /<ModuleRegex>/ [<ModuleVersion>] ...
   corelist [-v <PerlVersion>] [ <ModuleName> | /<ModuleRegex>/ ] ...
   corelist [-r <PerlVersion>] ...
   corelist --utils [-d] <UtilityName> [<UtilityName>] ...
   corelist --utils -v <PerlVersion>
   corelist --feature <FeatureName> [<FeatureName>] ...
   corelist --diff PerlVersion PerlVersion
   corelist --upstream <ModuleName>

=head1 OPTIONS

=over

=item -a

lists all versions of the given module (or the matching modules, in case you
used a module regexp) in the perls Module::CoreList knows about.

    corelist -a Unicode

    Unicode was first released with perl v5.6.2
      v5.6.2     3.0.1
      v5.8.0     3.2.0
      v5.8.1     4.0.0
      v5.8.2     4.0.0
      v5.8.3     4.0.0
      v5.8.4     4.0.1
      v5.8.5     4.0.1
      v5.8.6     4.0.1
      v5.8.7     4.1.0
      v5.8.8     4.1.0
      v5.8.9     5.1.0
      v5.9.0     4.0.0
      v5.9.1     4.0.0
      v5.9.2     4.0.1
      v5.9.3     4.1.0
      v5.9.4     4.1.0
      v5.9.5     5.0.0
      v5.10.0    5.0.0
      v5.10.1    5.1.0
      v5.11.0    5.1.0
      v5.11.1    5.1.0
      v5.11.2    5.1.0
      v5.11.3    5.2.0
      v5.11.4    5.2.0
      v5.11.5    5.2.0
      v5.12.0    5.2.0
      v5.12.1    5.2.0
      v5.12.2    5.2.0
      v5.12.3    5.2.0
      v5.12.4    5.2.0
      v5.13.0    5.2.0
      v5.13.1    5.2.0
      v5.13.2    5.2.0
      v5.13.3    5.2.0
      v5.13.4    5.2.0
      v5.13.5    5.2.0
      v5.13.6    5.2.0
      v5.13.7    6.0.0
      v5.13.8    6.0.0
      v5.13.9    6.0.0
      v5.13.10   6.0.0
      v5.13.11   6.0.0
      v5.14.0    6.0.0
      v5.14.1    6.0.0
      v5.15.0    6.0.0

=item -d

finds the first perl version where a module has been released by
date, and not by version number (as is the default).

=item --diff

Given two versions of perl, this prints a human-readable table of all module
changes between the two.  The output format may change in the future, and is
meant for I<humans>, not programs.  For programs, use the L<Module::CoreList>
API.

=item -? or -help

help! help! help! to see more help, try --man.

=item -man

all of the help

=item -v

lists all of the perl release versions we got the CoreList for.

If you pass a version argument (value of C<$]>, like C<5.00503> or C<5.008008>),
you get a list of all the modules and their respective versions.
(If you have the C<version> module, you can also use new-style version numbers,
like C<5.8.8>.)

In module filtering context, it can be used as Perl version filter.

=item -r

lists all of the perl releases and when they were released

If you pass a perl version you get the release date for that version only.

=item --utils

lists the first version of perl each named utility program was released with

May be used with -d to modify the first release criteria.

If used with -v <version> then all utilities released with that version of perl
are listed, and any utility programs named on the command line are ignored.

=item --feature, -f

lists the first version bundle of each named feature given

=item --upstream, -u

Shows if the given module is primarily maintained in perl core or on CPAN
and bug tracker URL.

=back

As a special case, if you specify the module name C<Unicode>, you'll get
the version number of the Unicode Character Database bundled with the
requested perl versions.

=cut

BEGIN { pop @INC if $INC[-1] eq '.' }
use Module::CoreList;
use Getopt::Long qw(:config no_ignore_case);
use Pod::Usage;
use strict;
use warnings;
use List::Util qw/maxstr/;

my %Opts;

GetOptions(
    \%Opts,
    qw[ help|?! man! r|release:s v|version:s a! d diff|D utils feature|f u|upstream ]
);

pod2usage(1) if $Opts{help};
pod2usage(-verbose=>2) if $Opts{man};

if(exists $Opts{r} ){
    if ( !$Opts{r} ) {
        print "\nModule::CoreList has release info for the following perl versions:\n";
        my $versions = { };
        my $max_ver_len = max_mod_len(\%Module::CoreList::released);
        for my $ver ( grep !/0[01]0$/, sort keys %Module::CoreList::released ) {
          printf "%-${max_ver_len}s    %s\n", format_perl_version($ver), $Module::CoreList::released{$ver};
        }
        print "\n";
        exit 0;
    }

    my $num_r = numify_version( $Opts{r} );
    my $version_hash = Module::CoreList->find_version($num_r);

    if( !$version_hash ) {
        print "\nModule::CoreList has no info on perl $Opts{r}\n\n";
        exit 1;
    }

    printf "Perl %s was released on %s\n\n", format_perl_version($num_r), $Module::CoreList::released{$num_r};
    exit 0;
}

if(exists $Opts{v} ){
    if( !$Opts{v} ) {
        print "\nModule::CoreList has info on the following perl versions:\n";
        print format_perl_version($_)."\n" for grep !/0[01]0$/, sort keys %Module::CoreList::version;
        print "\n";
        exit 0;
    }

    my $num_v = numify_version( $Opts{v} );

    if ($Opts{utils}) {
        utilities_in_version($num_v);
        exit 0;
    }

    my $version_hash = Module::CoreList->find_version($num_v);

    if( !$version_hash ) {
        print "\nModule::CoreList has no info on perl $Opts{v}\n\n";
        exit 1;
    }

    if ( !@ARGV ) {
	print "\nThe following modules were in perl $Opts{v} CORE\n";
	my $max_mod_len = max_mod_len($version_hash);
	for my $mod ( sort keys %$version_hash ) {
	    printf "%-${max_mod_len}s  %s\n", $mod, $version_hash->{$mod} || "";
	}
	print "\n";
	exit 0;
    }
}

if ($Opts{diff}) {
    if(@ARGV != 2) {
        die "\nprovide exactly two perl core versions to diff with --diff\n";
    }

    my ($old_ver, $new_ver) = @ARGV;

    my $old = numify_version($old_ver);
    if ( !Module::CoreList->find_version($old) ) {
        print "\nModule::CoreList has no info on perl $old_ver\n\n";
        exit 1;
    }
    my $new = numify_version($new_ver);
    if ( !Module::CoreList->find_version($new) ) {
        print "\nModule::CoreList has no info on perl $new_ver\n\n";
        exit 1;
    }

    my %diff = Module::CoreList::changes_between($old, $new);

    for my $lib (sort keys %diff) {
      my $diff = $diff{$lib};

      my $was = ! exists  $diff->{left} ? '(absent)'
              : ! defined $diff->{left} ? '(undef)'
              :                          $diff->{left};

      my $now = ! exists  $diff->{right} ? '(absent)'
              : ! defined $diff->{right} ? '(undef)'
              :                          $diff->{right};

        printf "%-35s %10s %10s\n", $lib, $was, $now;
    }
    exit(0);
}

if ($Opts{utils}) {
    die "\n--utils only available with perl v5.19.1 or greater\n"
        if $] < 5.019001;

    die "\nprovide at least one utility name to --utils\n"
        unless @ARGV;

    warn "\n-a has no effect when --utils is used\n"                 if $Opts{a};
    warn "\n--diff has no effect when --utils is used\n"             if $Opts{diff};
    warn "\n--upstream, or -u, has no effect when --utils is used\n" if $Opts{u};

    my $when = maxstr(values %Module::CoreList::released);
    print "\n","Data for $when\n";

    utility_version($_) for @ARGV;

    exit(0);
}

if ($Opts{feature}) {
    die "\n--feature is only available with perl v5.16.0 or greater\n"
      if $] < 5.016;

    die "\nprovide at least one feature name to --feature\n"
        unless @ARGV;

    no warnings 'once';
    require feature;

    my %feature2version;
    my @bundles =  map { $_->[0] }
                  sort { $b->[1] <=> $a->[1] }
                   map { [$_, numify_version($_)] }
                  grep { not /[^0-9.]/ }
                  keys %feature::feature_bundle;

    for my $version (@bundles) {
        $feature2version{$_} = $version =~ /^\d\.\d+$/ ? "$version.0" : $version
            for @{ $feature::feature_bundle{$version} };
    }

    # allow internal feature names, just in case someone gives us __SUB__
    # instead of current_sub.
    while (my ($name, $internal) = each %feature::feature) {
        $internal =~ s/^feature_//;
        $feature2version{$internal} = $feature2version{$name}
            if $feature2version{$name};
    }

    my $when = maxstr(values %Module::CoreList::released);
    print "\n","Data for $when\n";

    for my $feature (@ARGV) {
        print "feature \"$feature\" ",
            exists $feature2version{$feature}
                ? "was first released with the perl "
                  . format_perl_version(numify_version($feature2version{$feature}))
                  . " feature bundle\n"
                : "doesn't exist (or so I think)\n";
    }
    exit(0);
}

if ( !@ARGV ) {
    pod2usage(0);
}

while (@ARGV) {
	my ($mod, $ver);
	if ($ARGV[0] =~ /=/) {
	    ($mod, $ver) = split /=/, shift @ARGV;
	} else {
	    $mod = shift @ARGV;
	    $ver = (@ARGV && $ARGV[0] =~ /^\d/) ? shift @ARGV : "";
	}

	if ($mod !~ m|^/(.*)/([imosx]*)$|) { # not a regex
	    module_version($mod,$ver);
	} else {
	    my $re;
	    eval { $re = $2 ? qr/(?$2)($1)/ : qr/$1/; }; # trap exceptions while building regex
	    if ($@) {
		# regex errors are usually like 'Quantifier follow nothing in regex; marked by ...'
		# then we drop text after ';' to shorten message
		my $errmsg = $@ =~ /(.*);/ ? $1 : $@;
		warn "\n$mod  is a bad regex: $errmsg\n";
		next;
	    }
	    my @mod = Module::CoreList->find_modules($re);
	    if (@mod) {
		module_version($_, $ver) for @mod;
	    } else {
		$ver |= '';
		print "\n$mod $ver has no match in CORE (or so I think)\n";
	    }

	}
}

exit();

sub module_version {
    my($mod,$ver) = @_;

    if ( $Opts{v} ) {
	my $numeric_v = numify_version($Opts{v});
	my $version_hash = Module::CoreList->find_version($numeric_v);
	if ($version_hash) {
	    print $mod, " ", $version_hash->{$mod} || 'undef', "\n";
	    return;
	}
	else { die "Shouldn't happen" }
    }

    my $ret = $Opts{d}
	? Module::CoreList->first_release_by_date(@_)
	: Module::CoreList->first_release(@_);
    my $msg = $mod;
    $msg .= " $ver" if $ver;

    my $rem = $Opts{d}
	? Module::CoreList->removed_from_by_date($mod)
	: Module::CoreList->removed_from($mod);

	my $when = maxstr(values %Module::CoreList::released);
    print "\n","Data for $when\n";

    if( defined $ret ) {
        my $deprecated = Module::CoreList->deprecated_in($mod);
        $msg .= " was ";
        $msg .= "first " unless $ver;
        $msg .= "released with perl " . format_perl_version($ret);
        $msg .= ( $rem ? ',' : ' and' ) . " deprecated (will be CPAN-only) in " . format_perl_version($deprecated) if $deprecated;
        $msg .= " and removed from " . format_perl_version($rem) if $rem;
    } else {
        $msg .= " was not in CORE (or so I think)";
    }

    print $msg,"\n";

    if( defined $ret and exists $Opts{u} ) {
        my $upstream = $Module::CoreList::upstream{$mod};
        $upstream = 'undef' unless $upstream;
        print "upstream: $upstream\n";
        if ( $upstream ne 'blead' ) {
            my $bugtracker = $Module::CoreList::bug_tracker{$mod};
            $bugtracker = 'unknown' unless $bugtracker;
            print "bug tracker: $bugtracker\n";
        }
    }

    if(defined $ret and exists $Opts{a} and $Opts{a}){
        display_a($mod);
    }
}

sub utility_version {
    my ($utility) = @_;

    require Module::CoreList::Utils;

    my $released = $Opts{d}
        ? Module::CoreList::Utils->first_release_by_date($utility)
        : Module::CoreList::Utils->first_release($utility);

    my $removed = $Opts{d}
        ? Module::CoreList::Utils->removed_from_by_date($utility)
        : Module::CoreList::Utils->removed_from($utility);

    if ($released) {
        print "$utility was first released with perl ", format_perl_version($released);
        print " and later removed in ", format_perl_version($removed)
            if $removed;
        print "\n";
    } else {
        print "$utility was not in CORE (or so I think)\n";
    }
}

sub utilities_in_version {
    my ($version) = @_;

    require Module::CoreList::Utils;

    my @utilities = Module::CoreList::Utils->utilities($version);

    if (not @utilities) {
        print "\nModule::CoreList::Utils has no info on perl $version\n\n";
        exit 1;
    }

    print "\nThe following utilities were in perl ",
        format_perl_version($version), " CORE\n";
    print "$_\n" for sort { lc($a) cmp lc($b) } @utilities;
    print "\n";
}


sub max_mod_len {
    my $versions = shift;
    my $max = 0;
    for my $mod (keys %$versions) {
        $max = max($max, length $mod);
    }

    return $max;
}

sub max {
    my($this, $that) = @_;
    return $this if $this > $that;
    return $that;
}

sub display_a {
    my $mod = shift;

    for my $v (grep !/0[01]0$/, sort keys %Module::CoreList::version ) {
        next unless exists $Module::CoreList::version{$v}{$mod};

        my $mod_v = $Module::CoreList::version{$v}{$mod} || 'undef';
        printf "  %-10s %-10s\n", format_perl_version($v), $mod_v;
    }
    print "\n";
}


{
    my $have_version_pm;
    sub have_version_pm {
        return $have_version_pm if defined $have_version_pm;
        return $have_version_pm = eval { require version; 1 };
    }
}


sub format_perl_version {
    my $v = shift;
    return $v if $v < 5.006 or !have_version_pm;
    return version->new($v)->normal;
}


sub numify_version {
    my $ver = shift;
    if ($ver =~ /\..+\./) {
	have_version_pm()
	    or die "You need to install version.pm to use dotted version numbers\n";
        $ver = version->new($ver)->numify;
    }
    $ver += 0;
    return $ver;
}

=head1 EXAMPLES

    $ corelist File::Spec

    File::Spec was first released with perl 5.005

    $ corelist File::Spec 0.83

    File::Spec 0.83 was released with perl 5.007003

    $ corelist File::Spec 0.89

    File::Spec 0.89 was not in CORE (or so I think)

    $ corelist File::Spec::Aliens

    File::Spec::Aliens  was not in CORE (or so I think)

    $ corelist /IPC::Open/

    IPC::Open2 was first released with perl 5

    IPC::Open3 was first released with perl 5

    $ corelist /MANIFEST/i

    ExtUtils::Manifest was first released with perl 5.001

    $ corelist /Template/

    /Template/  has no match in CORE (or so I think)

    $ corelist -v 5.8.8 B

    B                        1.09_01

    $ corelist -v 5.8.8 /^B::/

    B::Asmdata               1.01
    B::Assembler             0.07
    B::Bblock                1.02_01
    B::Bytecode              1.01_01
    B::C                     1.04_01
    B::CC                    1.00_01
    B::Concise               0.66
    B::Debug                 1.02_01
    B::Deparse               0.71
    B::Disassembler          1.05
    B::Lint                  1.03
    B::O                     1.00
    B::Showlex               1.02
    B::Stackobj              1.00
    B::Stash                 1.00
    B::Terse                 1.03_01
    B::Xref                  1.01

=head1 COPYRIGHT

Copyright (c) 2002-2007 by D.H. aka PodMaster

Currently maintained by the perl 5 porters E<lt>perl5-porters@perl.orgE<gt>.

This program is distributed under the same terms as perl itself.
See http://perl.org/ or http://cpan.org/ for more info on that.

=cut