| blib/lib/CPAN/Dependency.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 94 | 226 | 41.5 |
| branch | 17 | 96 | 17.7 |
| condition | 7 | 57 | 12.2 |
| subroutine | 23 | 30 | 76.6 |
| pod | 13 | 13 | 100.0 |
| total | 154 | 422 | 36.4 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package CPAN::Dependency; | ||||||
| 2 | 9 | 9 | 635047 | use strict; | |||
| 9 | 25 | ||||||
| 9 | 385 | ||||||
| 3 | 9 | 9 | 47 | use warnings; | |||
| 9 | 18 | ||||||
| 9 | 245 | ||||||
| 4 | 9 | 9 | 58 | use Carp; | |||
| 9 | 23 | ||||||
| 9 | 937 | ||||||
| 5 | 9 | 9 | 9172 | use CPANPLUS::Backend; | |||
| 9 | 9541046 | ||||||
| 9 | 309 | ||||||
| 6 | 9 | 9 | 94 | use Cwd; | |||
| 9 | 18 | ||||||
| 9 | 581 | ||||||
| 7 | 9 | 9 | 42 | use Exporter (); | |||
| 9 | 17 | ||||||
| 9 | 127 | ||||||
| 8 | 9 | 9 | 44 | use File::Spec; | |||
| 9 | 15 | ||||||
| 9 | 265 | ||||||
| 9 | 9 | 9 | 297670 | use File::Slurp; | |||
| 9 | 50825 | ||||||
| 9 | 769 | ||||||
| 10 | 9 | 9 | 29958 | use Module::CoreList; | |||
| 9 | 434145 | ||||||
| 9 | 171 | ||||||
| 11 | 9 | 9 | 13887 | use YAML qw(LoadFile DumpFile); | |||
| 9 | 60989 | ||||||
| 9 | 697 | ||||||
| 12 | |||||||
| 13 | 9 | 9 | 76 | use constant ALL_CPAN => 'all CPAN modules'; | |||
| 9 | 19 | ||||||
| 9 | 459 | ||||||
| 14 | |||||||
| 15 | 9 | 9 | 46 | { no strict; | |||
| 9 | 19 | ||||||
| 9 | 3665 | ||||||
| 16 | $VERSION = '0.16'; | ||||||
| 17 | @ISA = qw(Exporter); | ||||||
| 18 | @EXPORT = qw(ALL_CPAN); | ||||||
| 19 | } | ||||||
| 20 | |||||||
| 21 | my($RESET,$BOLD,$RED,$GREEN,$YELLOW); | ||||||
| 22 | |||||||
| 23 | =head1 NAME | ||||||
| 24 | |||||||
| 25 | CPAN::Dependency - Analyzes CPAN modules and generates their dependency tree | ||||||
| 26 | |||||||
| 27 | =head1 VERSION | ||||||
| 28 | |||||||
| 29 | Version 0.16 | ||||||
| 30 | |||||||
| 31 | =head1 SYNOPSIS | ||||||
| 32 | |||||||
| 33 | Find and print the 10 most required CPAN distributions by | ||||||
| 34 | stand-alone processing. | ||||||
| 35 | |||||||
| 36 | use CPAN::Dependency; | ||||||
| 37 | |||||||
| 38 | my $cpandep = CPAN::Dependency->new(process => ALL_CPAN); | ||||||
| 39 | $cpandep->run; # this may take some time.. | ||||||
| 40 | $cpandep->calculate_score; | ||||||
| 41 | |||||||
| 42 | my %score = $cpandep->score_by_dists; | ||||||
| 43 | my @dists = sort { $score{$b} <=> $score{$a} } keys %score; | ||||||
| 44 | print "Top 10 modules\n"; | ||||||
| 45 | for my $dist (@dists[0..9]) { | ||||||
| 46 | printf "%5d %s\n", $score{$dist}, $dist; | ||||||
| 47 | } | ||||||
| 48 | |||||||
| 49 | Same thing, but this time by loading the prerequisites information | ||||||
| 50 | from the CPANTS database. | ||||||
| 51 | |||||||
| 52 | use CPAN::Dependency; | ||||||
| 53 | my $cpandep = new CPAN::Dependency; | ||||||
| 54 | $cpandep->load_cpants_db(file => 'cpants.db'); | ||||||
| 55 | $cpandep->calculate_score; | ||||||
| 56 | |||||||
| 57 | my %score = $cpandep->score_by_dists; | ||||||
| 58 | my @dists = sort { $score{$b} <=> $score{$a} } keys %score; | ||||||
| 59 | print "Top 10 modules\n"; | ||||||
| 60 | for my $dist (@dists[0..9]) { | ||||||
| 61 | printf "%5d %s\n", $score{$dist}, $dist; | ||||||
| 62 | } | ||||||
| 63 | |||||||
| 64 | |||||||
| 65 | =head1 DESCRIPTION | ||||||
| 66 | |||||||
| 67 | I | ||||||
| 68 | Check L<"See ALSO"> for similar, more recent modules.> | ||||||
| 69 | |||||||
| 70 | This module can process a set of distributions, up to the whole CPAN, | ||||||
| 71 | and extract the dependency relations between these distributions. | ||||||
| 72 | Alternatively, it can load the prerequisites information from a | ||||||
| 73 | CPANTS database. | ||||||
| 74 | |||||||
| 75 | It also calculates a score for each distribution based on the number | ||||||
| 76 | of times it appears in the prerequisites of other distributions. | ||||||
| 77 | The algorithm is described in more details in L<"SCORE CALCULATION">. | ||||||
| 78 | |||||||
| 79 | C |
||||||
| 80 | be saved and loaded using C |
||||||
| 81 | The structure looks like this: | ||||||
| 82 | |||||||
| 83 | DEPS_TREE = { | ||||||
| 84 | DIST => { | ||||||
| 85 | author => STRING, | ||||||
| 86 | cpanid => STRING, | ||||||
| 87 | score => NUMBER, | ||||||
| 88 | prereqs => { | ||||||
| 89 | DIST => BOOLEAN, | ||||||
| 90 | ... | ||||||
| 91 | }, | ||||||
| 92 | used_by => { | ||||||
| 93 | DIST => BOOLEAN, | ||||||
| 94 | ... | ||||||
| 95 | }, | ||||||
| 96 | }, | ||||||
| 97 | .... | ||||||
| 98 | } | ||||||
| 99 | |||||||
| 100 | With each distribution name I |
||||||
| 101 | |||||||
| 102 | =over 4 | ||||||
| 103 | |||||||
| 104 | =item * | ||||||
| 105 | |||||||
| 106 | C |
||||||
| 107 | (or last released) this distribution; | ||||||
| 108 | |||||||
| 109 | =item * | ||||||
| 110 | |||||||
| 111 | C |
||||||
| 112 | (or last released) this distribution; | ||||||
| 113 | |||||||
| 114 | =item * | ||||||
| 115 | |||||||
| 116 | C |
||||||
| 117 | |||||||
| 118 | =item * | ||||||
| 119 | |||||||
| 120 | C |
||||||
| 121 | each key is a prerequisite name and its value is a boolean which is true when | ||||||
| 122 | the distribution and the prerequisite are not from the same author; | ||||||
| 123 | |||||||
| 124 | =item * | ||||||
| 125 | |||||||
| 126 | C |
||||||
| 127 | particular distribution; each key is a distribution name and its value is a | ||||||
| 128 | boolean which is true when both distributions are not from the same author; | ||||||
| 129 | |||||||
| 130 | =back | ||||||
| 131 | |||||||
| 132 | =head1 METHODS | ||||||
| 133 | |||||||
| 134 | =over 4 | ||||||
| 135 | |||||||
| 136 | =item new() | ||||||
| 137 | |||||||
| 138 | Creates and returns a new object. | ||||||
| 139 | |||||||
| 140 | B |
||||||
| 141 | |||||||
| 142 | =over 4 | ||||||
| 143 | |||||||
| 144 | =item * | ||||||
| 145 | |||||||
| 146 | C |
||||||
| 147 | |||||||
| 148 | =item * | ||||||
| 149 | |||||||
| 150 | C |
||||||
| 151 | |||||||
| 152 | =item * | ||||||
| 153 | |||||||
| 154 | C |
||||||
| 155 | during the process or not. | ||||||
| 156 | |||||||
| 157 | =item * | ||||||
| 158 | |||||||
| 159 | C |
||||||
| 160 | |||||||
| 161 | =item * | ||||||
| 162 | |||||||
| 163 | C |
||||||
| 164 | |||||||
| 165 | =item * | ||||||
| 166 | |||||||
| 167 | C |
||||||
| 168 | |||||||
| 169 | =item * | ||||||
| 170 | |||||||
| 171 | C |
||||||
| 172 | |||||||
| 173 | =back | ||||||
| 174 | |||||||
| 175 | B |
||||||
| 176 | |||||||
| 177 | Creates a new C |
||||||
| 178 | and adds a few "big" modules to the process list: | ||||||
| 179 | |||||||
| 180 | my $cpandep = new CPAN::Dependency verbose => 1, | ||||||
| 181 | process => [qw(WWW::Mechanize Maypole Template CPAN::Search::Lite)] | ||||||
| 182 | |||||||
| 183 | Creates a new C |
||||||
| 184 | and adds all the distributions from the CPAN to the process list: | ||||||
| 185 | |||||||
| 186 | my $cpandep = new CPAN::Dependency verbose => 1, process => ALL_CPAN; | ||||||
| 187 | |||||||
| 188 | =cut | ||||||
| 189 | |||||||
| 190 | sub new { | ||||||
| 191 | 7 | 7 | 1 | 20892 | my $self = { | ||
| 192 | backend => 0, # CPANPLUS::Backend object | ||||||
| 193 | |||||||
| 194 | options => { # options | ||||||
| 195 | clean_build_dir => 0, # - delete CPANPLUS build directory ? | ||||||
| 196 | color => 0, # - use ANSI colors? | ||||||
| 197 | debug => 0, # - debug level | ||||||
| 198 | prefer_bin => 0, # - prefer binaries? | ||||||
| 199 | verbose => 0, # - verbose? | ||||||
| 200 | }, | ||||||
| 201 | |||||||
| 202 | process => [ ], # modules/distributions to process | ||||||
| 203 | |||||||
| 204 | prereqs => { }, # distributions dependencies | ||||||
| 205 | |||||||
| 206 | skip => { # distributions to skip (during processing) | ||||||
| 207 | 'perl' => 1, | ||||||
| 208 | 'parrot' => 1, | ||||||
| 209 | 'ponie' => 1, | ||||||
| 210 | }, | ||||||
| 211 | |||||||
| 212 | ignore => { # distributions to ignore (during dependencies calculations) | ||||||
| 213 | 'perl' => 1, | ||||||
| 214 | 'parrot' => 1, | ||||||
| 215 | 'ponie' => 1, | ||||||
| 216 | }, | ||||||
| 217 | }; | ||||||
| 218 | 7 | 33 | 54 | my $class = ref $_[0] || $_[0]; shift; | |||
| 7 | 18 | ||||||
| 219 | 7 | 24 | bless $self, $class; | ||||
| 220 | |||||||
| 221 | 7 | 77 | $self->{backend} = new CPANPLUS::Backend; | ||||
| 222 | 7 | 50 | 480007 | croak "fatal: Can't create CPANPLUS::Backend object" | |||
| 223 | unless defined $self->{backend}; | ||||||
| 224 | 7 | 74 | my $cpan = $self->{backend}; | ||||
| 225 | 7 | 90 | my $conf = $cpan->configure_object; | ||||
| 226 | |||||||
| 227 | 7 | 240 | $self->verbose(0); | ||||
| 228 | 7 | 49 | $self->debug(0); | ||||
| 229 | 7 | 87 | $self->color(1); | ||||
| 230 | |||||||
| 231 | 7 | 94 | $self->{build_dir} = File::Spec->catdir($conf->get_conf('base'), | ||||
| 232 | $cpan->_perl_version(perl => $^X), $conf->_get_build('moddir')); | ||||||
| 233 | |||||||
| 234 | 7 | 4034 | my %args = @_; | ||||
| 235 | |||||||
| 236 | # treat arguments for which an accessor exists | ||||||
| 237 | 7 | 39 | for my $attr (keys %args) { | ||||
| 238 | 12 | 100 | 33 | 118 | defined($self->$attr($args{$attr})) and delete $args{$attr} if $self->can($attr); | ||
| 239 | } | ||||||
| 240 | |||||||
| 241 | # treat remaining arguments | ||||||
| 242 | 7 | 26 | for my $attr (keys %args) { | ||||
| 243 | 1 | 36 | carp "warning: Unknown option '$attr': ignoring" | ||||
| 244 | } | ||||||
| 245 | |||||||
| 246 | 7 | 630 | return $self | ||||
| 247 | } | ||||||
| 248 | |||||||
| 249 | # | ||||||
| 250 | # generate accessors for all existing attributes | ||||||
| 251 | # | ||||||
| 252 | 9 | 9 | 53 | { no strict 'refs'; | |||
| 9 | 19 | ||||||
| 9 | 32635 | ||||||
| 253 | for my $attr (qw(clean_build_dir verbose)) { | ||||||
| 254 | *{__PACKAGE__.'::'.$attr} = sub { | ||||||
| 255 | 15 | 15 | 4998 | my $self = shift; | |||
| 256 | 15 | 56 | my $value = $self->{options}{$attr}; | ||||
| 257 | 15 | 50 | 119 | $self->{options}{$attr} = shift if @_; | |||
| 258 | 15 | 164 | return $value | ||||
| 259 | } | ||||||
| 260 | } | ||||||
| 261 | } | ||||||
| 262 | |||||||
| 263 | |||||||
| 264 | =item process() | ||||||
| 265 | |||||||
| 266 | Adds given distribution or module names to the list of packages to process. | ||||||
| 267 | The special argument C |
||||||
| 268 | process all packages in the CPAN. | ||||||
| 269 | |||||||
| 270 | B |
||||||
| 271 | |||||||
| 272 | Add distributions and modules to the process list, passing as a list: | ||||||
| 273 | |||||||
| 274 | $cpandep->process('WWW::Mechanize', 'Maypole', 'CPAN-Search-Lite'); | ||||||
| 275 | |||||||
| 276 | Add distributions and modules to the process list, passing as an arrayref: | ||||||
| 277 | |||||||
| 278 | $cpandep->process(['WWW-Mechanize', 'Maypole::Application', 'CPAN::Search::Lite']); | ||||||
| 279 | |||||||
| 280 | =cut | ||||||
| 281 | |||||||
| 282 | sub process { | ||||||
| 283 | 1 | 1 | 1 | 1594 | my $self = shift; | ||
| 284 | 1 | 50 | 50 | 11 | carp "error: No argument given to attribute process()" and return unless @_; | ||
| 285 | 0 | 0 | 0 | if($_[0] eq ALL_CPAN) { | |||
| 286 | 0 | 0 | push @{ $self->{process} }, sort keys %{ $self->{backend}->module_tree } | ||||
| 0 | 0 | ||||||
| 0 | 0 | ||||||
| 287 | } else { | ||||||
| 288 | 0 | 0 | 0 | push @{ $self->{process} }, ref $_[0] ? @{$_[0]} : @_ | |||
| 0 | 0 | ||||||
| 0 | 0 | ||||||
| 289 | } | ||||||
| 290 | } | ||||||
| 291 | |||||||
| 292 | |||||||
| 293 | =item skip() | ||||||
| 294 | |||||||
| 295 | Adds given distribution or module names to the list of packages that you | ||||||
| 296 | I |
||||||
| 297 | |||||||
| 298 | B |
||||||
| 299 | |||||||
| 300 | Add distributions and modules to the skip list, passing as a list: | ||||||
| 301 | |||||||
| 302 | $cpandep->skip('LWP::UserAgent', 'Net_SSLeay.pm', 'CGI'); | ||||||
| 303 | |||||||
| 304 | Add distributions and modules to the skip list, passing as an arrayref: | ||||||
| 305 | |||||||
| 306 | $cpandep->skip(['libwww-perl', 'Net::SSLeay', 'CGI.pm']); | ||||||
| 307 | |||||||
| 308 | =cut | ||||||
| 309 | |||||||
| 310 | sub skip { | ||||||
| 311 | 4 | 4 | 1 | 2223 | my $self = shift; | ||
| 312 | 4 | 100 | 50 | 27 | carp "error: No argument given to attribute skip()" and return unless @_; | ||
| 313 | 3 | 100 | 24 | my @packages = ref $_[0] ? @{$_[0]} : @_; | |||
| 2 | 12 | ||||||
| 314 | 3 | 11 | for my $package (@packages) { | ||||
| 315 | 8 | 55 | my $dist = $self->{backend}->parse_module(module => $package)->package_name; | ||||
| 316 | 8 | 3115158 | $self->{skip}{$dist} = 1; | ||||
| 317 | } | ||||||
| 318 | } | ||||||
| 319 | |||||||
| 320 | |||||||
| 321 | =item run() | ||||||
| 322 | |||||||
| 323 | Launches the execution of the C |
||||||
| 324 | |||||||
| 325 | =cut | ||||||
| 326 | |||||||
| 327 | sub run { | ||||||
| 328 | 0 | 0 | 1 | 0 | my $self = shift; | ||
| 329 | 0 | 0 | my $cpan = $self->{backend}; | ||||
| 330 | |||||||
| 331 | 0 | 0 | my @dists = @{ $self->{process} }; | ||||
| 0 | 0 | ||||||
| 332 | |||||||
| 333 | 0 | 0 | my($archive,$where) = (); | ||||
| 334 | |||||||
| 335 | 0 | 0 | for my $name (@dists) { | ||||
| 336 | 0 | 0 | my $dist = $cpan->parse_module(module => $name); | ||||
| 337 | 0 | 0 | my $dist_name = $dist->package_name; | ||||
| 338 | |||||||
| 339 | 0 | 0 | $self->_vprint($name); | ||||
| 340 | 0 | 0 | 0 | 0 | $self->_vprint(" >> ${YELLOW}skip: already processed$RESET\n") and | ||
| 0 | |||||||
| 341 | next if not defined $dist or $self->{skip}{$dist_name}++; | ||||||
| 342 | |||||||
| 343 | 0 | 0 | 0 | 0 | $self->_vprint(" >> ${YELLOW}skip: is a bundle$RESET\n") and | ||
| 344 | next if $dist->is_bundle; | ||||||
| 345 | |||||||
| 346 | 0 | 0 | $self->_vprintf(" => $BOLD%s$RESET %s by %s (%s)\n", $dist_name, | ||||
| 347 | $dist->package_version, $dist->author->cpanid, $dist->author->author); | ||||||
| 348 | |||||||
| 349 | 0 | 0 | $archive = $where = ''; | ||||
| 350 | |||||||
| 351 | # fetch and extract the distribution | ||||||
| 352 | 0 | 0 | eval { | ||||
| 353 | 0 | 0 | 0 | $archive = $dist->fetch(force => 1) or next; | |||
| 354 | 0 | 0 | 0 | $where = $dist->extract(force => 1) or next; | |||
| 355 | }; | ||||||
| 356 | 0 | 0 | 0 | 0 | $self->_vprint(" >> $BOLD${RED}CPANPLUS error: $@$RESET\n") and next if $@; | ||
| 357 | |||||||
| 358 | # find its dependencies (that's the harder part) | ||||||
| 359 | 0 | 0 | my $deps = undef; | ||||
| 360 | |||||||
| 361 | # if there's a META.yml, we've won | ||||||
| 362 | # argh! this is no longer true! distributions like SVK include a META.yml | ||||||
| 363 | # with no prereqs :( | ||||||
| 364 | 0 | 0 | 0 | if(-f File::Spec->catfile($where, 'META.yml')) { | |||
| 365 | 0 | 0 | eval { | ||||
| 366 | 0 | 0 | $deps = LoadFile(File::Spec->catfile($where, 'META.yml')); | ||||
| 367 | 0 | 0 | $deps = $deps->{requires}; | ||||
| 368 | }; | ||||||
| 369 | 0 | 0 | 0 | $self->_vprint(" >> $BOLD${RED}YAML error: $@$RESET\n") if $@; | |||
| 370 | } | ||||||
| 371 | |||||||
| 372 | # if not, we must try harder | ||||||
| 373 | 0 | 0 | 0 | 0 | unless(defined $deps and ref $deps eq 'HASH' and keys %$deps) { | ||
| 0 | |||||||
| 374 | 0 | 0 | $self->_vprint(" >> $BOLD${YELLOW}no META.yml; using parsing method$RESET\n"); | ||||
| 375 | |||||||
| 376 | # distribution uses Makefile.PL | ||||||
| 377 | 0 | 0 | 0 | if(-f File::Spec->catfile($where, 'Makefile.PL')) { | |||
| 0 | |||||||
| 378 | 0 | 0 | my $builder = read_file( File::Spec->catfile($where, 'Makefile.PL') ); | ||||
| 379 | 0 | 0 | $builder =~ / | ||||
| 380 | (?: PREREQ_PM.*?=>.*?\{(.*?)\} )| # ExtUtils::MakeMaker | ||||||
| 381 | (?: requires\(([^)]*)\)) # Module::Install | ||||||
| 382 | /sx; | ||||||
| 383 | 0 | 0 | 0 | my $requires = $1 || $2; | |||
| 384 | 0 | 0 | 0 | if(not defined $requires) { | |||
| 385 | 0 | 0 | $self->_vprint(" >> $BOLD${YELLOW}don't know how to figure out prereqs from Makefile.PL for $where$RESET\n"); | ||||
| 386 | } else { | ||||||
| 387 | 0 | 0 | eval "{ no strict; \$deps = { $requires \n} }"; | ||||
| 388 | } | ||||||
| 389 | |||||||
| 390 | # distribution uses Build.PL | ||||||
| 391 | } elsif(-f File::Spec->catfile($where, 'Build.PL')) { | ||||||
| 392 | 0 | 0 | my $builder = read_file( File::Spec->catfile($where, 'Build.PL') ); | ||||
| 393 | 0 | 0 | my($requires) = $builder =~ /requires.*?=>.*?\{(.*?)\}/s; | ||||
| 394 | 0 | 0 | eval "{ no strict; \$deps = { $requires \n} }"; | ||||
| 395 | |||||||
| 396 | } else { | ||||||
| 397 | 0 | 0 | $self->_vprint(" >> $BOLD${RED}error: no Makefile.PL or Build.PL found$RESET\n"); | ||||
| 398 | next | ||||||
| 399 | 0 | 0 | } | ||||
| 400 | } | ||||||
| 401 | |||||||
| 402 | 0 | 0 | 0 | $deps ||= {}; | |||
| 403 | 0 | 0 | my %deps = (); | ||||
| 404 | |||||||
| 405 | 0 | 0 | $self->_vprint(" \e[1;32mprereqs: ", join(', ', sort keys %$deps), "\e[0m\n"); | ||||
| 406 | |||||||
| 407 | # $deps contains module names, but we really want distribution names | ||||||
| 408 | # %deps will have the following structure: | ||||||
| 409 | # | ||||||
| 410 | # %deps = ( | ||||||
| 411 | # DIST_NAME => { | ||||||
| 412 | # PREREQ_DIST_1 => COUNT, | ||||||
| 413 | # PREREQ_DIST_2 => COUNT, | ||||||
| 414 | # ... | ||||||
| 415 | # } | ||||||
| 416 | # ) | ||||||
| 417 | # | ||||||
| 418 | # where COUNT is 0 when PREREQ_DIST_x and DIST_NAME have the same | ||||||
| 419 | # author, 1 otherwise. | ||||||
| 420 | # | ||||||
| 421 | 0 | 0 | for my $reqmod (keys %$deps) { | ||||
| 422 | 0 | 0 | $reqmod =~ s/^\s+//g; $reqmod =~ s/\s+$//g; | ||||
| 0 | 0 | ||||||
| 423 | |||||||
| 424 | 0 | 0 | 0 | 0 | $self->_vprint(" >> $BOLD${YELLOW}ignoring prereq $reqmod$RESET\n") | ||
| 425 | and next if $self->{ignore}{$reqmod}; | ||||||
| 426 | |||||||
| 427 | 0 | 0 | 0 | 0 | $self->_vprint(" >> $BOLD${YELLOW}$reqmod is in Perl core$RESET\n") | ||
| 428 | and next if Module::CoreList->first_release($reqmod); | ||||||
| 429 | |||||||
| 430 | 0 | 0 | my $reqdist = eval { $cpan->parse_module(module => $reqmod) }; | ||||
| 0 | 0 | ||||||
| 431 | 0 | 0 | 0 | 0 | $self->_vprint(" >> $BOLD${RED}error: no dist found for $reqmod$RESET\n") | ||
| 0 | |||||||
| 432 | and $deps{$reqmod} = 1 and next unless defined $reqdist; | ||||||
| 433 | |||||||
| 434 | 0 | 0 | 0 | 0 | $self->_vprint(" >> $BOLD${YELLOW}$reqmod is in Perl core$RESET\n") | ||
| 435 | and next if $reqdist->package_is_perl_core; | ||||||
| 436 | |||||||
| 437 | 0 | 0 | 0 | $deps{$reqdist->package_name} = | |||
| 438 | $reqdist->author->cpanid ne $dist->author->cpanid ? 1 : 0; | ||||||
| 439 | } | ||||||
| 440 | |||||||
| 441 | 0 | 0 | $self->{prereqs}{$dist_name} = { | ||||
| 442 | prereqs => { %deps }, | ||||||
| 443 | used_by => { }, | ||||||
| 444 | score => 0, | ||||||
| 445 | cpanid => $dist->author->cpanid, | ||||||
| 446 | author => $dist->author->author, | ||||||
| 447 | }; | ||||||
| 448 | |||||||
| 449 | } continue { | ||||||
| 450 | # clean up | ||||||
| 451 | 0 | 0 | eval { | ||||
| 452 | 0 | 0 | 0 | 0 | $cpan->_rmdir(dir => $where) if defined $where and -d $where; | ||
| 453 | 0 | 0 | 0 | $cpan->_rmdir(dir => $self->{build_dir}) if $self->{options}{clean_build_dir}; | |||
| 454 | 0 | 0 | 0 | $cpan->_mkdir(dir => $self->{build_dir}) if $self->{options}{clean_build_dir}; | |||
| 455 | } | ||||||
| 456 | } | ||||||
| 457 | |||||||
| 458 | 0 | 0 | $self->_vprint("${BOLD}END PROCESSING$RESET\n"); | ||||
| 459 | } | ||||||
| 460 | |||||||
| 461 | |||||||
| 462 | =item calculate_score() | ||||||
| 463 | |||||||
| 464 | Calculate the score of each distribution by walking through the | ||||||
| 465 | dependency tree. | ||||||
| 466 | |||||||
| 467 | =cut | ||||||
| 468 | |||||||
| 469 | sub calculate_score { | ||||||
| 470 | 0 | 0 | 1 | 0 | my $self = shift; | ||
| 471 | |||||||
| 472 | # now walk through the prereqs tree | ||||||
| 473 | 0 | 0 | for my $dist (keys %{$self->{prereqs}}) { | ||||
| 0 | 0 | ||||||
| 474 | 0 | 0 | $self->_tree_walk($dist, 1); | ||||
| 475 | } | ||||||
| 476 | } | ||||||
| 477 | |||||||
| 478 | |||||||
| 479 | =item deps_by_dists() | ||||||
| 480 | |||||||
| 481 | Return the hashref of the object that contains the dependency tree indexed | ||||||
| 482 | by distribution names. | ||||||
| 483 | |||||||
| 484 | =cut | ||||||
| 485 | |||||||
| 486 | sub deps_by_dists { | ||||||
| 487 | 0 | 0 | 1 | 0 | return $_[0]->{prereqs} | ||
| 488 | } | ||||||
| 489 | |||||||
| 490 | |||||||
| 491 | =item score_by_dists() | ||||||
| 492 | |||||||
| 493 | Returns a new hash that contains the score of the processed distributions, | ||||||
| 494 | indexed by the distribution names. | ||||||
| 495 | |||||||
| 496 | =cut | ||||||
| 497 | |||||||
| 498 | sub score_by_dists { | ||||||
| 499 | 0 | 0 | 1 | 0 | my $self = shift; | ||
| 500 | 0 | 0 | return map { $_ => $self->{prereqs}{$_}{score} } keys %{$self->{prereqs}}; | ||||
| 0 | 0 | ||||||
| 0 | 0 | ||||||
| 501 | } | ||||||
| 502 | |||||||
| 503 | |||||||
| 504 | =item save_deps_tree() | ||||||
| 505 | |||||||
| 506 | Saves the dependency tree of the object to a YAML stream. | ||||||
| 507 | Expect one of the following options. | ||||||
| 508 | |||||||
| 509 | B |
||||||
| 510 | |||||||
| 511 | =over 4 | ||||||
| 512 | |||||||
| 513 | =item * | ||||||
| 514 | |||||||
| 515 | C |
||||||
| 516 | |||||||
| 517 | =back | ||||||
| 518 | |||||||
| 519 | B |
||||||
| 520 | |||||||
| 521 | $cpandep->save_deps_tree(file => 'deps.yml'); | ||||||
| 522 | |||||||
| 523 | =cut | ||||||
| 524 | |||||||
| 525 | sub save_deps_tree { | ||||||
| 526 | 1 | 1 | 1 | 692 | my $self = shift; | ||
| 527 | 1 | 50 | 50 | 12 | carp "error: No argument given to function save_deps_tree()" and return unless @_; | ||
| 528 | 0 | 0 | my %args = @_; | ||||
| 529 | 0 | 0 | 0 | if(exists $args{file}) { | |||
| 530 | 0 | 0 | 0 | unlink($args{file}) if -f $args{file}; | |||
| 531 | 0 | 0 | DumpFile($args{file}, $self->{prereqs}); | ||||
| 532 | } | ||||||
| 533 | } | ||||||
| 534 | |||||||
| 535 | |||||||
| 536 | =item load_deps_tree() | ||||||
| 537 | |||||||
| 538 | Loads a YAML stream that contains a dependency tree into the current object. | ||||||
| 539 | Expect one of the following options. | ||||||
| 540 | |||||||
| 541 | B |
||||||
| 542 | |||||||
| 543 | =over 4 | ||||||
| 544 | |||||||
| 545 | =item * | ||||||
| 546 | |||||||
| 547 | C |
||||||
| 548 | |||||||
| 549 | =back | ||||||
| 550 | |||||||
| 551 | B |
||||||
| 552 | |||||||
| 553 | $cpandep->load_deps_tree(file => 'deps.yml'); | ||||||
| 554 | |||||||
| 555 | =cut | ||||||
| 556 | |||||||
| 557 | sub load_deps_tree { | ||||||
| 558 | 1 | 1 | 1 | 718 | my $self = shift; | ||
| 559 | 1 | 50 | 50 | 12 | carp "error: No argument given to function load_deps_tree()" and return unless @_; | ||
| 560 | 0 | 0 | my %args = @_; | ||||
| 561 | 0 | 0 | 0 | if(exists $args{file}) { | |||
| 562 | 0 | 0 | $self->{prereqs} = LoadFile($args{file}); | ||||
| 563 | } | ||||||
| 564 | } | ||||||
| 565 | |||||||
| 566 | |||||||
| 567 | =item load_cpants_db() | ||||||
| 568 | |||||||
| 569 | B |
||||||
| 570 | |||||||
| 571 | Loads the prerequisites information from the given CPANTS database. | ||||||
| 572 | Expects one of the following options. | ||||||
| 573 | |||||||
| 574 | B |
||||||
| 575 | |||||||
| 576 | =over 4 | ||||||
| 577 | |||||||
| 578 | =item * | ||||||
| 579 | |||||||
| 580 | C |
||||||
| 581 | |||||||
| 582 | =back | ||||||
| 583 | |||||||
| 584 | B |
||||||
| 585 | |||||||
| 586 | $cpandep->load_cpants_db(file => 'cpants.db'); | ||||||
| 587 | |||||||
| 588 | =cut | ||||||
| 589 | |||||||
| 590 | sub load_cpants_db { | ||||||
| 591 | 1 | 1 | 1 | 740 | my $self = shift; | ||
| 592 | 1 | 50 | 50 | 13 | carp "error: No argument given to function load_cpants_db()" and return unless @_; | ||
| 593 | 0 | 0 | my %args = @_; | ||||
| 594 | 0 | 0 | my $cpants_db = $args{file}; | ||||
| 595 | 0 | 0 | 0 | -f $cpants_db or croak "fatal: Can't find file '$cpants_db'"; | |||
| 596 | |||||||
| 597 | 0 | 0 | eval 'use DBI'; | ||||
| 598 | |||||||
| 599 | 0 | 0 | 0 | my $dbh = DBI->connect("dbi:SQLite:dbname=$cpants_db", '', '') | |||
| 600 | or croak "fatal: Can't read SQLite database: $DBI::errstr"; | ||||||
| 601 | |||||||
| 602 | 0 | 0 | my $dists_sth = $dbh->prepare(q{ | ||||
| 603 | SELECT dist.id, dist.dist, author.pauseid, author.name | ||||||
| 604 | FROM dist, author | ||||||
| 605 | WHERE author.id=dist.author | ||||||
| 606 | }); | ||||||
| 607 | |||||||
| 608 | 0 | 0 | my $prereqs_sth = $dbh->prepare('SELECT requires FROM prereq WHERE dist=?'); | ||||
| 609 | |||||||
| 610 | 0 | 0 | my $cpan = $self->{backend}; | ||||
| 611 | 0 | 0 | my @distinfo = (); | ||||
| 612 | 0 | 0 | $dists_sth->execute; | ||||
| 613 | 0 | 0 | while(@distinfo = $dists_sth->fetchrow_array) { | ||||
| 614 | 0 | 0 | my $dist_cpan_info = undef; | ||||
| 615 | 0 | 0 | eval { $dist_cpan_info = $cpan->parse_module(module => $distinfo[1]) }; | ||||
| 0 | 0 | ||||||
| 616 | |||||||
| 617 | 0 | 0 | $prereqs_sth->execute($distinfo[0]); | ||||
| 618 | 0 | 0 | my $prereqs = $prereqs_sth->fetchall_arrayref; | ||||
| 619 | 0 | 0 | my @prereqs = (); | ||||
| 620 | 0 | 0 | push @prereqs, map { @$_ } @$prereqs; | ||||
| 0 | 0 | ||||||
| 621 | |||||||
| 622 | 0 | 0 | my %deps = (); | ||||
| 623 | 0 | 0 | for my $reqmod (@prereqs) { | ||||
| 624 | 0 | 0 | $reqmod =~ s/^\s+//g; $reqmod =~ s/\s+$//g; | ||||
| 0 | 0 | ||||||
| 625 | 0 | 0 | 0 | next if $self->{ignore}{$reqmod}; | |||
| 626 | 0 | 0 | 0 | next if Module::CoreList->first_release($reqmod); | |||
| 627 | 0 | 0 | my $reqdist = eval { $cpan->parse_module(module => $reqmod) }; | ||||
| 0 | 0 | ||||||
| 628 | 0 | 0 | 0 | unless(defined $reqdist) { $deps{$reqmod} = 1; next } | |||
| 0 | 0 | ||||||
| 0 | 0 | ||||||
| 629 | 0 | 0 | 0 | next if $reqdist->package_is_perl_core; | |||
| 630 | 0 | 0 | 0 | $deps{$reqdist->package_name} = $reqdist->author->cpanid ne $distinfo[2] ? 1 : 0; | |||
| 631 | } | ||||||
| 632 | |||||||
| 633 | $self->{prereqs}{$distinfo[1]} = { | ||||||
| 634 | prereqs => { %deps }, | ||||||
| 635 | used_by => { }, | ||||||
| 636 | score => 0, | ||||||
| 637 | cpanid => $distinfo[2] || eval { $dist_cpan_info->author->cpanid }, | ||||||
| 638 | 0 | 0 | 0 | author => $distinfo[3] || eval { $dist_cpan_info->author->author }, | |||
| 0 | |||||||
| 639 | }; | ||||||
| 640 | } | ||||||
| 641 | |||||||
| 642 | 0 | 0 | $dbh->disconnect; | ||||
| 643 | } | ||||||
| 644 | |||||||
| 645 | =back | ||||||
| 646 | |||||||
| 647 | |||||||
| 648 | =head2 Internal Methods | ||||||
| 649 | |||||||
| 650 | =over 4 | ||||||
| 651 | |||||||
| 652 | =item _tree_walk() | ||||||
| 653 | |||||||
| 654 | Walks through the dependency tree and updates the score of each distribution. | ||||||
| 655 | See L<"SCORE CALCULATION">. | ||||||
| 656 | |||||||
| 657 | =cut | ||||||
| 658 | |||||||
| 659 | sub _tree_walk { | ||||||
| 660 | 0 | 0 | 0 | my $self = shift; | |||
| 661 | 0 | 0 | my $dist = shift; | ||||
| 662 | 0 | 0 | my $depth = shift; | ||||
| 663 | 0 | 0 | my $meta = $self->{prereqs}{$dist}; | ||||
| 664 | |||||||
| 665 | # avoid cycle dependencies | ||||||
| 666 | 0 | 0 | 0 | return if $meta->{has_seen}; | |||
| 667 | 0 | 0 | local $meta->{has_seen} = 1; | ||||
| 668 | |||||||
| 669 | #print '>'x$depth, " $dist => @{[keys %{$meta->{prereqs}}]}\n"; | ||||||
| 670 | 0 | 0 | for my $reqdist (keys %{ $meta->{prereqs} }) { | ||||
| 0 | 0 | ||||||
| 671 | # are $dist and $reqdist from the same author? | ||||||
| 672 | 0 | 0 | my $same_author = $meta->{prereqs}{$reqdist}; | ||||
| 673 | |||||||
| 674 | # increase the score of the dist this one depends upon | ||||||
| 675 | 0 | 0 | $self->{prereqs}{$reqdist}{score} += $depth * $same_author; | ||||
| 676 | |||||||
| 677 | # adds the current dist to the 'used_by' list of its prereq | ||||||
| 678 | 0 | 0 | 0 | 0 | $self->{prereqs}{$reqdist}{used_by}{$dist} = | ||
| 679 | ($self->{prereqs}{$reqdist}{cpanid}||'') ne $meta->{cpanid} ? 1 : 0; | ||||||
| 680 | |||||||
| 681 | # recurse | ||||||
| 682 | 0 | 0 | $self->_tree_walk($reqdist, $depth + $same_author); | ||||
| 683 | } | ||||||
| 684 | |||||||
| 685 | 0 | 0 | delete $meta->{has_seen}; | ||||
| 686 | } | ||||||
| 687 | |||||||
| 688 | =item _vprint() | ||||||
| 689 | |||||||
| 690 | Like C |
||||||
| 691 | |||||||
| 692 | =cut | ||||||
| 693 | |||||||
| 694 | sub _vprint { | ||||||
| 695 | 0 | 0 | 0 | my $self = shift; | |||
| 696 | 0 | 0 | 0 | print @_ if $self->{options}{verbose}; | |||
| 697 | 0 | 0 | return 1 | ||||
| 698 | } | ||||||
| 699 | |||||||
| 700 | =item _vprintf() | ||||||
| 701 | |||||||
| 702 | Like C |
||||||
| 703 | |||||||
| 704 | =cut | ||||||
| 705 | |||||||
| 706 | sub _vprintf { | ||||||
| 707 | 0 | 0 | 0 | my $self = shift; | |||
| 708 | 0 | 0 | 0 | printf @_ if $self->{options}{verbose}; | |||
| 709 | 0 | 0 | return 1 | ||||
| 710 | } | ||||||
| 711 | |||||||
| 712 | =back | ||||||
| 713 | |||||||
| 714 | |||||||
| 715 | =head1 OPTIONS | ||||||
| 716 | |||||||
| 717 | =over 4 | ||||||
| 718 | |||||||
| 719 | =item clean_build_dir() | ||||||
| 720 | |||||||
| 721 | Control whether to delete the CPANPLUS build directory during the | ||||||
| 722 | processing of the selected modules or not. | ||||||
| 723 | This is a quite aggressive method to clean up things, but it's needed | ||||||
| 724 | when processing the whole CPAN because some distributions are badly | ||||||
| 725 | made, and some may be just too big for a ramdisk. | ||||||
| 726 | Default to false (0). | ||||||
| 727 | |||||||
| 728 | =item color() | ||||||
| 729 | |||||||
| 730 | Selects whether to use ANSI colors or not when verbose is enabled. | ||||||
| 731 | Defaults to yes (1). | ||||||
| 732 | |||||||
| 733 | =cut | ||||||
| 734 | |||||||
| 735 | sub color { | ||||||
| 736 | 11 | 11 | 1 | 951 | my $self = shift; | ||
| 737 | 11 | 42 | my $old = $self->{options}{color}; | ||||
| 738 | 11 | 50 | 51 | if(defined $_[0]) { | |||
| 739 | 11 | 28 | $self->{options}{color} = $_[0]; | ||||
| 740 | 11 | 100 | 153 | ($RESET , $BOLD , $RED , $GREEN , $YELLOW) = | |||
| 741 | $self->{options}{color} ? | ||||||
| 742 | ("\e[0m", "\e[1m", "\e[31m", "\e[32m", "\e[33m") : | ||||||
| 743 | ('')x5 | ||||||
| 744 | } | ||||||
| 745 | 11 | 43 | return $old | ||||
| 746 | } | ||||||
| 747 | |||||||
| 748 | =item debug() | ||||||
| 749 | |||||||
| 750 | Set debug level. Defaults to 0. | ||||||
| 751 | |||||||
| 752 | =cut | ||||||
| 753 | |||||||
| 754 | sub debug { | ||||||
| 755 | 11 | 11 | 1 | 1358 | my $self = shift; | ||
| 756 | 11 | 36 | my $old = $self->{options}{debug}; | ||||
| 757 | 11 | 50 | 54 | if(defined $_[0]) { | |||
| 758 | 11 | 37 | $self->{options}{debug} = $_[0]; | ||||
| 759 | 11 | 55 | $self->{backend}->configure_object->set_conf(verbose => $_[0]); | ||||
| 760 | } | ||||||
| 761 | 11 | 3261 | return $old | ||||
| 762 | } | ||||||
| 763 | |||||||
| 764 | =item prefer_bin() | ||||||
| 765 | |||||||
| 766 | Tells CPANPLUS to use binary programs instead of Perl modules when | ||||||
| 767 | there is the choice (i.e. use B |
||||||
| 768 | |||||||
| 769 | =cut | ||||||
| 770 | |||||||
| 771 | sub prefer_bin { | ||||||
| 772 | 4 | 4 | 1 | 1097 | my $self = shift; | ||
| 773 | 4 | 18 | my $old = $self->{options}{prefer_bin}; | ||||
| 774 | 4 | 50 | 21 | if(defined $_[0]) { | |||
| 775 | 4 | 12 | $self->{options}{prefer_bin} = $_[0]; | ||||
| 776 | 4 | 21 | $self->{backend}->configure_object->set_conf(prefer_bin => $_[0]); | ||||
| 777 | } | ||||||
| 778 | 4 | 929 | return $old | ||||
| 779 | } | ||||||
| 780 | |||||||
| 781 | =item verbose() | ||||||
| 782 | |||||||
| 783 | Sets verbose mode to on (1) or off (0). Defaults to off. | ||||||
| 784 | |||||||
| 785 | =back | ||||||
| 786 | |||||||
| 787 | |||||||
| 788 | =head1 SCORE CALCULATION | ||||||
| 789 | |||||||
| 790 | Once the prerequisites for each distribution have been found, the score | ||||||
| 791 | of each distribution is calculated using the following algorithm: | ||||||
| 792 | |||||||
| 793 | =over 4 | ||||||
| 794 | |||||||
| 795 | =item 1 | ||||||
| 796 | |||||||
| 797 | for each distribution I |
||||||
| 798 | |||||||
| 799 | =item 2 | ||||||
| 800 | |||||||
| 801 | S< >S< >for each prerequisite I of this distribution |
||||||
| 802 | |||||||
| 803 | =item 3 | ||||||
| 804 | |||||||
| 805 | S< >S< >S< >S< >if both I are not made by the same author, |
||||||
| 806 | update the score of I by adding it the current dependency depth |
||||||
| 807 | |||||||
| 808 | =item 4 | ||||||
| 809 | |||||||
| 810 | S< >S< >S< >S< >recurse step 1 using I
|
||||||
| 811 | |||||||
| 812 | =back | ||||||
| 813 | |||||||
| 814 | The aim of this algorithm is to increase the score of distributions | ||||||
| 815 | that are depended upon by many other distributions, while avoiding the | ||||||
| 816 | cases where one author releases a horde of modules which depend upon | ||||||
| 817 | each others. | ||||||
| 818 | |||||||
| 819 | |||||||
| 820 | =head1 PROCESSING NOTES | ||||||
| 821 | |||||||
| 822 | C |
||||||
| 823 | which means that you need to configure CPANPLUS for the account that will | ||||||
| 824 | run the C |
||||||
| 825 | for this. If the account is not supposed to have access to the Internet, | ||||||
| 826 | use a mini-CPAN mirror. See also L<"Local mirror">. | ||||||
| 827 | |||||||
| 828 | |||||||
| 829 | =head1 SPEED TIPS | ||||||
| 830 | |||||||
| 831 | Here are a few tips to speed up the processing when you want to process | ||||||
| 832 | many modules (or the whole CPAN). | ||||||
| 833 | |||||||
| 834 | =head2 Local mirror | ||||||
| 835 | |||||||
| 836 | If it's not the case yet, you should use C |
||||||
| 837 | mini-CPAN local mirror. Then you just need to configure C |
||||||
| 838 | use your mini-CPAN instead of a network mirror. A mini-CPAN can also be | ||||||
| 839 | shared using a web server but if you want speed, you should keep one on | ||||||
| 840 | your local filesystem. | ||||||
| 841 | |||||||
| 842 | Note that you can also add your own private distributions into your | ||||||
| 843 | mini-CPAN using C |
||||||
| 844 | use C |
||||||
| 845 | the CPAN. | ||||||
| 846 | |||||||
| 847 | For more information see L |
||||||
| 848 | |||||||
| 849 | =head2 Ramdisk | ||||||
| 850 | |||||||
| 851 | If your system supports this feature (most modern systems do), you should | ||||||
| 852 | create a ramdisk and move the C |
||||||
| 853 | Here are the instructions for Linux. Other systems are left as an exercise | ||||||
| 854 | for the reader C<:-)> | ||||||
| 855 | |||||||
| 856 | =head3 Ramdisk for Linux | ||||||
| 857 | |||||||
| 858 | The following commands must be executed as root. | ||||||
| 859 | cpanplus is assumed to be the user that will executes this module. | ||||||
| 860 | |||||||
| 861 | =over 4 | ||||||
| 862 | |||||||
| 863 | =item * | ||||||
| 864 | |||||||
| 865 | Create a ramdisk of S<32 MB>: | ||||||
| 866 | |||||||
| 867 | dd if=/dev/zero of=/dev/ram0 bs=1M count=32 | ||||||
| 868 | |||||||
| 869 | =item * | ||||||
| 870 | |||||||
| 871 | Format it and creates an Ext2 filesystem: | ||||||
| 872 | |||||||
| 873 | mke2fs -L ramdisk0 /dev/ram0 | ||||||
| 874 | |||||||
| 875 | =item * | ||||||
| 876 | |||||||
| 877 | Now mount it: | ||||||
| 878 | |||||||
| 879 | mkdir /mnt/ramdisk | ||||||
| 880 | mount /dev/ram0 /mnt/ramdisk/ | ||||||
| 881 | mkdir /mnt/ramdisk/cpanplus | ||||||
| 882 | chown cpanplus /mnt/ramdisk/cpanplus/ | ||||||
| 883 | |||||||
| 884 | =item * | ||||||
| 885 | |||||||
| 886 | Now, as the user cpanplus, move the build directory onto the ramdisk | ||||||
| 887 | and symlink it: | ||||||
| 888 | |||||||
| 889 | mv .cpanplus/5.8.5 /mnt/ramdisk/cpanplus/ | ||||||
| 890 | ln -s /mnt/ramdisk/cpanplus/5.8.5 .cpanplus/5.8.5 | ||||||
| 891 | |||||||
| 892 | =back | ||||||
| 893 | |||||||
| 894 | Note that we are explicitly avoiding to move the whole F<.cpanplus/> | ||||||
| 895 | directory because it will grow really big during the processing: | ||||||
| 896 | some C |
||||||
| 897 | F |
||||||
| 898 | the whole CPAN, it means that you'll have here a complete copy of your | ||||||
| 899 | mini-CPAN, so be sure that you have enough disk space (or symlink | ||||||
| 900 | this directory as well to another volume when you have enough space). | ||||||
| 901 | |||||||
| 902 | =head3 Ramdisk for Mac OS X | ||||||
| 903 | |||||||
| 904 | Here is a small shell script that creates, format and mount a ramdisk | ||||||
| 905 | of S<64 MB>. Its size can be changed by changing the number of blocks, | ||||||
| 906 | where one block is S<512 bytes>. This is a version for OS X.5 and newer: | ||||||
| 907 | |||||||
| 908 | #!/bin/sh | ||||||
| 909 | BLOCK=128000 | ||||||
| 910 | diskutil erasevolume HFS+ "ramdisk" `hdiutil attach -nomount ram://$BLOCKS` | ||||||
| 911 | |||||||
| 912 | and here is a version for OS X.4 and previous: | ||||||
| 913 | |||||||
| 914 | #!/bin/sh | ||||||
| 915 | BLOCK=128000 | ||||||
| 916 | dev=`hdid -nomount ram://$BLOCKS` | ||||||
| 917 | newfs_hfs -v RAMDisk $dev | ||||||
| 918 | mkdir /Volumes/RAMDisk | ||||||
| 919 | chmod 777 /Volumes/RAMDisk | ||||||
| 920 | mount -t hfs $dev /Volumes/RAMDisk | ||||||
| 921 | |||||||
| 922 | Then follow the same instructions for moving the F |
||||||
| 923 | as given for Linux. | ||||||
| 924 | |||||||
| 925 | =head3 Ramdisk for Solaris | ||||||
| 926 | |||||||
| 927 | Beginning with Solaris 9 12/03, Solaris includes a C |
||||||
| 928 | command for managing ramdisks. Below are the links for the documentation | ||||||
| 929 | of that command. | ||||||
| 930 | |||||||
| 931 | =over 4 | ||||||
| 932 | |||||||
| 933 | =item * | ||||||
| 934 | |||||||
| 935 | Solaris 11: | ||||||
| 936 | C |
||||||
| 937 | |||||||
| 938 | =item * | ||||||
| 939 | |||||||
| 940 | Solaris 10: | ||||||
| 941 | C |
||||||
| 942 | |||||||
| 943 | =item * | ||||||
| 944 | |||||||
| 945 | Solaris 9 12/03: | ||||||
| 946 | C |
||||||
| 947 | |||||||
| 948 | =back | ||||||
| 949 | |||||||
| 950 | Ramdisks can also be created in previous versions of Solaris using | ||||||
| 951 | a pseudo-device. Below are the links for the corresponding documentation. | ||||||
| 952 | |||||||
| 953 | =over 4 | ||||||
| 954 | |||||||
| 955 | =item * | ||||||
| 956 | |||||||
| 957 | Solaris 10: | ||||||
| 958 | C |
||||||
| 959 | C |
||||||
| 960 | |||||||
| 961 | =item * | ||||||
| 962 | |||||||
| 963 | Solaris 9: | ||||||
| 964 | C |
||||||
| 965 | |||||||
| 966 | =item * | ||||||
| 967 | |||||||
| 968 | Solaris 8: | ||||||
| 969 | C |
||||||
| 970 | |||||||
| 971 | =item * | ||||||
| 972 | |||||||
| 973 | Solaris 7: | ||||||
| 974 | C |
||||||
| 975 | |||||||
| 976 | =item * | ||||||
| 977 | |||||||
| 978 | Solaris 2.6: | ||||||
| 979 | C |
||||||
| 980 | |||||||
| 981 | =back | ||||||
| 982 | |||||||
| 983 | =head3 Ramdisk for FreeBSD | ||||||
| 984 | |||||||
| 985 | Based on L |
||||||
| 986 | the following commands should create a 256 megabytes ramdisk under S |
||||||
| 987 | |||||||
| 988 | /sbin/mdconfig -a -t malloc -s 256M -u 10 | ||||||
| 989 | /sbin/newfs -U /dev/md10 | ||||||
| 990 | /sbin/mount /dev/md10 /mnt/ramdisk | ||||||
| 991 | |||||||
| 992 | The equivalent script using C |
||||||
| 993 | exercise for the reader. | ||||||
| 994 | |||||||
| 995 | |||||||
| 996 | =head3 Ramdisk for Windows | ||||||
| 997 | |||||||
| 998 | It seems there is no built-in mechanism or tool for creating a ramdisk under | ||||||
| 999 | Windows, but the following links give a few ways to do so. | ||||||
| 1000 | |||||||
| 1001 | =over 4 | ||||||
| 1002 | |||||||
| 1003 | =item * | ||||||
| 1004 | |||||||
| 1005 | Microsoft C |
||||||
| 1006 | |||||||
| 1007 | =item * | ||||||
| 1008 | |||||||
| 1009 | AR Soft RAMDisk (free): L |
||||||
| 1010 | |||||||
| 1011 | =item * | ||||||
| 1012 | |||||||
| 1013 | Cenatek RAMDisk (commercial): L |
||||||
| 1014 | |||||||
| 1015 | =item * | ||||||
| 1016 | |||||||
| 1017 | SuperSeed RamDisk (commercial): L |
||||||
| 1018 | |||||||
| 1019 | =back | ||||||
| 1020 | |||||||
| 1021 | |||||||
| 1022 | =head1 DIAGNOSTICS | ||||||
| 1023 | |||||||
| 1024 | =over 4 | ||||||
| 1025 | |||||||
| 1026 | =item Can't create CPANPLUS::Backend object | ||||||
| 1027 | |||||||
| 1028 | B<(F)> C |
||||||
| 1029 | |||||||
| 1030 | =item Can't find file '%s' | ||||||
| 1031 | |||||||
| 1032 | B<(F)> The file given in argument could not be found. | ||||||
| 1033 | |||||||
| 1034 | =item Can't read SQLite database: %s | ||||||
| 1035 | |||||||
| 1036 | B<(F)> The SQLite database could not be read by the DBI driver. | ||||||
| 1037 | Details follow. The message "file is encrypted or is not a database(1)" | ||||||
| 1038 | usually means that the is not an SQLite database or not in a version | ||||||
| 1039 | handled by the available C |
||||||
| 1040 | |||||||
| 1041 | =item No argument given to attribute %s | ||||||
| 1042 | |||||||
| 1043 | B<(W)> As the message implies, you didn't supply the expected argument to | ||||||
| 1044 | the attribute. | ||||||
| 1045 | |||||||
| 1046 | =item No argument given to function %s | ||||||
| 1047 | |||||||
| 1048 | B<(W)> As the message implies, you didn't supply the expected arguments to | ||||||
| 1049 | the function. | ||||||
| 1050 | |||||||
| 1051 | =item Unknown option '%s': ignoring | ||||||
| 1052 | |||||||
| 1053 | B<(W)> You gave to C |
||||||
| 1054 | |||||||
| 1055 | =back | ||||||
| 1056 | |||||||
| 1057 | |||||||
| 1058 | =head1 SEE ALSO | ||||||
| 1059 | |||||||
| 1060 | =head2 Similar modules | ||||||
| 1061 | |||||||
| 1062 | C |
||||||
| 1063 | CPAN Phalanx project), and there are now more recent modules on the CPAN in | ||||||
| 1064 | the same field, but with more features: | ||||||
| 1065 | |||||||
| 1066 | L |
||||||
| 1067 | |||||||
| 1068 | The CPANTS modules: L |
||||||
| 1069 | (see also L |
||||||
| 1070 | |||||||
| 1071 | L |
||||||
| 1072 | (not restricted to the CPAN) | ||||||
| 1073 | |||||||
| 1074 | L |
||||||
| 1075 | |||||||
| 1076 | |||||||
| 1077 | =head2 Related modules | ||||||
| 1078 | |||||||
| 1079 | L |
||||||
| 1080 | |||||||
| 1081 | L |
||||||
| 1082 | |||||||
| 1083 | L |
||||||
| 1084 | |||||||
| 1085 | |||||||
| 1086 | =head1 AUTHOR | ||||||
| 1087 | |||||||
| 1088 | SE |
||||||
| 1089 | |||||||
| 1090 | =head1 BUGS | ||||||
| 1091 | |||||||
| 1092 | Please report any bugs or feature requests to | ||||||
| 1093 | C |
||||||
| 1094 | L |
||||||
| 1095 | I will be notified, and then you'll automatically be notified | ||||||
| 1096 | of progress on your bug as I make changes. | ||||||
| 1097 | |||||||
| 1098 | =head1 COPYRIGHT & LICENSE | ||||||
| 1099 | |||||||
| 1100 | Copyright 2005 SE |
||||||
| 1101 | |||||||
| 1102 | This program is free software; you can redistribute it and/or modify it | ||||||
| 1103 | under the same terms as Perl itself. | ||||||
| 1104 | |||||||
| 1105 | =cut | ||||||
| 1106 | |||||||
| 1107 | 1; # End of CPAN::Dependency |