1package Config::Perl::V; 2 3use strict; 4use warnings; 5 6use Config; 7use Exporter; 8use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS); 9$VERSION = "0.36"; 10@ISA = qw( Exporter ); 11@EXPORT_OK = qw( plv2hash summary myconfig signature ); 12%EXPORT_TAGS = ( 13 'all' => [ @EXPORT_OK ], 14 'sig' => [ "signature" ], 15 ); 16 17# Characteristics of this binary (from libperl): 18# Compile-time options: DEBUGGING PERL_DONT_CREATE_GVSV PERL_MALLOC_WRAP 19# USE_64_BIT_INT USE_LARGE_FILES USE_PERLIO 20 21# The list are as the perl binary has stored it in PL_bincompat_options 22# search for it in 23# perl.c line 1643 S_Internals_V () 24# perl -ne'(/^S_Internals_V/../^}/)&&s/^\s+"( .*)"/$1/ and print' perl.c 25# perl.h line 4566 PL_bincompat_options 26# perl -ne'(/^\w.*PL_bincompat/../^\w}/)&&s/^\s+"( .*)"/$1/ and print' perl.h 27my %BTD = map {( $_ => 0 )} qw( 28 29 DEBUGGING 30 NO_HASH_SEED 31 NO_MATHOMS 32 NO_PERL_INTERNAL_RAND_SEED 33 NO_PERL_RAND_SEED 34 NO_TAINT_SUPPORT 35 PERL_BOOL_AS_CHAR 36 PERL_COPY_ON_WRITE 37 PERL_DISABLE_PMC 38 PERL_DONT_CREATE_GVSV 39 PERL_EXTERNAL_GLOB 40 PERL_HASH_FUNC_DJB2 41 PERL_HASH_FUNC_MURMUR3 42 PERL_HASH_FUNC_ONE_AT_A_TIME 43 PERL_HASH_FUNC_ONE_AT_A_TIME_HARD 44 PERL_HASH_FUNC_ONE_AT_A_TIME_OLD 45 PERL_HASH_FUNC_SDBM 46 PERL_HASH_FUNC_SIPHASH 47 PERL_HASH_FUNC_SUPERFAST 48 PERL_IS_MINIPERL 49 PERL_MALLOC_WRAP 50 PERL_MEM_LOG 51 PERL_MEM_LOG_ENV 52 PERL_MEM_LOG_ENV_FD 53 PERL_MEM_LOG_NOIMPL 54 PERL_MEM_LOG_STDERR 55 PERL_MEM_LOG_TIMESTAMP 56 PERL_NEW_COPY_ON_WRITE 57 PERL_OP_PARENT 58 PERL_PERTURB_KEYS_DETERMINISTIC 59 PERL_PERTURB_KEYS_DISABLED 60 PERL_PERTURB_KEYS_RANDOM 61 PERL_PRESERVE_IVUV 62 PERL_RC_STACK 63 PERL_RELOCATABLE_INCPUSH 64 PERL_USE_DEVEL 65 PERL_USE_SAFE_PUTENV 66 PERL_USE_UNSHARED_KEYS_IN_LARGE_HASHES 67 SILENT_NO_TAINT_SUPPORT 68 UNLINK_ALL_VERSIONS 69 USE_ATTRIBUTES_FOR_PERLIO 70 USE_FAST_STDIO 71 USE_HASH_SEED_EXPLICIT 72 USE_LOCALE 73 USE_LOCALE_CTYPE 74 USE_NO_REGISTRY 75 USE_PERL_ATOF 76 USE_SITECUSTOMIZE 77 USE_THREAD_SAFE_LOCALE 78 79 DEBUG_LEAKING_SCALARS 80 DEBUG_LEAKING_SCALARS_FORK_DUMP 81 DECCRTL_SOCKETS 82 FAKE_THREADS 83 FCRYPT 84 HAS_TIMES 85 HAVE_INTERP_INTERN 86 MULTIPLICITY 87 MYMALLOC 88 NO_HASH_SEED 89 PERL_DEBUG_READONLY_COW 90 PERL_DEBUG_READONLY_OPS 91 PERL_GLOBAL_STRUCT 92 PERL_GLOBAL_STRUCT_PRIVATE 93 PERL_HASH_NO_SBOX32 94 PERL_HASH_USE_SBOX32 95 PERL_IMPLICIT_CONTEXT 96 PERL_IMPLICIT_SYS 97 PERLIO_LAYERS 98 PERL_MAD 99 PERL_MICRO 100 PERL_NEED_APPCTX 101 PERL_NEED_TIMESBASE 102 PERL_OLD_COPY_ON_WRITE 103 PERL_POISON 104 PERL_SAWAMPERSAND 105 PERL_TRACK_MEMPOOL 106 PERL_USES_PL_PIDSTATUS 107 PL_OP_SLAB_ALLOC 108 THREADS_HAVE_PIDS 109 USE_64_BIT_ALL 110 USE_64_BIT_INT 111 USE_IEEE 112 USE_ITHREADS 113 USE_LARGE_FILES 114 USE_LOCALE_COLLATE 115 USE_LOCALE_NUMERIC 116 USE_LOCALE_TIME 117 USE_LONG_DOUBLE 118 USE_PERLIO 119 USE_QUADMATH 120 USE_REENTRANT_API 121 USE_SFIO 122 USE_SOCKS 123 VMS_DO_SOCKETS 124 VMS_SHORTEN_LONG_SYMBOLS 125 VMS_SYMBOL_CASE_AS_IS 126 ); 127 128# These are all the keys that are 129# 1. Always present in %Config - lib/Config.pm #87 tie %Config 130# 2. Reported by 'perl -V' (the rest) 131my @config_vars = qw( 132 133 api_subversion 134 api_version 135 api_versionstring 136 archlibexp 137 dont_use_nlink 138 d_readlink 139 d_symlink 140 exe_ext 141 inc_version_list 142 ldlibpthname 143 patchlevel 144 path_sep 145 perl_patchlevel 146 privlibexp 147 scriptdir 148 sitearchexp 149 sitelibexp 150 subversion 151 usevendorprefix 152 version 153 154 git_commit_id 155 git_describe 156 git_branch 157 git_uncommitted_changes 158 git_commit_id_title 159 git_snapshot_date 160 161 package revision version_patchlevel_string 162 163 osname osvers archname 164 myuname 165 config_args 166 hint useposix d_sigaction 167 useithreads usemultiplicity 168 useperlio d_sfio uselargefiles usesocks 169 use64bitint use64bitall uselongdouble 170 usemymalloc default_inc_excludes_dot bincompat5005 171 172 cc ccflags 173 optimize 174 cppflags 175 ccversion gccversion gccosandvers 176 intsize longsize ptrsize doublesize byteorder 177 d_longlong longlongsize d_longdbl longdblsize 178 ivtype ivsize nvtype nvsize lseektype lseeksize 179 alignbytes prototype 180 181 ld ldflags 182 libpth 183 libs 184 perllibs 185 libc so useshrplib libperl 186 gnulibc_version 187 188 dlsrc dlext d_dlsymun ccdlflags 189 cccdlflags lddlflags 190 ); 191 192my %empty_build = ( 193 'osname' => "", 194 'stamp' => 0, 195 'options' => { %BTD }, 196 'patches' => [], 197 ); 198 199sub _make_derived { 200 my $conf = shift; 201 202 for ( [ 'lseektype' => "Off_t" ], 203 [ 'myuname' => "uname" ], 204 [ 'perl_patchlevel' => "patch" ], 205 ) { 206 my ($official, $derived) = @{$_}; 207 $conf->{'config'}{$derived} ||= $conf->{'config'}{$official}; 208 $conf->{'config'}{$official} ||= $conf->{'config'}{$derived}; 209 $conf->{'derived'}{$derived} = delete $conf->{'config'}{$derived}; 210 } 211 212 if (exists $conf->{'config'}{'version_patchlevel_string'} && 213 !exists $conf->{'config'}{'api_version'}) { 214 my $vps = $conf->{'config'}{'version_patchlevel_string'}; 215 $vps =~ s{\b revision \s+ (\S+) }{}x and 216 $conf->{'config'}{'revision'} ||= $1; 217 218 $vps =~ s{\b version \s+ (\S+) }{}x and 219 $conf->{'config'}{'api_version'} ||= $1; 220 $vps =~ s{\b subversion \s+ (\S+) }{}x and 221 $conf->{'config'}{'subversion'} ||= $1; 222 $vps =~ s{\b patch \s+ (\S+) }{}x and 223 $conf->{'config'}{'perl_patchlevel'} ||= $1; 224 } 225 226 ($conf->{'config'}{'version_patchlevel_string'} ||= join " ", 227 map { ($_, $conf->{'config'}{$_} ) } 228 grep { $conf->{'config'}{$_} } 229 qw( api_version subversion perl_patchlevel )) =~ s/\bperl_//; 230 231 $conf->{'config'}{'perl_patchlevel'} ||= ""; # 0 is not a valid patchlevel 232 233 if ($conf->{'config'}{'perl_patchlevel'} =~ m{^git\w*-([^-]+)}i) { 234 $conf->{'config'}{'git_branch'} ||= $1; 235 $conf->{'config'}{'git_describe'} ||= $conf->{'config'}{'perl_patchlevel'}; 236 } 237 238 $conf->{'config'}{$_} ||= "undef" for grep m{^(?:use|def)} => @config_vars; 239 240 $conf; 241 } # _make_derived 242 243sub plv2hash { 244 my %config; 245 246 my $pv = join "\n" => @_; 247 248 if ($pv =~ m{^Summary of my\s+(\S+)\s+\(\s*(.*?)\s*\)}m) { 249 $config{'package'} = $1; 250 my $rev = $2; 251 $rev =~ s/^ revision \s+ (\S+) \s*//x and $config{'revision'} = $1; 252 $rev and $config{'version_patchlevel_string'} = $rev; 253 my ($rel) = $config{'package'} =~ m{perl(\d)}; 254 my ($vers, $subvers) = $rev =~ m{version\s+(\d+)\s+subversion\s+(\d+)}; 255 defined $vers && defined $subvers && defined $rel and 256 $config{'version'} = "$rel.$vers.$subvers"; 257 } 258 259 if ($pv =~ m{^\s+(Snapshot of:)\s+(\S+)}) { 260 $config{'git_commit_id_title'} = $1; 261 $config{'git_commit_id'} = $2; 262 } 263 264 # these are always last on line and can have multiple quotation styles 265 for my $k (qw( ccflags ldflags lddlflags )) { 266 $pv =~ s{, \s* $k \s*=\s* (.*) \s*$}{}mx or next; 267 my $v = $1; 268 $v =~ s/\s*,\s*$//; 269 $v =~ s/^(['"])(.*)\1$/$2/; 270 $config{$k} = $v; 271 } 272 273 my %kv; 274 if ($pv =~ m{\S,? (?:osvers|archname)=}) { # attr is not the first on the line 275 # up to and including 5.24, a line could have multiple kv pairs 276 %kv = ($pv =~ m{\b 277 (\w+) # key 278 \s*= # assign 279 ( '\s*[^']*?\s*' # quoted value 280 | \S+[^=]*?\s*\n # unquoted running till end of line 281 | \S+ # unquoted value 282 | \s*\n # empty 283 ) 284 (?:,?\s+|\s*\n)? # optional separator (5.8.x reports did 285 }gx); # not have a ',' between every kv pair) 286 } 287 else { 288 # as of 5.25, each kv pair is listed on its own line 289 %kv = ($pv =~ m{^ 290 \s+ 291 (\w+) # key 292 \s*=\s* # assign 293 (.*?) # value 294 \s*,?\s*$ 295 }gmx); 296 } 297 298 while (my ($k, $v) = each %kv) { 299 $k =~ s{\s+$} {}; 300 $v =~ s{\s*\n\z} {}; 301 $v =~ s{,$} {}; 302 $v =~ m{^'(.*)'$} and $v = $1; 303 $v =~ s{\s+$} {}; 304 $config{$k} = $v; 305 } 306 307 my $build = { %empty_build }; 308 309 $pv =~ m{^\s+Compiled at\s+(.*)}m 310 and $build->{'stamp'} = $1; 311 $pv =~ m{^\s+Locally applied patches:(?:\s+|\n)(.*?)(?:[\s\n]+Buil[td] under)}ms 312 and $build->{'patches'} = [ split m{\n+\s*}, $1 ]; 313 $pv =~ m{^\s+Compile-time options:(?:\s+|\n)(.*?)(?:[\s\n]+(?:Locally applied|Buil[td] under))}ms 314 and map { $build->{'options'}{$_} = 1 } split m{\s+|\n} => $1; 315 316 $build->{'osname'} = $config{'osname'}; 317 $pv =~ m{^\s+Built under\s+(.*)}m 318 and $build->{'osname'} = $1; 319 $config{'osname'} ||= $build->{'osname'}; 320 321 return _make_derived ({ 322 'build' => $build, 323 'environment' => {}, 324 'config' => \%config, 325 'derived' => {}, 326 'inc' => [], 327 }); 328 } # plv2hash 329 330sub summary { 331 my $conf = shift || myconfig (); 332 ref $conf eq "HASH" 333 && exists $conf->{'config'} 334 && exists $conf->{'build'} 335 && ref $conf->{'config'} eq "HASH" 336 && ref $conf->{'build'} eq "HASH" or return; 337 338 my %info = map { 339 exists $conf->{'config'}{$_} ? ( $_ => $conf->{'config'}{$_} ) : () } 340 qw( archname osname osvers revision patchlevel subversion version 341 cc ccversion gccversion config_args inc_version_list 342 d_longdbl d_longlong use64bitall use64bitint useithreads 343 uselongdouble usemultiplicity usemymalloc useperlio useshrplib 344 doublesize intsize ivsize nvsize longdblsize longlongsize lseeksize 345 default_inc_excludes_dot 346 ); 347 $info{$_}++ for grep { $conf->{'build'}{'options'}{$_} } keys %{$conf->{'build'}{'options'}}; 348 349 return \%info; 350 } # summary 351 352sub signature { 353 my $no_md5 = "0" x 32; 354 my $conf = summary (shift) or return $no_md5; 355 356 eval { require Digest::MD5 }; 357 $@ and return $no_md5; 358 359 $conf->{'cc'} =~ s{.*\bccache\s+}{}; 360 $conf->{'cc'} =~ s{.*[/\\]}{}; 361 362 delete $conf->{'config_args'}; 363 return Digest::MD5::md5_hex (join "\xFF" => map { 364 "$_=".(defined $conf->{$_} ? $conf->{$_} : "\xFE"); 365 } sort keys %{$conf}); 366 } # signature 367 368sub myconfig { 369 my $args = shift; 370 my %args = ref $args eq "HASH" ? %{$args} : 371 ref $args eq "ARRAY" ? @{$args} : (); 372 373 my $build = { %empty_build }; 374 375 # 5.14.0 and later provide all the information without shelling out 376 my $stamp = eval { Config::compile_date () }; 377 if (defined $stamp) { 378 $stamp =~ s/^Compiled at //; 379 $build->{'osname'} = $^O; 380 $build->{'stamp'} = $stamp; 381 $build->{'patches'} = [ Config::local_patches () ]; 382 $build->{'options'}{$_} = 1 for Config::bincompat_options (), 383 Config::non_bincompat_options (); 384 } 385 else { 386 #y $pv = qx[$^X -e"sub Config::myconfig{};" -V]; 387 my $cnf = plv2hash (qx[$^X -V]); 388 389 $build->{$_} = $cnf->{'build'}{$_} for qw( osname stamp patches options ); 390 } 391 392 my @KEYS = keys %ENV; 393 my %env = 394 map {( $_ => $ENV{$_} )} grep m{^PERL} => @KEYS; 395 if ($args{'env'}) { 396 $env{$_} = $ENV{$_} for grep m{$args{'env'}} => @KEYS; 397 } 398 399 my %config = map { $_ => $Config{$_} } @config_vars; 400 401 return _make_derived ({ 402 'build' => $build, 403 'environment' => \%env, 404 'config' => \%config, 405 'derived' => {}, 406 'inc' => \@INC, 407 }); 408 } # myconfig 409 4101; 411 412__END__ 413 414=head1 NAME 415 416Config::Perl::V - Structured data retrieval of perl -V output 417 418=head1 SYNOPSIS 419 420 use Config::Perl::V; 421 422 my $local_config = Config::Perl::V::myconfig (); 423 print $local_config->{config}{osname}; 424 425=head1 DESCRIPTION 426 427=head2 $conf = myconfig () 428 429This function will collect the data described in L</"The hash structure"> below, 430and return that as a hash reference. It optionally accepts an option to 431include more entries from %ENV. See L</environment> below. 432 433Note that this will not work on uninstalled perls when called with 434C<-I/path/to/uninstalled/perl/lib>, but it works when that path is in 435C<$PERL5LIB> or in C<$PERL5OPT>, as paths passed using C<-I> are not 436known when the C<-V> information is collected. 437 438=head2 $conf = plv2hash ($text [, ...]) 439 440Convert a sole 'perl -V' text block, or list of lines, to a complete 441myconfig hash. All unknown entries are defaulted. 442 443=head2 $info = summary ([$conf]) 444 445Return an arbitrary selection of the information. If no C<$conf> is 446given, C<myconfig ()> is used instead. 447 448=head2 $md5 = signature ([$conf]) 449 450Return the MD5 of the info returned by C<summary ()> without the 451C<config_args> entry. 452 453If C<Digest::MD5> is not available, it return a string with only C<0>'s. 454 455=head2 The hash structure 456 457The returned hash consists of 4 parts: 458 459=over 4 460 461=item build 462 463This information is extracted from the second block that is emitted by 464C<perl -V>, and usually looks something like 465 466 Characteristics of this binary (from libperl): 467 Compile-time options: DEBUGGING USE_64_BIT_INT USE_LARGE_FILES 468 Locally applied patches: 469 defined-or 470 MAINT24637 471 Built under linux 472 Compiled at Jun 13 2005 10:44:20 473 @INC: 474 /usr/lib/perl5/5.8.7/i686-linux-64int 475 /usr/lib/perl5/5.8.7 476 /usr/lib/perl5/site_perl/5.8.7/i686-linux-64int 477 /usr/lib/perl5/site_perl/5.8.7 478 /usr/lib/perl5/site_perl 479 . 480 481or 482 483 Characteristics of this binary (from libperl): 484 Compile-time options: DEBUGGING MULTIPLICITY 485 PERL_DONT_CREATE_GVSV PERL_IMPLICIT_CONTEXT 486 PERL_MALLOC_WRAP PERL_TRACK_MEMPOOL 487 PERL_USE_SAFE_PUTENV USE_ITHREADS 488 USE_LARGE_FILES USE_PERLIO 489 USE_REENTRANT_API 490 Built under linux 491 Compiled at Jan 28 2009 15:26:59 492 493This information is not available anywhere else, including C<%Config>, 494but it is the information that is only known to the perl binary. 495 496The extracted information is stored in 5 entries in the C<build> hash: 497 498=over 4 499 500=item osname 501 502This is most likely the same as C<$Config{osname}>, and was the name 503known when perl was built. It might be different if perl was cross-compiled. 504 505The default for this field, if it cannot be extracted, is to copy 506C<$Config{osname}>. The two may be differing in casing (OpenBSD vs openbsd). 507 508=item stamp 509 510This is the time string for which the perl binary was compiled. The default 511value is 0. 512 513=item options 514 515This is a hash with all the known defines as keys. The value is either 0, 516which means unknown or unset, or 1, which means defined. 517 518=item derived 519 520As some variables are reported by a different name in the output of C<perl -V> 521than their actual name in C<%Config>, I decided to leave the C<config> entry 522as close to reality as possible, and put in the entries that might have been 523guessed by the printed output in a separate block. 524 525=item patches 526 527This is a list of optionally locally applied patches. Default is an empty list. 528 529=back 530 531=item environment 532 533By default this hash is only filled with the environment variables 534out of %ENV that start with C<PERL>, but you can pass the C<env> option 535to myconfig to get more 536 537 my $conf = Config::Perl::V::myconfig ({ env => qr/^ORACLE/ }); 538 my $conf = Config::Perl::V::myconfig ([ env => qr/^ORACLE/ ]); 539 540=item config 541 542This hash is filled with the variables that C<perl -V> fills its report 543with, and it has the same variables that C<Config::myconfig> returns 544from C<%Config>. 545 546=item inc 547 548This is the list of default @INC. 549 550=back 551 552=head1 REASONING 553 554This module was written to be able to return the configuration for the 555currently used perl as deeply as needed for the CPANTESTERS framework. 556Up until now they used the output of myconfig as a single text blob, 557and so it was missing the vital binary characteristics of the running 558perl and the optional applied patches. 559 560=head1 BUGS 561 562Please feedback what is wrong 563 564=head1 TODO 565 566 * Implement retrieval functions/methods 567 * Documentation 568 * Error checking 569 * Tests 570 571=head1 AUTHOR 572 573H.Merijn Brand <h.m.brand@xs4all.nl> 574 575=head1 COPYRIGHT AND LICENSE 576 577Copyright (C) 2009-2023 H.Merijn Brand 578 579This library is free software; you can redistribute it and/or modify 580it under the same terms as Perl itself. 581 582=cut 583