1# Copyright (C) 2001-2015, Parrot Foundation. 2 3=head1 NAME 4 5Parrot::Configure::Compiler - C-Related methods for configuration and more 6 7=head1 DESCRIPTION 8 9The Parrot::Configure::Compiler module provides methods inherited by 10Parrot::Configure which prepare and/or run C programs during configure probes. 11Other methods from this module will be used to generate makefiles and other 12files. Template entries of the form C<@key@> will be replaced with C<key>'s 13value from the currently created configuration system's data. 14 15Beware that Parrot::Config is not available at configure time. 16 17=head2 Methods 18 19=over 4 20 21=cut 22 23package Parrot::Configure::Compiler; 24 25use strict; 26use warnings; 27 28use Carp; 29use File::Spec (); 30use lib ('lib'); 31use Parrot::Configure::Utils qw( 32 prompt copy_if_diff move_if_diff integrate 33 capture_output check_progs _slurp 34 _run_command _build_compile_command 35 move_if_diff 36); 37use Parrot::BuildUtil qw( add_to_generated ); 38 39# report the makefile and lineno 40sub makecroak { 41 my ($conf, $error) = @_; 42 my ($file, $line) = ($conf->{_compiler_file}, $conf->{_compiler_line}); 43 die "$error at $file line $line\n"; 44} 45 46our %file_types_info = ( 47 makefile => { 48 comment_type => '#', 49 }, 50 c => { 51 comment_type => '/*', 52 }, 53 pmc => { 54 comment_type => '/*', 55 }, 56 perl => { 57 comment_type => '#', 58 }, 59 pir => { 60 comment_type => '#', 61 }, 62); 63 64=item C<cc_gen()> 65 66 $conf->cc_gen($source) 67 68Generates F<test_$$.c> from the specified source file. 69 70=cut 71 72sub cc_gen { 73 my $conf = shift; 74 my $source = shift; 75 76 $conf->genfile( $source, "test_$$.c", file_type => 'c' ); 77} 78 79=item C<cc_build()> 80 81 $conf->cc_build($cc_args, $link_args) 82 83These items are used from current config settings: 84 85 $cc, $ccflags, $ldout, $o, $link, $linkflags, $cc_exe_out, $exe, $libs 86 87Calls the compiler and linker on F<test_$$.c>. 88 89Returns the last error code. 90 91=cut 92 93sub cc_build { 94 my $conf = shift; 95 my ( $cc_args, $link_args ) = @_; 96 97 $cc_args = '' unless defined $cc_args; 98 $link_args = '' unless defined $link_args; 99 100 my $verbose = $conf->options->get('verbose'); 101 102 my ( $cc, $ccflags, $ldout, $o, $link, $linkflags, $cc_exe_out, $exe, $libs ) = 103 $conf->data->get(qw(cc ccflags ld_out o link linkflags cc_exe_out exe libs)); 104 105 # unique test file name for parallel builds 106 my $test = 'test_' . $$; 107 my $compile_command = _build_compile_command( $cc, $ccflags, $cc_args ); 108 my $compile_result = _run_command( $compile_command, "$test.cco", "$test.cco", $verbose ); 109 110 if ($compile_result) { 111 confess "C compiler failed (see $test.cco)"; 112 return $compile_result; 113 } 114 115 my $link_result = 116 _run_command( "$link $linkflags $test$o $link_args ${cc_exe_out}${test}${exe} $libs", 117 "$test.ldo", "$test.ldo", $verbose ) 118 and confess "Linker failed (see $test.ldo)"; 119 if ($link_result) { 120 return $link_result; 121 } 122 return; 123} 124 125=item C<cc_run()> 126 127 $conf->cc_run(); 128 129Calls the F<test> (or F<test.exe>) executable. Any output is directed to 130F<test.out>. 131 132Returns the captured stdout, and in array context with the additional exit code. 133 134=cut 135 136sub cc_run { 137 my $conf = shift; 138 my $exe = $conf->data->get('exe'); 139 my $slash = $conf->data->get('slash'); 140 my $verbose = $conf->options->get('verbose'); 141 my $test = 'test_' . $$; 142 my $test_exe = ".${slash}${test}${exe}"; 143 144 my $run_error; 145 if ( defined( $_[0] ) && length( $_[0] ) ) { 146 local $" = ' '; 147 $run_error = _run_command( "$test_exe @_", "./$test.out", undef, $verbose ); 148 } 149 else { 150 $run_error = _run_command( $test_exe, "./$test.out", undef, $verbose ); 151 } 152 153 my $output = _slurp("./$test.out"); 154 return wantarray ? ($output, $run_error) : $output; 155} 156 157=item C<cc_run_capture()> 158 159 $conf->cc_run_capture(); 160 161Same as C<cc_run()> except that warnings and errors are also directed to 162F<test.out>. 163 164Returns the captured stdout combined with stderr, and in array context 165with the additional exit code. 166 167=cut 168 169sub cc_run_capture { 170 my $conf = shift; 171 my $exe = $conf->data->get('exe'); 172 my $slash = $conf->data->get('slash'); 173 my $verbose = $conf->options->get('verbose'); 174 my $test = 'test_' . $$; 175 my $run_error; 176 177 if ( defined( $_[0] ) && length( $_[0] ) ) { 178 local $" = ' '; 179 $run_error = _run_command( ".${slash}$test${exe} @_", "./$test.out", "./$test.out", $verbose ); 180 } 181 else { 182 $run_error = _run_command( ".${slash}$test${exe}", "./$test.out", "./$test.out", $verbose ); 183 } 184 185 my $output = _slurp("./$test.out"); 186 return wantarray ? ($output, $run_error) : $output; 187} 188 189=item C<cc_clean()> 190 191 $conf->cc_clean(); 192 193Cleans up all files in the root folder that match the glob F<test.*>. 194 195=cut 196 197sub cc_clean { ## no critic Subroutines::RequireFinalReturn 198 my $conf = shift; 199 unlink map "test_${$}$_", qw( .c .cco .ldo .out ), 200 $conf->data->get(qw( o exe )), 201 # MSVC 202 ($^O eq 'MSWin32' ? qw( .exe.manifest .ilk .pdb ) : ()); 203} 204 205=item C<shebang_mod()> 206 207 $conf->shebang_mod($source, $target); 208 209Takes the specified source file, replacing entries like C<@key@> with 210C<key>'s value from the configuration system's data, and writes the results 211to specified target file. The replacement is only done in the first line of 212the file normally to set the shebang value accordingly. 213 214=cut 215 216sub shebang_mod { 217 my $conf = shift; 218 my ( $source, $target ) = @_; 219 220 open my $in, '<', $source or die "Can't open $source: $!"; 221 open my $out, '>', "${target}_tmp" or die "Can't open ${target}_tmp: $!"; 222 223 my $line = <$in>; 224 225 # interpolate @foo@ values 226 $line =~ s{ \@ (\w+) \@ }{ 227 if(defined(my $val=$conf->data->get($1))) { 228 $val; 229 } 230 else { 231 warn "value for '\@$1\@' in $source is undef"; 232 ''; 233 } 234 }egx; 235 236 print $out $line; 237 238 while ( my $line = <$in> ) { 239 print $out $line; 240 } 241 242 close($in) or die "Can't close $source: $!"; 243 close($out) or die "Can't close $target: $!"; 244 245 move_if_diff( "${target}_tmp", $target ); 246} 247 248=item C<genfile()> 249 250 $conf->genfile($source, $target, %options); 251 252Takes the specified source file, replacing entries like C<@key@> with 253C<key>'s value from the configuration system's data, and writes the results 254to specified target file. 255 256If a C<::> is present in the C<@key@>, the replaced value will first try to 257use the full key, but if that is not present, the key up to the C<::> is used. 258For example, if C<@cc_warn::src/embed.c@> is used, and that key doesn't 259exist, the fallback key would be C<@cc_warn@>. 260 261Respects the following options when manipulating files (Note: most of the 262replacement syntax assumes the source text is on a single line.) 263 264=over 4 265 266=item file_type 267 268If set to a C<makefile>, C<c> or C<perl> value, C<comment_type> will be set to 269corresponding value. Moreover, when set to a C<makefile> value, it will 270enable C<conditioned_lines>. 271 272Its value will be detected automatically by target file name unless you set 273it to a special value C<none>. 274 275=item conditioned_lines #IF #UNLESS #ELSIF #ELSE 276 277If conditioned_lines is true, then lines beginning in C<#IF>, C<#UNLESS>, 278C<#ELSIF>, and C<#ELSE> are evaluated conditionally, and the content after the 279C<:> is included or excluded, depending on the evaluation of the expression. 280 281Lines beginning with C<#IF(expr):> are skipped if the expr condition is false, 282otherwise the content after the C<:> is inserted. Lines beginning with 283C<#UNLESS(expr):> are skipped if the expr condition is true, otherwise the 284content after the C<:> is inserted. Lines beginning with C<#ELSIF(expr):> or 285C<#ELSE:> are evaluated if the preceding C<#IF(expr):> evaluated to false. 286 287A condition C<expr> may be: 288 289=over 4 290 291=item * 292 293A single key, which is true if a config key is true, 294 295=item * 296 297Equal to the platform name or the osname - case-sensitive, 298 299=item * 300 301A C<key==value> expression, which is true if the config key has the expected 302value, or 303 304=item * 305 306A logical combination of C<|>, C<OR>, C<&>, C<AND>, C<!>, C<NOT>. 307 308=back 309 310A key must only consist of the characters C<A-Z a-z 0-9 _ ->, and is checked 311case-sensitively against the configuration key or the platform name. Truth is 312defined as any value that is not C<0>, an empty string, or C<undef>. 313 314The value in C<key==value> expressions may not contain spaces. Quotes in 315values are not supported. 316 317The word ops C<AND>, C<OR> and C<NOT> are case-insensitive. C<!> and C<NOT> 318bind closer than C<&>, C<AND>, C<|>, and C<OR>. The order of precedence for 319C<AND> and C<OR> is undefined. 320 321 322For instance: 323 324 #IF(win32): src/atomic/gcc_x86$(O) 325 326will be included if the platform is win32. 327 328 #IF(cpuarch==i386): src/atomic/gcc_x86$(O) 329 330will be included if the value of the config key "cpuarch" is "i386". 331 332 #IF(cpuarch==i386): src/atomic/gcc_x86$(O) 333 #ELSIF(cpuarch==sparcv9): src/atomic/sparc_v9.s 334 #ELSE: 335 336will include " src/atomic/gcc_x86$(O)" if the config key "cpuarch" is 337ste to "i386", will include " src/atomic/sparc_v9.s" instead if 338"cpuarch" is set to "sparcv9", and will include an empty line otherwise. 339 340 #IF(win32 and glut and not cygwin): 341 342will be used on "win32" and if "glut" is defined, but not on "cygwin". 343 344=item comment_type 345 346This option takes has two possible values, C<#> or C</*>. If present and 347set to one of these two values, the generated file will contain a 348generated header that is commented out appropriately. 349 350=item ignore_pattern 351 352A regular expression. Any lines in the file matching this expression are 353ignored when determining if the target file has changed (and should therefore 354be overwritten with a new copy). 355 356=item feature_file 357 358When feature_file is set to a true value, a lines beginning with C<#perl> 359forces the remaining lines of the file to be evaluated as perl code. Before 360this evaluation occurs, any substitution of @@ values is performed on the 361original text. 362 363=item expand_gmake_syntax 364 365If set to a true value, then certain types of I<gmake> syntax will be expanded 366into their full equivalents. For example: 367 368 $(wildcard PATTERN) 369 370Will be replaced B<at config time> with the list of files that match this 371pattern. Note! Be very careful when determining whether or not to disable 372this expansion during config time and letting I<gmake> evaluate these: the 373config system itself may change state of the filesystem, causing the 374directives to expand differently depending on when they're run. Another 375potential issue to consider there is that most makefiles, while generated 376from the root directory, are B<run> from a subdirectory. So relative path names 377become an issue. 378 379The I<gmake> replacements are done repeatedly on a single line, so nested 380syntax works ok. 381 382=over 4 383 384=item addprefix 385 386=item basename 387 388=item wildcard 389 390=item notdir 391 392=back 393 394=item replace_slashes 395 396If set to a true value, then filenames on a win32 platform will get their 397forward slashes '/' automatically converted to '\'. 398win32 can handle forward slashes finer (since XP), but cmd.exe tries to 399detect /c and more as argument. 400If you ssh into cygwin for remote testing, or for smoker cronjobs 401calling cmd /c "nmake" or dmake or mingw32-make will fail. 402 403For example: 404 405 POD2MAN = C:\perl516\perl\bin/pod2man 406 CC_INC = -I./include -I./include/pmc 407 DEV_TOOLS_DIR = tools/dev 408 $(PERL) $(DEV_TOOLS_DIR)/headerizer.pl 409=> 410 POD2MAN = C:\perl516\perl\bin\pod2man 411 CC_INC = -I.\include -I.\include\pmc 412 DEV_TOOLS_DIR = tools\dev 413 $(PERL) $(DEV_TOOLS_DIR)\headerizer.pl 414 415=back 416 417=cut 418 419sub genfile { 420 my $conf = shift; 421 my ( $source, $target, %options ) = @_; 422 423 my $calling_sub = (caller(1))[3] || q{}; 424 425 open my $in, '<', $source or die "Can't open $source: $!"; 426 open my $out, '>', "${target}_tmp" or die "Can't open ${target}_tmp: $!"; 427 428 if ( !exists $options{file_type}) { 429 if ( $target =~ m/makefile$/i || $target =~ m/\.mak/) { 430 $options{file_type} = 'makefile'; 431 } 432 elsif ($target =~ m/\.p[lm]$/i ) { 433 $options{file_type} = 'perl'; 434 } 435 elsif ($target =~ m/\.[hc]$/ ) { 436 $options{file_type} = 'c'; 437 } 438 elsif ($target =~ m/\.pmc$/ ) { 439 $options{file_type} = 'pmc'; 440 } 441 elsif ($target =~ m/\.pir$/ ) { 442 $options{file_type} = 'pir'; 443 } 444 } elsif ( $options{file_type} eq 'none' ) { 445 delete $options{file_type}; 446 } 447 448 if ( $options{file_type} ) { 449 unless ( exists $file_types_info{$options{file_type}} ) { 450 die "Unknown file_type '$options{file_type}'"; 451 } 452 unless ( exists $options{comment_type} ) { 453 $options{comment_type} = 454 $file_types_info{$options{file_type}}{comment_type}; 455 } 456 if ( $options{file_type} eq 'makefile' ) { 457 $options{conditioned_lines} = 1; 458 $options{replace_slashes} = 1 if $^O eq 'MSWin32'; 459 } 460 } 461 462 if ( $options{comment_type} ) { 463 my @comment = ( 'ex: set ro: -*- buffer-read-only:t -*-', 464 'DO NOT EDIT THIS FILE', 465 'Generated by ' . __PACKAGE__ . " from $source" ); 466 467 if ( $options{comment_type} eq '#' ) { 468 foreach my $line (@comment) { 469 $line = "# $line\n"; 470 } 471 } 472 elsif ( $options{comment_type} eq '/*' ) { 473 foreach my $line (@comment) { 474 $line = " * $line\n"; 475 } 476 $comment[0] =~ s{^}{/*\n}; # '/*' 477 $comment[-1] =~ s{$}{\n */}; # ' */' 478 } 479 else { 480 die "Unknown comment type '$options{comment_type}'"; 481 } 482 print {$out} @comment, "\n"; # extra newline after header 483 } 484 485 if ($target eq 'CFLAGS') { 486 $options{conditioned_lines} = 1; 487 } 488 489 if ( $options{manifest} ) { 490 add_to_generated( $target, @{$options{manifest}} ); 491 } 492 elsif ($target !~ /^(cc_gen|test_)/) { 493 add_to_generated( $target, "[]" ); 494 } 495 496 # this loop can not be implemented as a foreach loop as the body 497 # is dependent on <IN> being evaluated lazily 498 499 $conf->{_compiler_file} = $source; 500 my $former_truth = -1; 501 LINE: 502 while ( my $line = <$in> ) { 503 $conf->{_compiler_line} = $.; 504 505 # everything after the line starting with #perl is eval'ed 506 if ( $line =~ /^#perl/ && $options{feature_file} ) { 507 508 # OUT was/is used at the output filehandle in eval'ed scripts 509 # e.g. feature.pl or feature_h.in 510 no warnings 'once'; 511 local *OUT = $out; 512 use warnings; 513 my $text = do { local $/; <$in> }; 514 515 # interpolate @foo@ values 516 $text =~ s{ \@ (\w+) \@ }{\$conf->data->get("$1")}gx; 517 eval $text; 518 croak $@ if $@; 519 last LINE; 520 } 521 if ( $options{conditioned_lines} ) { 522 my ($op, $expr, $rest); 523 # allow multiple keys and nested parens here 524 if (($op,$expr,$rest)=($line =~ m/^#(IF|UNLESS|ELSIF)\((.+)\):(.*)/s)) { 525 if (($op eq 'ELSIF') and $former_truth) { 526 next LINE; # no useless check if former IF was true 527 } 528 my $truth = cond_eval($conf, $expr); 529 if ($op eq 'IF') { 530 $former_truth = $truth; 531 next LINE unless $truth; 532 } 533 elsif ($op eq 'UNLESS') { 534 $former_truth = !$truth; 535 next LINE if $truth; 536 } 537 elsif ($op eq 'ELSIF') { 538 $former_truth = $truth; 539 next LINE unless $truth; 540 } 541 $line = $rest; 542 } 543 elsif ( $former_truth != -1 and $line =~ m/^#ELSE:(.*)/s ) { 544 next LINE if $former_truth; 545 $line = $1; 546 } 547 else { # reset 548 $former_truth = -1; # ELSE must immediately follow a conditional. 549 } 550 } 551 552 # interpolate gmake-ish expansions.. 553 if ( $options{expand_gmake_syntax} ) { 554 my $any_gmake; 555 GMAKES: 556 $any_gmake = 0; 557 558 if ( 559 $line =~ s{\$ \( wildcard \s+ ([^)]+) \)}{ 560 join (' ', glob $1) 561 }egx 562 ) 563 { 564 $any_gmake++; 565 } 566 567 if ( 568 $line =~ s{\$ \( notdir \s+ ([^)]+) \)}{ 569 join (' ', 570 map { (File::Spec->splitpath($_))[2] } 571 split(' ', $1) 572 ) 573 }egx 574 ) 575 { 576 $any_gmake++; 577 } 578 579 # documented as removing any .-based suffix 580 if ( 581 $line =~ s{\$ \( basename \s+ ([^)]+) \)}{ 582 join (' ', 583 map { 584 my @split = File::Spec->splitpath($_); 585 $split[2] =~ s/\.[^.]*$//; 586 File::Spec->catpath(@split); 587 } split(' ', $1) 588 ) 589 }egx 590 ) 591 { 592 $any_gmake++; 593 } 594 595 if ( 596 $line =~ s{\$ \( addprefix \s+ ([^,]+) \s* , \s* ([^)]+) \)}{ 597 my ($prefix,$list) = ($1, $2); 598 join (' ', 599 map { $_ = $prefix . $_ } 600 split(' ', $list) 601 ) 602 }egx 603 ) 604 { 605 $any_gmake++; 606 } 607 608 # we might have only gotten the innermost expression. try again. 609 goto GMAKES if $any_gmake; 610 } 611 612 # interpolate @foo@ values 613 $line =~ s{ \@ (\w+) \@ }{ 614 if(defined(my $val=$conf->data->get($1))) { 615 $val; 616 } 617 else { 618 warn "value for '\@$1\@' in $source is undef"; 619 ''; 620 } 621 }egx; 622 623 # interpolate @foo::bar@ values 624 $line =~ s{ \@ (\w+) :: ([^\@]+) \@ }{ 625 my $full = $1 . '::' . $2; 626 my $base = $1; 627 if(defined(my $val=$conf->data->get($full))) { 628 $val; 629 } 630 elsif(defined($val=$conf->data->get($base))) { 631 $val; 632 } 633 else { 634 warn "value for '\@$full\@' in $source is undef, no fallback"; 635 ''; 636 } 637 }egx; 638 639 my $warn_replace_slashes; 640 if ( $options{replace_slashes} and $^O eq 'MSWin32') { 641 # warn "option replace_slashes currently ignored\n" unless $warn_replace_slashes++; 642 $line =~ s{/}{\\}g; 643 } 644 645 print $out $line; 646 } 647 648 close($in) or die "Can't close $source: $!"; 649 close($out) or die "Can't close $target: $!"; 650 651 move_if_diff( "${target}_tmp", $target, $options{ignore_pattern} ); 652} 653 654# Return the next subexpression from the expression in $_[0] 655# and remove it from the input expression. 656# Allowed chars: A-Z a-z 0-9 _ -, so let's take [-\w]. 657# E.g. "(not win32 and has_glut)" 658# => not win32 => has_glut 659# "(!win32&has_glut)|cygwin" - perl-style 660# !win32&has_glut => !win32 => &has_glut => |cygwin 661sub next_expr { 662 my $s = $_[0]; 663 return "" unless $s; 664 # start of a subexpression? 665 if ($s =~ /^\((.+)\)\s*(.*)/) { # longest match to matching closing paren 666 $_[0] = $2 ? $2 : ""; # modify the 2nd arg 667 return $1; 668 } 669 else { 670 $s =~ s/^\s+//; # left-trim to make it more robust 671 if ($s =~ /^([-\w=]+)\s*(.*)?/) { # shortest match to next non-word char 672 # start with word expr 673 $_[0] = $2 ? $2 : ""; # modify the 2nd arg expr in the caller 674 return $1; 675 } 676 else { 677 # special case: start with non-word op (perl-syntax only) 678 $s =~ /^([|&!])\s*(.*)?/; # shortest match to next word char 679 $_[0] = $2 ? $2 : ""; # modify the 2nd arg expr in the caller 680 return $1; 681 } 682 } 683} 684 685# Checks the logical truth of the hash value: exists and not empty. 686# Also check the platform name, the 'osname' key, if the hash key does not exist. 687# Also check for key==value, like #IF(ld==gcc) 688sub cond_eval_single { 689 my $conf = $_[0]; 690 my $key = $_[1]; 691 return unless defined $key; 692 if ($key =~ /^([-\w]+)==(.+)$/) { 693 return ($2 eq $conf->data->get($1)); 694 } 695 else { 696 return exists($conf->data->{c}->{$key}) 697 ? ($conf->data()->get($key) ? 1 : 0) 698 : $key eq $conf->data()->get('osname'); 699 } 700} 701 702# Recursively evaluate boolean expressions with multiple keys and | & ! ops. 703# Order of precedence: Just "!" and "NOT" binds tighter than AND and OR. 704# There's no precedence for AND over OR defined, just left to right. 705sub cond_eval { 706 my $conf = $_[0]; 707 my $expr = $_[1]; 708 my @count = split /[\s!&|\(]+/, $expr; # optimizable with tr 709 if (@count > 1) { # multiple keys: recurse into 710 my $truth = 0; 711 my $prevtruth = 0; 712 my $key = next_expr($expr); 713 my $op = ''; 714 LOOP: 715 while ($key) { 716 if (($key eq '!') or (uc($key) eq 'NOT')) { 717 # bind next key immediately 718 $op = 'NOT'; 719 $key = next_expr($expr); 720 } 721 elsif ($truth and ($op eq 'OR')) { 722 # true OR: => true 723 last LOOP; 724 } 725 $prevtruth = $truth; 726 if (!$truth and ($op eq 'AND')) { # false AND: => false, skip rest 727 last LOOP; 728 } 729 $truth = cond_eval($conf, $key); 730 if ($op eq 'NOT') { # NOT *: invert 731 $truth = $truth ? 0 : 1; 732 } 733 elsif ($op eq 'AND' and !$truth) { # * AND false: => false 734 last LOOP; 735 } 736 # * OR false => * (keep $truth). true OR * already handled before 737 my $prevexpr = $expr; 738 $op = next_expr($expr); 739 if ($op) { 740 if ($op eq '|' or uc($op) eq 'OR') { 741 $op = 'OR'; 742 } 743 elsif ($op eq '&' or uc($op) eq 'AND') { 744 $op = 'AND'; 745 } 746 elsif ($op eq '!' or uc($op) eq 'NOT') { 747 $op = 'NOT'; 748 } 749 else { 750 makecroak($conf, "invalid op \"$op\" in \"$_[1]\" at \"$prevexpr\""); 751 } 752 $key = next_expr($expr); 753 } 754 elsif ($prevexpr) { 755 makecroak($conf, "Makefile conditional syntax error: missing op in \"$_[1]\" at \"$prevexpr\""); 756 } 757 else { 758 last LOOP; # end of expr, nothing left 759 } 760 if ($prevexpr eq $expr) { 761 makecroak($conf, "Makefile conditional parser error in \"$_[1]\" at \"$prevexpr\""); 762 } 763 } 764 return $truth; 765 } 766 cond_eval_single($conf, $expr); 767} 768 769=item C<append_configure_log($path)> 770 771 $conf->append_configure_log($path) 772 773Adds $path to F<MANIFEST_configure.generated>. 774 775Deprecated, there is no F<MANIFEST_configure.generated> anymore. 776Replaced by C<add_to_generated()>. 777 778=back 779 780=cut 781 782sub append_configure_log { 783 my $conf = shift; 784 my $target = shift; 785 if ( $conf->{active_configuration} ) { 786 warn "append_configure_log() is DEPRECATED and ignored. Use add_to_generated() instead"; 787 788 my $generated_log = 'MANIFEST_configure.generated'; 789 open my $GEN, '>>', $generated_log 790 or die "Can't open $generated_log for appending: $!"; 791 print $GEN "$target\n"; 792 close $GEN or die "Can't close $generated_log after appending: $!"; 793 } 794} 795 796=head1 SEE ALSO 797 798=over 4 799 800=item F<docs/configuration.pod> 801 802=back 803 804=cut 805 8061; 807 808# Local Variables: 809# mode: cperl 810# cperl-indent-level: 4 811# fill-column: 100 812# End: 813# vim: expandtab shiftwidth=4: 814