Introduce GOVERNANCE document and empty RESOLUTIONS file.
[dbsrgits/DBIx-Class.git] / t / 00describe_environment.t
CommitLineData
cebc0cc8 1###
2### This version is rather 5.8-centric, because DBIC itself is 5.8
3### It certainly can be rewritten to degrade well on 5.6
4###
5
3cd1b172 6# Very important to grab the snapshot early, as we will be reporting
7# the INC indices from the POV of whoever ran the script, *NOT* from
8# the POV of the internals
9my @initial_INC;
cebc0cc8 10BEGIN {
3cd1b172 11 @initial_INC = @INC;
12}
13
c0329273 14BEGIN { do "./t/lib/ANFANG.pm" or die ( $@ || $! ) }
3cd1b172 15
c0329273 16BEGIN {
750a4ad2 17 if ( "$]" < 5.010) {
cebc0cc8 18
19 # Pre-5.10 perls pollute %INC on unsuccesfull module
20 # require, making it appear as if the module is already
21 # loaded on subsequent require()s
22 # Can't seem to find the exact RT/perldelta entry
23 #
24 # The reason we can't just use a sane, clean loader, is because
25 # if a Module require()s another module the %INC will still
26 # get filled with crap and we are back to square one. A global
27 # fix is really the only way for this test, as we try to load
28 # each available module separately, and have no control (nor
29 # knowledge) over their common dependencies.
30 #
31 # we want to do this here, in the very beginning, before even
32 # warnings/strict are loaded
33
cebc0cc8 34 require DBICTest::Util::OverrideRequire;
35
36 DBICTest::Util::OverrideRequire::override_global_require( sub {
37 my $res = eval { $_[0]->() };
38 if ($@ ne '') {
39 delete $INC{$_[1]};
40 die $@;
41 }
42 return $res;
43 } );
44 }
45}
46
cebc0cc8 47use strict;
48use warnings;
49
50use Test::More 'no_plan';
6c7ca962 51
52# Things happen... unfortunately
53$SIG{__DIE__} = sub {
c8215e2c 54 die $_[0] unless defined $^S and ! $^S;
6c7ca962 55
56 diag "Something horrible happened while assembling the diag data\n$_[0]";
57 exit 0;
58};
59
cebc0cc8 60use Config;
61use File::Find 'find';
3cd1b172 62use Digest::MD5 ();
63use Cwd 'abs_path';
c6b73be9 64use File::Spec;
843b427a 65use List::Util 'max';
cebc0cc8 66use ExtUtils::MakeMaker;
cebc0cc8 67
c6b73be9 68use DBICTest::RunMode;
10be570e 69use DBIx::Class::_Util 'visit_namespaces';
c6b73be9 70use DBIx::Class::Optional::Dependencies;
cebc0cc8 71
c6b73be9 72my $known_paths = {
55c6fb91 73 SA => {
74 config_key => 'sitearch',
75 },
76 SL => {
77 config_key => 'sitelib',
78 },
c6b73be9 79 SS => {
80 config_key => 'sitelib_stem',
81 match_order => 1,
82 },
83 SP => {
84 config_key => 'siteprefix',
85 match_order => 2,
86 },
55c6fb91 87 VA => {
88 config_key => 'vendorarch',
89 },
90 VL => {
91 config_key => 'vendorlib',
92 },
c6b73be9 93 VS => {
94 config_key => 'vendorlib_stem',
95 match_order => 3,
96 },
97 VP => {
98 config_key => 'vendorprefix',
99 match_order => 4,
100 },
55c6fb91 101 PA => {
102 config_key => 'archlib',
103 },
104 PL => {
105 config_key => 'privlib',
106 },
c6b73be9 107 PP => {
108 config_key => 'prefix',
109 match_order => 5,
110 },
111 BLA => {
112 rel_path => './blib/arch',
113 skip_unversioned_modules => 1,
114 },
115 BLL => {
116 rel_path => './blib/lib',
117 skip_unversioned_modules => 1,
118 },
55c6fb91 119 INC => {
c6b73be9 120 rel_path => './inc',
55c6fb91 121 },
122 LIB => {
c6b73be9 123 rel_path => './lib',
124 skip_unversioned_modules => 1,
55c6fb91 125 },
3cd1b172 126 T => {
c6b73be9 127 rel_path => './t',
128 skip_unversioned_modules => 1,
129 },
65ea9cad 130 XT => {
131 rel_path => './xt',
132 skip_unversioned_modules => 1,
133 },
c6b73be9 134 CWD => {
135 rel_path => '.',
3cd1b172 136 },
55c6fb91 137 HOME => {
c6b73be9 138 rel_path => '~',
139 abs_unix_path => abs_unix_path (
55c6fb91 140 eval { require File::HomeDir and File::HomeDir->my_home }
141 ||
c6b73be9 142 $ENV{USERPROFILE}
143 ||
55c6fb91 144 $ENV{HOME}
145 ||
146 glob('~')
147 ),
148 },
cebc0cc8 149};
150
c6b73be9 151for my $k (keys %$known_paths) {
152 my $v = $known_paths->{$k};
4bfba4ee 153
55c6fb91 154 # never use home as a found-in-dir marker - it is too broad
155 # HOME is only used by the shortener
3cd1b172 156 $v->{marker} = $k unless $k eq 'HOME';
4bfba4ee 157
c6b73be9 158 unless ( $v->{abs_unix_path} ) {
159 if ( $v->{rel_path} ) {
160 $v->{abs_unix_path} = abs_unix_path( $v->{rel_path} );
55c6fb91 161 }
162 elsif ( $Config{ $v->{config_key} || '' } ) {
c6b73be9 163 $v->{abs_unix_path} = abs_unix_path (
55c6fb91 164 $Config{"$v->{config_key}exp"} || $Config{$v->{config_key}}
165 );
166 }
167 }
cebc0cc8 168
c6b73be9 169 delete $known_paths->{$k} unless $v->{abs_unix_path} and -d $v->{abs_unix_path};
cebc0cc8 170}
c6b73be9 171my $seen_markers = {};
cebc0cc8 172
55c6fb91 173# first run through lib/ and *try* to load anything we can find
cebc0cc8 174# within our own project
175find({
176 wanted => sub {
177 -f $_ or return;
178
399b9455 179 $_ =~ m|lib/DBIx/Class/_TempExtlib| and return;
180
cebc0cc8 181 # can't just `require $fn`, as we need %INC to be
182 # populated properly
183 my ($mod) = $_ =~ /^ lib [\/\\] (.+) \.pm $/x
184 or return;
185
55c6fb91 186 try_module_require(join ('::', File::Spec->splitdir($mod)) )
cebc0cc8 187 },
188 no_chdir => 1,
189}, 'lib' );
190
55c6fb91 191
192
cebc0cc8 193# now run through OptDeps and attempt loading everything else
194#
195# some things needs to be sorted before other things
196# positive - load first
197# negative - load last
198my $load_weights = {
199 # Make sure oracle is tried last - some clients (e.g. 10.2) have symbol
200 # clashes with libssl, and will segfault everything coming after them
201 "DBD::Oracle" => -999,
202};
3cd1b172 203
204my @known_modules = sort
cebc0cc8 205 { ($load_weights->{$b}||0) <=> ($load_weights->{$a}||0) }
8fc4291e 206 qw( Data::Dumper ),
cebc0cc8 207 keys %{
208 DBIx::Class::Optional::Dependencies->req_list_for([
250d9e55 209 grep
210 # some DBDs are notoriously problematic to load
211 # hence only show stuff based on test_rdbms which will
212 # take into account necessary ENVs
55c6fb91 213 { $_ !~ /^ (?: rdbms | dist )_ /x }
250d9e55 214 keys %{DBIx::Class::Optional::Dependencies->req_group_list}
cebc0cc8 215 ])
216 }
217;
218
3cd1b172 219try_module_require($_) for @known_modules;
cebc0cc8 220
55c6fb91 221my $has_versionpm = eval { require version };
3cd1b172 222
223
224# At this point we've loaded everything we ever could, but some modules
225# (understandably) crapped out. For an even more thorough report, note
226# everthing present in @INC we excplicitly know about (via OptDeps)
227# *even though* it didn't load
228my $known_failed_loads;
229
230for my $mod (@known_modules) {
231 my $inc_key = module_notional_filename($mod);
232 next if defined $INC{$inc_key};
233
234 if (defined( my $idx = module_found_at_inc_index( $mod, \@INC ) ) ) {
c6b73be9 235 $known_failed_loads->{$mod} = abs_unix_path( "$INC[$idx]/$inc_key" );
3cd1b172 236 }
237
238}
239
240my $perl = 'perl';
241
242# This is a cool idea, but the line is too long even with shortening :(
243#
244#for my $i ( 1 .. $Config{config_argc} ) {
245# my $conf_arg = $Config{"config_arg$i"};
246# $conf_arg =~ s!
247# \= (.+)
248# !
c6b73be9 249# '=' . shorten_fn($1)
3cd1b172 250# !ex;
251#
252# $perl .= " $conf_arg";
253#}
254
255my $interesting_modules = {
256 # pseudo module
257 $perl => {
258 version => $],
65ea9cad 259 abs_unix_path => abs_unix_path($^X),
3cd1b172 260 }
55c6fb91 261};
3cd1b172 262
263
65ea9cad 264# drill through the *ENTIRE* symtable and build a map of interesting modules
cebc0cc8 265visit_namespaces( action => sub {
4bfba4ee 266 no strict 'refs';
cebc0cc8 267 my $pkg = shift;
268
269 # keep going, but nothing to see here
270 return 1 if $pkg eq 'main';
271
4bfba4ee 272 # private - not interested, including no further descent
cebc0cc8 273 return 0 if $pkg =~ / (?: ^ | :: ) _ /x;
274
3cd1b172 275 my $inc_key = module_notional_filename($pkg);
cebc0cc8 276
c6b73be9 277 my $abs_unix_path = (
3cd1b172 278 $INC{$inc_key}
4bfba4ee 279 and
3cd1b172 280 -f $INC{$inc_key}
4bfba4ee 281 and
3cd1b172 282 -r $INC{$inc_key}
55c6fb91 283 and
c6b73be9 284 abs_unix_path($INC{$inc_key})
3cd1b172 285 );
cebc0cc8 286
3cd1b172 287 # handle versions first (not interested in synthetic classes)
cebc0cc8 288 if (
3cd1b172 289 defined ${"${pkg}::VERSION"}
cebc0cc8 290 and
3cd1b172 291 ${"${pkg}::VERSION"} !~ /\Qset by base.pm/
cebc0cc8 292 ) {
cebc0cc8 293
3cd1b172 294 # make sure a version can be extracted, be noisy when it doesn't work
295 # do this even if we are throwing away the result below in lieu of EUMM
296 my $mod_ver = eval { $pkg->VERSION };
297
298 if (my $err = $@) {
299 $err =~ s/^/ /mg;
300 say_err (
301 "Calling `$pkg->VERSION` resulted in an exception, which should never "
302 . "happen - please file a bug with the distribution containing $pkg. "
303 . "Complete exception text below:\n\n$err"
304 );
305 }
306 elsif( ! defined $mod_ver or ! length $mod_ver ) {
307 my $ret = defined $mod_ver
308 ? "the empty string ''"
309 : "'undef'"
310 ;
311
312 say_err (
313 "Calling `$pkg->VERSION` returned $ret, even though \$${pkg}::VERSION "
314 . "is defined, which should never happen - please file a bug with the "
315 . "distribution containing $pkg."
316 );
317
318 undef $mod_ver;
319 }
320
321 if (
c6b73be9 322 $abs_unix_path
3cd1b172 323 and
c6b73be9 324 defined ( my $eumm_ver = eval { MM->parse_version( $abs_unix_path ) } )
3cd1b172 325 ) {
326
327 # can only run the check reliably if v.pm is there
328 if (
329 $has_versionpm
330 and
331 defined $mod_ver
332 and
333 $eumm_ver ne $mod_ver
334 and
335 (
336 ( eval { version->parse( do { (my $v = $eumm_ver) =~ s/_//g; $v } ) } || 0 )
337 !=
338 ( eval { version->parse( do { (my $v = $mod_ver) =~ s/_//g; $v } ) } || 0 )
339 )
340 ) {
341 say_err (
342 "Mismatch of versions '$mod_ver' and '$eumm_ver', obtained respectively "
c6b73be9 343 . "via `$pkg->VERSION` and parsing the version out of @{[ shorten_fn( $abs_unix_path ) ]} "
3cd1b172 344 . "with ExtUtils::MakeMaker\@@{[ ExtUtils::MakeMaker->VERSION ]}. "
345 . "This should never happen - please check whether this is still present "
346 . "in the latest version, and then file a bug with the distribution "
347 . "containing $pkg."
348 );
349 }
350
351 $interesting_modules->{$pkg}{version} = $eumm_ver;
352 }
353 elsif( defined $mod_ver ) {
354
355 $interesting_modules->{$pkg}{version} = $mod_ver;
356 }
cebc0cc8 357 }
65ea9cad 358 elsif ( $known_failed_loads->{$pkg} ) {
359 $abs_unix_path = $known_failed_loads->{$pkg};
c6b73be9 360 $interesting_modules->{$pkg}{version} = '!! LOAD FAIL !!';
55c6fb91 361 }
362
c6b73be9 363 if ($abs_unix_path) {
364 my ($marker, $initial_inc_idx);
365
366 my $current_inc_idx = module_found_at_inc_index($pkg, \@INC);
367 my $p = subpath_of_known_path( $abs_unix_path );
368
369 if (
370 defined $current_inc_idx
371 and
372 $p->{marker}
373 and
374 abs_unix_path($INC[$current_inc_idx]) eq $p->{abs_unix_path}
375 ) {
376 $marker = $p->{marker};
3cd1b172 377 }
c6b73be9 378 elsif (defined ( $initial_inc_idx = module_found_at_inc_index($pkg, \@initial_INC) ) ) {
379 $marker = "\$INC[$initial_inc_idx]";
3cd1b172 380 }
55c6fb91 381
3cd1b172 382 # we are only interested if there was a declared version already above
c6b73be9 383 # OR if the module came from somewhere other than skip_unversioned_modules
3cd1b172 384 if (
385 $marker
386 and
387 (
388 $interesting_modules->{$pkg}
389 or
c6b73be9 390 !$p->{skip_unversioned_modules}
3cd1b172 391 )
392 ) {
393 $interesting_modules->{$pkg}{source_marker} = $marker;
c6b73be9 394 $seen_markers->{$marker} = 1;
55c6fb91 395 }
3cd1b172 396
397 # at this point only fill in the path (md5 calc) IFF it is interesting
398 # in any respect
c6b73be9 399 $interesting_modules->{$pkg}{abs_unix_path} = $abs_unix_path
3cd1b172 400 if $interesting_modules->{$pkg};
cebc0cc8 401 }
402
403 1;
404});
405
65ea9cad 406# compress identical versions sourced from ./blib, ./lib, ./t and ./xt
407# as close to the root of a namespace as we can
c6b73be9 408purge_identically_versioned_submodules_with_markers([ map {
409 ( $_->{skip_unversioned_modules} && $_->{marker} ) || ()
410} values %$known_paths ]);
cebc0cc8 411
3cd1b172 412ok 1, (scalar keys %$interesting_modules) . " distinctly versioned modules found";
cebc0cc8 413
414# do not announce anything under ci - we are watching for STDERR silence
3cd1b172 415exit 0 if DBICTest::RunMode->is_ci;
cebc0cc8 416
cebc0cc8 417
55c6fb91 418# diag the result out
419my $max_ver_len = max map
420 { length "$_" }
3cd1b172 421 ( 'xxx.yyyzzz_bbb', map { $_->{version} || '' } values %$interesting_modules )
55c6fb91 422;
c6b73be9 423my $max_marker_len = max map { length $_ } ( '$INC[999]', keys %$seen_markers );
55c6fb91 424
ba35e8ec 425# Note - must be less than 76 chars wide to account for the diag() prefix
55c6fb91 426my $discl = <<'EOD';
427
ba35e8ec 428List of loadable modules within both *OPTIONAL* and core dependency chains
65ea9cad 429present on this system (modules sourced from ./blib, ./lib, ./t, and ./xt
c6b73be9 430with versions identical to their parent namespace were omitted for brevity)
431
432 *** Note that *MANY* of these modules will *NEVER* be loaded ***
433 *** during normal operation of DBIx::Class ***
55c6fb91 434EOD
cebc0cc8 435
3cd1b172 436# pre-assemble everything and print it in one shot
437# makes it less likely for parallel test execution to insert bogus lines
438my $final_out = "\n$discl\n";
cebc0cc8 439
c6b73be9 440$final_out .= "\@INC at startup (does not reflect manipulation at runtime):\n";
cebc0cc8 441
c6b73be9 442my $in_inc_skip;
443for (0.. $#initial_INC) {
444
65ea9cad 445 my $shortname = shorten_fn( $initial_INC[$_] );
c6b73be9 446
65ea9cad 447 # when *to* print a line of INC
c6b73be9 448 if (
449 ! $ENV{AUTOMATED_TESTING}
450 or
451 @initial_INC < 11
452 or
453 $seen_markers->{"\$INC[$_]"}
454 or
65ea9cad 455 ! -e $shortname
c6b73be9 456 or
65ea9cad 457 ! File::Spec->file_name_is_absolute($shortname)
c6b73be9 458 ) {
459 $in_inc_skip = 0;
460 $final_out .= sprintf ( "% 3s: %s\n",
461 $_,
65ea9cad 462 $shortname
c6b73be9 463 );
464 }
465 elsif(! $in_inc_skip++) {
466 $final_out .= " ...\n";
467 }
468}
469
470$final_out .= "\n";
471
472if (my @seen_known_paths = grep { $known_paths->{$_} } keys %$seen_markers) {
3cd1b172 473
474 $final_out .= join "\n", 'Sourcing markers:', (map
475 {
476 sprintf "%*s: %s",
477 $max_marker_len => $_->{marker},
c6b73be9 478 ($_->{config_key} ? "\$Config{$_->{config_key}}" : "$_->{rel_path}/" )
3cd1b172 479 }
480 sort
55c6fb91 481 {
3cd1b172 482 !!$b->{config_key} cmp !!$a->{config_key}
483 or
484 ( $a->{marker}||'') cmp ($b->{marker}||'')
cebc0cc8 485 }
c6b73be9 486 @{$known_paths}{@seen_known_paths}
3cd1b172 487 ), '', '';
cebc0cc8 488
cebc0cc8 489}
490
3cd1b172 491$final_out .= "=============================\n";
cebc0cc8 492
3cd1b172 493$final_out .= join "\n", (map
494 { sprintf (
843b427a 495 "%*s %*s %*s%s",
3cd1b172 496 $max_marker_len => $interesting_modules->{$_}{source_marker} || '',
497 $max_ver_len => ( defined $interesting_modules->{$_}{version}
498 ? $interesting_modules->{$_}{version}
499 : ''
500 ),
c6b73be9 501 -78 => $_,
502 ($interesting_modules->{$_}{abs_unix_path}
503 ? " [ MD5: @{[ get_md5( $interesting_modules->{$_}{abs_unix_path} ) ]} ]"
504 : "! -f \$INC{'@{[ module_notional_filename($_) ]}'}"
3cd1b172 505 ),
506 ) }
507 sort { lc($a) cmp lc($b) } keys %$interesting_modules
508), '';
509
510$final_out .= "=============================\n$discl\n\n";
4bfba4ee 511
3cd1b172 512diag $final_out;
4bfba4ee 513
02154caf 514# *very* large printouts may not finish flushing before the test exits
515# injecting a <testname> ... ok in the middle of the diag
516# http://www.cpantesters.org/cpan/report/fbdac74c-35ca-11e6-ab41-c893a58a4b8c
517select( undef, undef, undef, 0.2 );
518
55c6fb91 519exit 0;
4bfba4ee 520
4bfba4ee 521
4bfba4ee 522
c6b73be9 523sub say_err { print STDERR "\n", @_, "\n\n" };
cebc0cc8 524
55c6fb91 525# do !!!NOT!!! use Module::Runtime's require_module - it breaks CORE::require
526sub try_module_require {
527 # trap deprecation warnings and whatnot
528 local $SIG{__WARN__} = sub {};
529 local $@;
530 eval "require $_[0]";
531}
cebc0cc8 532
c6b73be9 533sub abs_unix_path {
534 return '' unless (
535 defined $_[0]
536 and
537 ( -e $_[0] or File::Spec->file_name_is_absolute($_[0]) )
538 );
55c6fb91 539
3cd1b172 540 # File::Spec's rel2abs does not resolve symlinks
541 # we *need* to look at the filesystem to be sure
2c038b0a 542 #
543 # But looking at the FS for non-existing basenames *may*
544 # throw on some OSes so be extra paranoid:
545 # http://www.cpantesters.org/cpan/report/26a6e42f-6c23-1014-b7dd-5cd275d8a230
546 #
547 my $abs_fn = eval { abs_path($_[0]) } || '';
55c6fb91 548
2c038b0a 549 if ( $abs_fn and $^O eq 'MSWin32' ) {
55c6fb91 550
551 # sometimes we can get a short/longname mix, normalize everything to longnames
2c038b0a 552 $abs_fn = Win32::GetLongPathName($abs_fn)
553 if -e $abs_fn;
55c6fb91 554
555 # Fixup (native) slashes in Config not matching (unixy) slashes in INC
c6b73be9 556 $abs_fn =~ s|\\|/|g;
55c6fb91 557 }
cebc0cc8 558
c6b73be9 559 $abs_fn;
cebc0cc8 560}
561
55c6fb91 562sub shorten_fn {
563 my $fn = shift;
564
c6b73be9 565 my $abs_fn = abs_unix_path($fn);
55c6fb91 566
2c038b0a 567 if ($abs_fn and my $p = subpath_of_known_path( $fn ) ) {
c6b73be9 568 $abs_fn =~ s| (?<! / ) $|/|x
569 if -d $abs_fn;
570
571 if ($p->{rel_path}) {
572 $abs_fn =~ s!\Q$p->{abs_unix_path}!$p->{rel_path}!
573 and return $abs_fn;
574 }
575 elsif ($p->{config_key}) {
576 $abs_fn =~ s!\Q$p->{abs_unix_path}!<<$p->{marker}>>!
577 and
578 $seen_markers->{$p->{marker}} = 1
579 and
580 return $abs_fn;
581 }
55c6fb91 582 }
583
c6b73be9 584 # we got so far - not a known path
585 # return the unixified version it if was absolute, leave as-is otherwise
65ea9cad 586 my $rv = ( $abs_fn and File::Spec->file_name_is_absolute( $fn ) )
c6b73be9 587 ? $abs_fn
588 : $fn
589 ;
65ea9cad 590
591 $rv = "( ! -e ) $rv" unless -e $rv;
592
593 return $rv;
55c6fb91 594}
595
c6b73be9 596sub subpath_of_known_path {
597 my $abs_fn = abs_unix_path( $_[0] )
55c6fb91 598 or return '';
599
c6b73be9 600 for my $p (
601 sort {
602 length( $b->{abs_unix_path} ) <=> length( $a->{abs_unix_path} )
603 or
604 ( $a->{match_order} || 0 ) <=> ( $b->{match_order} || 0 )
605 }
606 values %$known_paths
55c6fb91 607 ) {
3cd1b172 608 # run through the matcher twice - first always append a /
609 # then try without
610 # important to avoid false positives
611 for my $suff ( '/', '' ) {
c6b73be9 612 return { %$p } if 0 == index( $abs_fn, "$p->{abs_unix_path}$suff" );
3cd1b172 613 }
55c6fb91 614 }
615}
616
3cd1b172 617sub module_found_at_inc_index {
c6b73be9 618 my ($mod, $inc_dirs) = @_;
619
620 return undef unless @$inc_dirs;
3cd1b172 621
622 my $fn = module_notional_filename($mod);
623
7dc14bc0 624 # trust INC if it specifies an existing path
625 if( -f ( my $existing_path = abs_unix_path( $INC{$fn} ) ) ) {
626 for my $i ( 0 .. $#$inc_dirs ) {
627
628 # searching from here on out won't mean anything
629 # FIXME - there is actually a way to interrogate this safely, but
630 # that's a fight for another day
631 return undef if length ref $inc_dirs->[$i];
632
633 return $i
634 if 0 == index( $existing_path, abs_unix_path( $inc_dirs->[$i] ) . '/' );
635 }
636 }
c6b73be9 637
7dc14bc0 638 for my $i ( 0 .. $#$inc_dirs ) {
3cd1b172 639
640 if (
c6b73be9 641 -d $inc_dirs->[$i]
3cd1b172 642 and
c6b73be9 643 -f "$inc_dirs->[$i]/$fn"
3cd1b172 644 and
c6b73be9 645 -r "$inc_dirs->[$i]/$fn"
3cd1b172 646 ) {
647 return $i;
648 }
649 }
650
651 return undef;
652}
653
654sub purge_identically_versioned_submodules_with_markers {
655 my $markers = shift;
656
657 return unless @$markers;
658
659 for my $mod ( sort { length($b) <=> length($a) } keys %$interesting_modules ) {
660
661 next unless defined $interesting_modules->{$mod}{version};
662
663 my $marker = $interesting_modules->{$mod}{source_marker}
664 or next;
665
666 next unless grep { $marker eq $_ } @$markers;
667
668 my $parent = $mod;
669
670 while ( $parent =~ s/ :: (?: . (?! :: ) )+ $ //x ) {
671 $interesting_modules->{$parent}
672 and
673 ($interesting_modules->{$parent}{version}||'') eq $interesting_modules->{$mod}{version}
674 and
675 ($interesting_modules->{$parent}{source_marker}||'') eq $interesting_modules->{$mod}{source_marker}
676 and
677 delete $interesting_modules->{$mod}
678 and
679 last
680 }
681 }
682}
683
684sub module_notional_filename {
685 (my $fn = $_[0] . '.pm') =~ s|::|/|g;
686 $fn;
687}
688
55c6fb91 689sub get_md5 {
690 # we already checked for -r/-f, just bail if can't open
691 open my $fh, '<:raw', $_[0] or return '';
55c6fb91 692 Digest::MD5->new->addfile($fh)->hexdigest;
693}