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