summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteffen Mueller <smueller@cpan.org>2011-05-09 22:39:25 +0200
committerSteffen Mueller <smueller@cpan.org>2011-07-18 23:09:43 +0200
commite30afae0a3edd83821767f6ac56dbf053c52f468 (patch)
tree5fc5f9104ca330506680e0bf86e1a856f9a2de3c
parentf11ca51e41e898a77f1fd33b9e0371e69b1be73a (diff)
downloadperl-smueller/perllocal.tar.gz
Parser/generator for perllocal.pod filessmueller/perllocal
This adds an interface to parse/append to perllocal.pod files programmatically. The plan is to patch ExtUtils::Command::MM to use this module if available and to teach Module::Build to use it, too, so that M::B can finally append to perllocal.pod.
-rw-r--r--MANIFEST5
-rw-r--r--dist/ExtUtils-Install/lib/ExtUtils/Perllocal.pm100
-rw-r--r--dist/ExtUtils-Install/lib/ExtUtils/Perllocal/Entry.pm166
-rw-r--r--dist/ExtUtils-Install/lib/ExtUtils/Perllocal/Parser.pm161
-rw-r--r--dist/ExtUtils-Install/t/Perllocal.t62
-rw-r--r--dist/ExtUtils-Install/t/data/perllocal.pod352
6 files changed, 846 insertions, 0 deletions
diff --git a/MANIFEST b/MANIFEST
index 71ba80ed50..ccd8cf9304 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -2959,7 +2959,11 @@ dist/ExtUtils-Install/Changes ExtUtils-Install change log
dist/ExtUtils-Install/lib/ExtUtils/Installed.pm Information on installed extensions
dist/ExtUtils-Install/lib/ExtUtils/Install.pm Handles 'make install' on extensions
dist/ExtUtils-Install/lib/ExtUtils/Packlist.pm Manipulates .packlist files
+dist/ExtUtils-Install/lib/ExtUtils/Perllocal/Entry.pm Parse/generate perllocal.pod
+dist/ExtUtils-Install/lib/ExtUtils/Perllocal/Parser.pm Parse/generate perllocal.pod
+dist/ExtUtils-Install/lib/ExtUtils/Perllocal.pm Parse/generate perllocal.pod
dist/ExtUtils-Install/t/can_write_dir.t Does the _can_write_dir function of ExtUtils::Install work properly?
+dist/ExtUtils-Install/t/data/perllocal.pod Test data for ExtUtils::Perllocal
dist/ExtUtils-Install/t/Installapi2.t See if new api for ExtUtils::Install::install() works
dist/ExtUtils-Install/t/Installed.t See if ExtUtils::Installed works
dist/ExtUtils-Install/t/Install.t See if ExtUtils::Install works
@@ -2968,6 +2972,7 @@ dist/ExtUtils-Install/t/lib/MakeMaker/Test/Setup/BFD.pm MakeMaker test utilities
dist/ExtUtils-Install/t/lib/MakeMaker/Test/Utils.pm MakeMaker test utilities
dist/ExtUtils-Install/t/lib/TieOut.pm Testing library to capture prints
dist/ExtUtils-Install/t/Packlist.t See if Packlist works
+dist/ExtUtils-Install/t/Perllocal.t See if Perllocal works
dist/ExtUtils-Manifest/lib/ExtUtils/Manifest.pm Utilities to write MANIFEST files
dist/ExtUtils-Manifest/lib/ExtUtils/MANIFEST.SKIP The default MANIFEST.SKIP
dist/ExtUtils-Manifest/t/Manifest.t See if ExtUtils::Manifest works
diff --git a/dist/ExtUtils-Install/lib/ExtUtils/Perllocal.pm b/dist/ExtUtils-Install/lib/ExtUtils/Perllocal.pm
new file mode 100644
index 0000000000..e85c02097b
--- /dev/null
+++ b/dist/ExtUtils-Install/lib/ExtUtils/Perllocal.pm
@@ -0,0 +1,100 @@
+package ExtUtils::Perllocal;
+
+use 5.00503;
+use strict;
+use Carp qw();
+use vars qw($VERSION);
+$VERSION = '1.44_01';
+$VERSION = eval $VERSION;
+require ExtUtils::Perllocal::Entry;
+
+sub new {
+ my $class = shift;
+ my %opts = @_;
+ die "'file' parameter required!" if not defined $opts{file};
+ my $self = bless({%opts} => $class);
+ return $self;
+}
+
+sub append_entry {
+ my $self = shift;
+ my $entry = shift;
+
+ my $perllocal = $self->{file};
+ my $pod = $entry->as_pod;
+ open FH, ">>$perllocal"
+ or die "Cannot open perllocal file '$perllocal' for appending: $!";
+ print FH $pod;
+ close FH
+ or die "Failed to write to perllocal file '$perllocal': $!";
+}
+
+sub get_entries {
+ my $self = shift;
+
+ require ExtUtils::Perllocal::Parser;
+ my $file = $self->{file};
+
+ my $parser = ExtUtils::Perllocal::Parser->new;
+ return $parser->parse_from_file($file);
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+ExtUtils::Perllocal - manage perllocal.pod files
+
+=head1 SYNOPSIS
+
+ use ExtUtils::Perllocal;
+ my $pl = ExtUtils::Perllocal->new(file => '/path/to/perllocal.pod');
+ my $entry = ExtUtils::Perllocal::Entry->new(...);
+ $pl->append_entry($entry); # writes to file
+ my @entries = $pl->get_entries(); # parses file
+ foreach my $entry (@entries) {
+ # See ExtUtils::Perllocal::Entry
+ }
+
+=head1 DESCRIPTION
+
+C<ExtUtils::Perllocal> provides a standard way to manage F<perllocal.pod> files.
+
+=head1 METHODS
+
+=head2 new
+
+Contstructor. Takes named parameters.
+
+Mandatory parameter C<file> should point at the F<perllocal.pod> file
+that you intend to append to or parse.
+
+=head2 append_entry
+
+Appends an entry to the specified F<perllocal.pod> file. Takes
+an L<ExtUtils::Perllocal::Entry> object as argument.
+
+=head2 get_entries
+
+Parses the file and returns the list of L<ExtUtils::Perllocal::Entry>
+objects from the file.
+
+B<Note:> Requires the C<Pod::Parser> and C<Time::Local> modules
+at run-time when C<get_entries> is called.
+
+=head1 AUTHOR
+
+Steffen Mueller, C<smueller@cpan.org>
+
+Inspired by C<ExtUtils::Command::MM> by Randy Kobes.
+
+=head1 COPRIGHT AND LICENSE
+
+Copyright (c) 2011 by Steffen Mueller
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
diff --git a/dist/ExtUtils-Install/lib/ExtUtils/Perllocal/Entry.pm b/dist/ExtUtils-Install/lib/ExtUtils/Perllocal/Entry.pm
new file mode 100644
index 0000000000..56021256e1
--- /dev/null
+++ b/dist/ExtUtils-Install/lib/ExtUtils/Perllocal/Entry.pm
@@ -0,0 +1,166 @@
+package ExtUtils::Perllocal::Entry;
+
+use 5.00503;
+use strict;
+use Carp qw();
+
+sub new {
+ my $class = shift;
+ my %opts = @_;
+ if (not defined $opts{name}) {
+ Carp::croak("Need module name as 'name' parameter");
+ }
+
+ my $self = bless({
+ 'time' => time(),
+ type => 'Module',
+ data => {},
+ %opts,
+ } => $class);
+
+ return $self;
+}
+
+sub as_pod {
+ my $self = shift;
+
+ my $pod;
+ $pod = sprintf <<POD, scalar localtime($self->{'time'});
+=head2 %s: C<$self->{type}> L<$self->{name}|$self->{name}>
+
+=over 4
+
+POD
+
+ foreach my $key (sort keys %{$self->{data}}) {
+ if ($key =~ ':') {
+ die "The keys in the 'data' hash of perllocal.pod entries "
+ . "must not contain colons, but '$key' does!";
+ }
+ my $value = $self->{data}{$key};
+ $pod .= <<POD
+=item *
+
+C<$key: $value>
+
+POD
+ }
+ $pod .= "=back\n\n";
+
+ return $pod;
+}
+
+sub name {
+ my $self = shift;
+ if (@_) {
+ $self->{name} = shift;
+ }
+ return $self->{name};
+}
+
+sub time {
+ my $self = shift;
+ if (@_) {
+ $self->{time} = shift;
+ }
+ return $self->{time};
+}
+
+sub type {
+ my $self = shift;
+ if (@_) {
+ $self->{type} = shift;
+ }
+ return $self->{type};
+}
+
+sub data {
+ my $self = shift;
+ return $self->{data};
+}
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+ExtUtils::Perllocal::Entry - A single perllocal.pod entry
+
+=head1 SYNOPSIS
+
+ use ExtUtils::Perllocal;
+ my $pl = ExtUtils::Perllocal->new(file => '/path/to/perllocal.pod');
+ my $entry = ExtUtils::Perllocal::Entry->new(
+ name => 'The::Module',
+ type => 'Module', # defaults to 'Module'
+ 'time' => $seconds_since_epoch, # defaults to running time()
+ data => { # key/value pairs that will be written as an =item list, no defaults
+ # These are all conventions:
+ "installed into" => $path_to_installation,
+ LINKTYPE => 'dynamic', # static|dynamic
+ VERSION => The::Module->VERSION,
+ EXE_FILES => join(' ', @exe_files),
+ },
+ );
+ $pl->append_entry($entry); # writes to file
+
+=head1 DESCRIPTION
+
+C<ExtUtils::Perllocal::Entry> is the in-memory representation of a single
+F<perllocal.pod> entry.
+
+=head1 METHODS
+
+=head2 new
+
+Constructor. Takes named parameters.
+Requires the C<name> parameter indicating the module name.
+
+C<type> is the type of the thing to be installed and defaults to C<Module>.
+C<time> is the installation time as seconds since the UNIX epoch.
+C<data> can contain key/value pairs of additional data to include
+as an itemized list in alphabetical key order. Some conventional
+data is shown in the SYNOPSIS.
+
+Due to the historic output format, the keys of the data hash
+cannot contain colons or else, parsing them again would become
+impossible.
+
+=head2 as_pod
+
+Returns the POD representation of the entry.
+
+=head2 name
+
+Read/write accessor for the module name.
+
+=head2 time
+
+Read/write accessor for the installation time.
+
+=head2 type
+
+Read/write accessor for the installation type.
+
+=head2 data
+
+Read accessor for the additional data (see constructor docs).
+Returns the actual hash ref. that is contained in the object,
+so modifying it modifies the state of the object.
+
+=head1 AUTHOR
+
+Steffen Mueller, C<smueller@cpan.org>
+
+Inspired by C<ExtUtils::Command::MM> by Randy Kobes.
+
+=head1 COPRIGHT AND LICENSE
+
+Copyright (c) 2011 by Steffen Mueller
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
diff --git a/dist/ExtUtils-Install/lib/ExtUtils/Perllocal/Parser.pm b/dist/ExtUtils-Install/lib/ExtUtils/Perllocal/Parser.pm
new file mode 100644
index 0000000000..404aff4d46
--- /dev/null
+++ b/dist/ExtUtils-Install/lib/ExtUtils/Perllocal/Parser.pm
@@ -0,0 +1,161 @@
+package ExtUtils::Perllocal::Parser;
+
+use 5.00503;
+use strict;
+use Carp qw();
+require ExtUtils::Perllocal::Entry;
+require Pod::Simple::SimpleTree;
+require Time::Local;
+
+sub new {
+ my $class = shift;
+ my %opt = @_;
+ my $self = bless({%opt} => $class);
+ return $self;
+}
+
+sub parse_from_file {
+ my $self = shift;
+ my $file = shift;
+ local $self->{file} = $file;
+ my $psst = Pod::Simple::SimpleTree->new->parse_file($file)->root;
+ my @nodes = _subnodes($psst);
+
+ my @entries;
+ while (@nodes) {
+ # Parse this: =head2 Wed Nov 3 20:46:45 2010: C<Module> L<Module::CoreList|Module::CoreList>
+ my $head2 = shift @nodes;
+ if ($head2->[0] ne 'head2') {
+ $self->_report_error($head2, "Expected =head2");
+ next;
+ }
+ my @subn = _subnodes($head2);
+ if (@subn != 4 or ref($subn[0])
+ or not ref($subn[1]) or $subn[1][0] ne 'C'
+ or not ref($subn[3]) or not $subn[3][0] eq 'L')
+ {
+ $self->_report_error($head2, "Expected string of format '[date]: C<[type]> L<[name]>");
+ next;
+ }
+ my $type = $subn[1][2];
+ my $name = $subn[3][2];
+ my $epoch = date_str_to_epoch(\$subn[0]);
+ if (not $epoch or not $subn[0] =~ /^:/) {
+ $self->_report_error($head2, "Expected string of format '[date]: C<[type]> L<[name]>");
+ }
+
+ my $entry = ExtUtils::Perllocal::Entry->new(
+ 'time' => $epoch, type => $type, name => $name,
+ );
+ my $entry_data = $entry->data;
+
+ # Parse the data section (=over.. =item * C<key: value> =back)
+ my $over = shift @nodes;
+ @subn = _subnodes($over);
+ foreach my $bullet (@subn) {
+ if (not ref($bullet)) {
+ $self->_report_error($over, "Expected only =item's in =over");
+ next;
+ }
+ elsif (not $bullet->[0] eq 'item-bullet') {
+ $self->_report_error($bullet, "Expected only =item's in =over");
+ next;
+ }
+ elsif (not ref($bullet->[2]) or $bullet->[2][0] ne 'C') {
+ $self->_report_error($bullet, "Expected C<> in =item");
+ next;
+ }
+ elsif (ref($bullet->[2][2])) {
+ $self->_report_error($bullet, "Expected text in the C<> within each=item");
+ next;
+ }
+
+ my $text = $bullet->[2][2];
+ my ($key, $value) = split /\s*:\s*/, $text, 2;
+ if (not defined $key or not defined $value) {
+ $self->_report_error($bullet, "Expected text of the form 'C<key: value>'");
+ }
+ $entry_data->{$key} = $value;
+ }
+ push @entries, $entry;
+ }
+
+ return @entries;
+}
+
+sub _subnodes {
+ my $n = shift;
+ return @{$n}[2..$#$n];
+}
+
+sub _report_error {
+ my $self = shift;
+ my $node = shift;
+ my $err = shift;
+
+ return if $self->{silent};
+ my $debug = 1;
+ if (ref($node)) {
+ my $line = $node->[1]{start_line};
+ Carp::carp("Invalid perllocal.pod at $self->{file}:$line: $err");
+ if ($debug) {
+ require Data::Dumper;
+ warn Data::Dumper->Dump([$node], ['$node']);
+ }
+ }
+ else {
+ Carp::carp("Invalid perllocal.pod '$self->{file}': $err");
+ }
+}
+
+
+SCOPE: {
+ # Low dependency mode... parse manually. Yikes.
+ my $WeekDayRegexp = qr/(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)/;
+ my $MonthRegexp = qr/(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/;
+ my $imonth = 0;
+ my %MonthToNumber = map {$_ => $imonth++} qw(
+ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
+ );
+
+ sub date_str_to_epoch {
+ my $strref = shift;
+ $$strref =~ s/^$WeekDayRegexp\s+($MonthRegexp)
+ \s+(\d+)\s+(\d\d):(\d\d):(\d\d)\s+(\d\d\d\d)//ox
+ or return();
+
+ my $epoch = Time::Local::timelocal($5, $4, $3, $2, $MonthToNumber{$1}, $6);
+ return $epoch;
+ }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+ExtUtils::Perllocal::Parser - Internal parser tool for ExtUtils::Perllocal
+
+=head1 SYNOPSIS
+
+ use ExtUtils::Perllocal;
+
+=head1 DESCRIPTION
+
+Internal to L<ExtUtils::Perllocal>. B<Never> use this directly!
+
+=head1 AUTHOR
+
+Steffen Mueller, C<smueller@cpan.org>
+
+Inspired by C<ExtUtils::Command::MM> by Randy Kobes.
+
+=head1 COPRIGHT AND LICENSE
+
+Copyright (c) 2011 by Steffen Mueller
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
diff --git a/dist/ExtUtils-Install/t/Perllocal.t b/dist/ExtUtils-Install/t/Perllocal.t
new file mode 100644
index 0000000000..2f8265ecb6
--- /dev/null
+++ b/dist/ExtUtils-Install/t/Perllocal.t
@@ -0,0 +1,62 @@
+#!/usr/bin/perl -w
+
+# Test ExtUtils::Install.
+
+use strict;
+use Test::More tests => 10;
+use File::Spec;
+BEGIN { use_ok('ExtUtils::Perllocal') }
+
+my $datafile = File::Spec->catdir("t", "data", "perllocal.pod");
+$datafile = File::Spec->catdir("data", "perllocal.pod") if not -f $datafile;
+ok(-f $datafile, "test file exists");
+
+my $pl = ExtUtils::Perllocal->new(file => $datafile);
+isa_ok($pl, 'ExtUtils::Perllocal');
+my @entries = $pl->get_entries();
+is(scalar(@entries), 16, "Found all entries");
+
+my $pod = join '', map $_->as_pod, @entries;
+is($pod, slurp($datafile), "rountrip okay");
+
+my $testfile = $datafile . '.tmp';
+$SIG{INT} = $SIG{HUP} = $SIG{TERM} = sub {
+ unlink $testfile;
+ exit(1);
+};
+
+END {unlink $testfile}
+
+open OFH, ">$testfile"
+ or die "Cannot open $testfile for writing: $!";
+
+print OFH $pod;
+close OFH;
+
+$pl = ExtUtils::Perllocal->new(file => $testfile);
+my %entrydata = (
+ type => 'Foo',
+ name => 'The::Name',
+ 'time' => 1304973319,
+ data => {d1 => 'foo', bar => 'baz'},
+);
+$pl->append_entry(ExtUtils::Perllocal::Entry->new(%entrydata));
+
+$pl = ExtUtils::Perllocal->new(file => $testfile);
+@entries = $pl->get_entries();
+is(scalar(@entries), 17, "Found all entries + 1");
+my $e = $entries[-1];
+is($e->type, $entrydata{type});
+is($e->name, $entrydata{name});
+is($e->time, $entrydata{time});
+is_deeply($e->data, $entrydata{data});
+unlink($testfile);
+
+sub slurp {
+ my $datafile = shift;
+ open FH, "<$datafile" or die $!;
+ local $/;
+ my $tmp = <FH>;
+ close FH;
+ $tmp
+}
diff --git a/dist/ExtUtils-Install/t/data/perllocal.pod b/dist/ExtUtils-Install/t/data/perllocal.pod
new file mode 100644
index 0000000000..08a45f2361
--- /dev/null
+++ b/dist/ExtUtils-Install/t/data/perllocal.pod
@@ -0,0 +1,352 @@
+=head2 Wed Nov 3 20:46:45 2010: C<Module> L<Module::CoreList|Module::CoreList>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: corelist>
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 2.40>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Sun Nov 7 16:42:24 2010: C<Module> L<Class::XSAccessor|Class::XSAccessor>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 1.09>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Thu Dec 30 14:35:32 2010: C<Module> L<Parallel::ForkManager|Parallel::ForkManager>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 0.7.9>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Fri Dec 31 10:29:08 2010: C<Module> L<Graph::Easy|Graph::Easy>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: bin/graph-easy>
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 0.70>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Fri Dec 31 10:31:32 2010: C<Module> L<Class::XSAccessor|Class::XSAccessor>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 1.11>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Fri Dec 31 14:18:33 2010: C<Module> L<Statistics::Test::Sequence|Statistics::Test::Sequence>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 0.01>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Fri Dec 31 14:18:35 2010: C<Module> L<Math::Random::MT|Math::Random::MT>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 1.12>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Fri Dec 31 14:18:37 2010: C<Module> L<Statistics::Test::RandomWalk|Statistics::Test::RandomWalk>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 0.01>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Fri Dec 31 14:30:35 2010: C<Module> L<Text::FindIndent|Text::FindIndent>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 0.09>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Fri Dec 31 17:06:52 2010: C<Module> L<Math::Symbolic|Math::Symbolic>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 0.606>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Fri Dec 31 17:16:31 2010: C<Module> L<Regexp::Common|Regexp::Common>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 2010010201>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Sat Jan 8 15:03:09 2011: C<Module> L<Algorithm::QuadTree|Algorithm::QuadTree>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 0.1>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Sat Jan 8 18:23:47 2011: C<Module> L<Devel::NYTProf|Devel::NYTProf>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: bin/nytprofhtml bin/nytprofcsv bin/nytprofcg bin/nytprofmerge>
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 4.06>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Sat Jan 8 19:08:57 2011: C<Module> L<Algorithm::SpatialIndex|Algorithm::SpatialIndex>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 0.01>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Wed Jan 19 13:51:49 2011: C<Module> L<ExtUtils::CBuilder|ExtUtils::CBuilder>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 0.2802>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+
+=head2 Wed Jan 19 13:51:52 2011: C<Module> L<ExtUtils::ParseXS|ExtUtils::ParseXS>
+
+=over 4
+
+=item *
+
+C<EXE_FILES: >
+
+=item *
+
+C<LINKTYPE: dynamic>
+
+=item *
+
+C<VERSION: 2.2206>
+
+=item *
+
+C<installed into: /usr/local/share/perl/5.10.1>
+
+=back
+